mirror of
https://github.com/penpot/penpot.git
synced 2025-05-30 02:06:10 +02:00
Refactor shape resize and displacement.
This commit is contained in:
parent
23dbc77da9
commit
af57d8b449
14 changed files with 577 additions and 252 deletions
|
@ -8,17 +8,18 @@
|
||||||
(:require [beicon.core :as rx]
|
(:require [beicon.core :as rx]
|
||||||
[uxbox.util.uuid :as uuid]
|
[uxbox.util.uuid :as uuid]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.store :as st]
|
||||||
[uxbox.util.forms :as sc]
|
[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.util.workers :as uw]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.geom :as geom]
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.store :as st]
|
|
||||||
[uxbox.main.data.core :refer (worker)]
|
[uxbox.main.data.core :refer (worker)]
|
||||||
[uxbox.main.data.shapes-impl :as impl]
|
[uxbox.main.data.shapes-impl :as impl]
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]))
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.geom.point :as gpt]))
|
|
||||||
|
|
||||||
;; --- Shapes CRUD
|
;; --- Shapes CRUD
|
||||||
|
|
||||||
|
@ -70,18 +71,21 @@
|
||||||
(gpt/point c/canvas-start-x
|
(gpt/point c/canvas-start-x
|
||||||
c/canvas-start-y))
|
c/canvas-start-y))
|
||||||
|
|
||||||
|
(declare apply-temporal-displacement)
|
||||||
|
|
||||||
(defn initial-align-shape
|
(defn initial-align-shape
|
||||||
[id]
|
[id]
|
||||||
(reify
|
(reify
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
(let [shape (get-in state [:shapes id])
|
(let [{:keys [x1 y1] :as shape} (->> (get-in state [:shapes id])
|
||||||
shape (geom/outer-rect state shape)
|
(geom/shape->rect-shape state))
|
||||||
point (gpt/point (:x shape) (:y shape))
|
point1 (gpt/point x1 y1)
|
||||||
point (gpt/add point canvas-coords)]
|
point2 (gpt/add point1 canvas-coords)]
|
||||||
(->> (align-point point)
|
(->> (align-point point2)
|
||||||
(rx/map #(gpt/subtract % point))
|
(rx/map #(gpt/subtract % canvas-coords))
|
||||||
(rx/map #(move-shape id %)))))))
|
(rx/map (fn [{:keys [x y] :as pt}]
|
||||||
|
(apply-temporal-displacement id (gpt/subtract pt point1)))))))))
|
||||||
|
|
||||||
(defn update-line-attrs
|
(defn update-line-attrs
|
||||||
[sid {:keys [x1 y1 x2 y2] :as opts}]
|
[sid {:keys [x1 y1 x2 y2] :as opts}]
|
||||||
|
@ -119,6 +123,92 @@
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:shapes sid] geom/resize-dim opts))))
|
(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
|
(defn update-vertex-position
|
||||||
[id {:keys [vid delta]}]
|
[id {:keys [vid delta]}]
|
||||||
(reify
|
(reify
|
||||||
|
@ -208,7 +298,6 @@
|
||||||
(when rx {:rx rx})
|
(when rx {:rx rx})
|
||||||
(when ry {:ry ry})))))
|
(when ry {:ry ry})))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Shape Proportions
|
;; --- Shape Proportions
|
||||||
|
|
||||||
(defn lock-proportions
|
(defn lock-proportions
|
||||||
|
@ -378,17 +467,20 @@
|
||||||
id (first (get-in state [:pages page :shapes]))]
|
id (first (get-in state [:pages page :shapes]))]
|
||||||
(assoc-in state [:workspace :selected] #{id})))))
|
(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
|
(defn select-shape
|
||||||
"Mark a shape selected for drawing in the canvas."
|
"Mark a shape selected for drawing in the canvas."
|
||||||
[id]
|
[id]
|
||||||
(reify
|
(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)))))
|
|
||||||
|
|
||||||
;; --- Select Shapes
|
;; --- Select Shapes
|
||||||
|
|
||||||
|
|
|
@ -555,6 +555,7 @@
|
||||||
(case type
|
(case type
|
||||||
:rect (transform-rect shape xfmt)
|
:rect (transform-rect shape xfmt)
|
||||||
:icon (transform-rect shape xfmt)
|
:icon (transform-rect shape xfmt)
|
||||||
|
:text (transform-rect shape xfmt)
|
||||||
:image (transform-rect shape xfmt)
|
:image (transform-rect shape xfmt)
|
||||||
:path (transform-path shape xfmt)
|
:path (transform-path shape xfmt)
|
||||||
:circle (transform-circle shape xfmt))))
|
:circle (transform-circle shape xfmt))))
|
||||||
|
|
|
@ -5,50 +5,39 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.shapes.circle
|
(ns uxbox.main.ui.shapes.circle
|
||||||
(:require [sablono.core :refer-macros [html]]
|
(:require [lentes.core :as l]
|
||||||
[rum.core :as rum]
|
|
||||||
[lentes.core :as l]
|
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[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
|
;; --- Circle Component
|
||||||
|
|
||||||
(declare circle-shape)
|
(declare circle-shape)
|
||||||
|
|
||||||
(defn- circle-component-render
|
(mx/defc circle-component
|
||||||
[own shape]
|
{:mixins [mx/reactive mx/static]}
|
||||||
|
[shape]
|
||||||
(let [{:keys [id x y width height group]} shape
|
(let [{:keys [id x y width height group]} shape
|
||||||
selected (mx/react common/selected-ref)
|
selected (mx/react common/selected-ref)
|
||||||
selected? (contains? selected id)
|
selected? (contains? selected id)
|
||||||
on-mouse-down #(common/on-mouse-down % shape selected)]
|
on-mouse-down #(common/on-mouse-down % shape selected)]
|
||||||
(html
|
[:g.shape {:class (when selected? "selected")
|
||||||
[:g.shape {:class (when selected? "selected")
|
:on-mouse-down on-mouse-down}
|
||||||
:on-mouse-down on-mouse-down}
|
(circle-shape shape identity)]))
|
||||||
(circle-shape shape identity)])))
|
|
||||||
|
|
||||||
(def circle-component
|
|
||||||
(mx/component
|
|
||||||
{:render circle-component-render
|
|
||||||
:name "circle-component"
|
|
||||||
:mixins [mx/static mx/reactive]}))
|
|
||||||
|
|
||||||
;; --- Circle Shape
|
;; --- Circle Shape
|
||||||
|
|
||||||
(defn- circle-shape-render
|
(mx/defc circle-shape
|
||||||
[own {:keys [id] :as shape}]
|
{:mixins [mx/static]}
|
||||||
(let [key (str "shape-" id)
|
[{:keys [id tmp-resize-xform tmp-displacement] :as shape}]
|
||||||
rfm (geom/transformation-matrix shape)
|
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
|
||||||
props (select-keys shape [:cx :cy :rx :ry])
|
tmp-displacement (gmt/translate tmp-displacement))
|
||||||
attrs (-> (attrs/extract-style-attrs shape)
|
|
||||||
(merge {:id key :key key :transform (str rfm)})
|
|
||||||
(merge props))]
|
|
||||||
(html
|
|
||||||
[:ellipse attrs])))
|
|
||||||
|
|
||||||
(def circle-shape
|
props {:transform (str xfmt) :id (str id)}
|
||||||
(mx/component
|
attrs (merge props
|
||||||
{:render circle-shape-render
|
(attrs/extract-style-attrs shape)
|
||||||
:name "circle-shape"
|
(select-keys shape [:cx :cy :rx :ry]))]
|
||||||
:mixins [mx/static]}))
|
[:ellipse attrs]))
|
||||||
|
|
|
@ -10,11 +10,12 @@
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.store :as st]
|
[uxbox.store :as st]
|
||||||
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.main.data.shapes :as uds]
|
[uxbox.main.data.shapes :as uds]
|
||||||
[uxbox.main.ui.keyboard :as kbd]
|
[uxbox.main.ui.keyboard :as kbd]
|
||||||
[uxbox.main.ui.workspace.base :as wb]
|
[uxbox.main.ui.workspace.base :as wb]
|
||||||
|
[uxbox.util.geom.point :as gpt]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
[uxbox.util.rlocks :as rlocks]
|
||||||
[uxbox.main.geom :as geom]
|
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
@ -36,13 +37,19 @@
|
||||||
|
|
||||||
(defn start-move
|
(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)
|
(let [stoper (->> (rx/map first wb/events-s)
|
||||||
(rx/filter #(= % :mouse/up))
|
(rx/filter #(= % :mouse/up))
|
||||||
(rx/take 1))
|
(rx/take 1))
|
||||||
stream (rx/take-until stoper wb/mouse-delta-s)
|
stream (->> wb/mouse-delta-s
|
||||||
on-move #(st/emit! (uds/move-shape shape %))
|
(rx/take-until stoper))
|
||||||
on-stop #(rlocks/release! :shape/move)]
|
on-move (partial on-move shape)
|
||||||
|
on-stop (partial on-stop shape)]
|
||||||
(when @wb/alignment-ref
|
(when @wb/alignment-ref
|
||||||
(st/emit! (uds/initial-align-shape shape)))
|
(st/emit! (uds/initial-align-shape shape)))
|
||||||
(rx/subscribe stream on-move nil on-stop)))]
|
(rx/subscribe stream on-move nil on-stop)))]
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
(ns uxbox.main.ui.shapes.group
|
(ns uxbox.main.ui.shapes.group
|
||||||
(:require [lentes.core :as l]
|
(:require [lentes.core :as l]
|
||||||
[uxbox.store :as st]
|
[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.common :as common]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||||
[uxbox.main.ui.shapes.icon :as icon]
|
[uxbox.main.ui.shapes.icon :as icon]
|
||||||
|
@ -16,7 +16,9 @@
|
||||||
[uxbox.main.ui.shapes.text :as text]
|
[uxbox.main.ui.shapes.text :as text]
|
||||||
[uxbox.main.ui.shapes.path :as path]
|
[uxbox.main.ui.shapes.path :as path]
|
||||||
[uxbox.main.ui.shapes.image :as image]
|
[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
|
;; --- Helpers
|
||||||
|
|
||||||
|
@ -64,12 +66,14 @@
|
||||||
|
|
||||||
(mx/defc group-shape
|
(mx/defc group-shape
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
[{:keys [items id dx dy rotation] :as shape} factory]
|
[{:keys [id items tmp-resize-xform tmp-displacement] :as shape} factory]
|
||||||
(let [key (str "shape-" id)
|
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
|
||||||
rfm (geom/transformation-matrix shape)
|
tmp-displacement (gmt/translate tmp-displacement))
|
||||||
attrs (merge {:id key :key key :transform (str rfm)}
|
|
||||||
(attrs/extract-style-attrs shape)
|
props {:id (str id)
|
||||||
(attrs/make-debug-attrs shape))]
|
:transform (str xfmt)}
|
||||||
|
|
||||||
|
attrs (merge props (attrs/extract-style-attrs shape))]
|
||||||
[:g attrs
|
[:g attrs
|
||||||
(for [item (reverse items)
|
(for [item (reverse items)
|
||||||
:let [key (str item)]]
|
:let [key (str item)]]
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.shapes.icon
|
(ns uxbox.main.ui.shapes.icon
|
||||||
(:require [uxbox.util.mixins :as mx :include-macros true]
|
(:require [uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[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
|
;; --- Icon Component
|
||||||
|
|
||||||
|
@ -28,19 +29,30 @@
|
||||||
|
|
||||||
(mx/defc icon-shape
|
(mx/defc icon-shape
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]}
|
||||||
[{:keys [x1 y1 content id metadata] :as shape} factory]
|
[shape]
|
||||||
(let [key (str "shape-" id)
|
(let [{:keys [x1 y1 content id metadata
|
||||||
;; rfm (geom/transformation-matrix shape)
|
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)))
|
view-box (apply str (interpose " " (:view-box metadata)))
|
||||||
size (geom/size shape)
|
|
||||||
attrs (merge {:id key :key key ;; :transform (str rfm)
|
xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
|
||||||
:x x1 :y y1 :view-box view-box
|
tmp-displacement (gmt/translate tmp-displacement)
|
||||||
:preserve-aspect-ratio "none"
|
true (gmt/translate x1 y1)
|
||||||
:dangerouslySetInnerHTML {:__html content}}
|
true (gmt/scale scalex scaley))
|
||||||
size
|
|
||||||
(attrs/extract-style-attrs shape)
|
props {:id (str id)
|
||||||
(attrs/make-debug-attrs shape))]
|
:preserve-aspect-ratio "none"
|
||||||
[:svg attrs]))
|
:dangerouslySetInnerHTML {:__html content}
|
||||||
|
:transform (str xfmt)}
|
||||||
|
|
||||||
|
attrs (merge props (attrs/extract-style-attrs shape))]
|
||||||
|
[:g attrs]))
|
||||||
|
|
||||||
;; --- Icon SVG
|
;; --- Icon SVG
|
||||||
|
|
||||||
|
@ -48,7 +60,7 @@
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]}
|
||||||
[{:keys [content id metadata] :as shape}]
|
[{:keys [content id metadata] :as shape}]
|
||||||
(let [view-box (apply str (interpose " " (:view-box metadata)))
|
(let [view-box (apply str (interpose " " (:view-box metadata)))
|
||||||
id (str "icon-svg-" id)
|
props {:view-box view-box
|
||||||
props {:view-box view-box :id id
|
:id (str id)
|
||||||
:dangerouslySetInnerHTML {:__html content}}]
|
:dangerouslySetInnerHTML {:__html content}}]
|
||||||
[:svg props]))
|
[:svg props]))
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
(ns uxbox.main.ui.shapes.image
|
(ns uxbox.main.ui.shapes.image
|
||||||
(:require [beicon.core :as rx]
|
(:require [beicon.core :as rx]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[uxbox.util.http :as http]
|
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.store :as st]
|
[uxbox.store :as st]
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||||
[uxbox.main.data.images :as udi]
|
[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
|
;; --- Refs
|
||||||
|
|
||||||
|
@ -51,13 +51,22 @@
|
||||||
|
|
||||||
(mx/defc image-shape
|
(mx/defc image-shape
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]}
|
||||||
[{:keys [id x1 y1 image] :as shape}]
|
[shape]
|
||||||
(let [key (str "shape-" id)
|
(let [{:keys [id x1 y1 image
|
||||||
;; rfm (geom/transformation-matrix shape)
|
width height
|
||||||
size (geom/size shape)
|
tmp-resize-xform
|
||||||
props {:x x1 :y y1 :id key :key key
|
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"
|
:preserve-aspect-ratio "none"
|
||||||
:xlink-href (:url image)}
|
:xlink-href (:url image)
|
||||||
attrs (-> (attrs/extract-style-attrs shape)
|
:transform (str xfmt)
|
||||||
(merge props size))]
|
:width width
|
||||||
|
:height height}
|
||||||
|
|
||||||
|
attrs (merge props (attrs/extract-style-attrs shape))]
|
||||||
[:image attrs]))
|
[:image attrs]))
|
||||||
|
|
|
@ -5,13 +5,14 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.ui.shapes.path
|
(ns uxbox.main.ui.shapes.path
|
||||||
(:require [uxbox.util.mixins :as mx :include-macros true]
|
(:require [potok.core :as ptk]
|
||||||
[potok.core :as ptk]
|
|
||||||
[uxbox.store :as st]
|
[uxbox.store :as st]
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||||
[uxbox.main.data.shapes :as uds]
|
[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
|
;; --- Path Component
|
||||||
|
|
||||||
|
@ -45,11 +46,11 @@
|
||||||
|
|
||||||
(mx/defc path-shape
|
(mx/defc path-shape
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]}
|
||||||
[{:keys [id drawing?] :as shape}]
|
[{:keys [id tmp-resize-xform tmp-displacement] :as shape}]
|
||||||
(let [key (str "shape-" id)
|
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
|
||||||
rfm (geom/transformation-matrix shape)
|
tmp-displacement (gmt/translate tmp-displacement))
|
||||||
attrs (-> (attrs/extract-style-attrs shape)
|
props {:transform (str xfmt)
|
||||||
(merge {:id key :key key :d (render-path shape)})
|
:id (str id)
|
||||||
(merge (when-not drawing?
|
:d (render-path shape)}
|
||||||
#_{:transform (str rfm)})))]
|
attrs (merge props (attrs/extract-style-attrs shape))]
|
||||||
[:path attrs]))
|
[:path attrs]))
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
(ns uxbox.main.ui.shapes.rect
|
(ns uxbox.main.ui.shapes.rect
|
||||||
(:require [uxbox.main.ui.shapes.common :as common]
|
(:require [uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[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]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
;; --- Rect Component
|
;; --- Rect Component
|
||||||
|
@ -23,18 +25,26 @@
|
||||||
selected? (contains? selected id)
|
selected? (contains? selected id)
|
||||||
on-mouse-down #(common/on-mouse-down % shape selected)]
|
on-mouse-down #(common/on-mouse-down % shape selected)]
|
||||||
[:g.shape {:class (when selected? "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 shape identity)]))
|
||||||
|
|
||||||
;; --- Rect Shape
|
;; --- Rect Shape
|
||||||
|
|
||||||
(mx/defc rect-shape
|
(mx/defc rect-shape
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]}
|
||||||
[{:keys [id x1 y1] :as shape}]
|
[shape]
|
||||||
(let [key (str "shape-" id)
|
(let [{:keys [id x1 y1 width height
|
||||||
rfm (geom/transformation-matrix shape)
|
tmp-resize-xform
|
||||||
size (geom/size shape)
|
tmp-displacement]} (geom/size shape)
|
||||||
props {:x x1 :y y1 :id key :key key :transform (str rfm)}
|
|
||||||
attrs (-> (attrs/extract-style-attrs shape)
|
xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
|
||||||
(merge props size))]
|
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]))
|
[:rect attrs]))
|
||||||
|
|
|
@ -9,14 +9,16 @@
|
||||||
"Multiple selection handlers component."
|
"Multiple selection handlers component."
|
||||||
(:require [lentes.core :as l]
|
(:require [lentes.core :as l]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[uxbox.store :as st]
|
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
|
[uxbox.store :as st]
|
||||||
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.data.shapes :as uds]
|
[uxbox.main.data.shapes :as uds]
|
||||||
[uxbox.main.ui.workspace.base :as wb]
|
[uxbox.main.ui.workspace.base :as wb]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.main.ui.shapes.common :as scommon]
|
[uxbox.main.ui.shapes.common :as scommon]
|
||||||
[uxbox.main.geom :as geom]
|
[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.geom.point :as gpt]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
|
@ -41,115 +43,257 @@
|
||||||
|
|
||||||
(def edition-ref scommon/edition-ref)
|
(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
|
;; --- Resize Implementation
|
||||||
|
|
||||||
(defn- start-resize
|
(defn- start-resize
|
||||||
[vid sid]
|
[vid ids shape]
|
||||||
(letfn [(on-resize [[delta ctrl?]]
|
(letfn [(gen-matrix [shape {scalex :x scaley :y}]
|
||||||
(let [params {:vid vid :delta (assoc delta :lock ctrl?)}]
|
(case vid
|
||||||
(st/emit! (uds/update-vertex-position sid params))))
|
: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 []
|
(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
|
(let [stoper (->> wb/events-s
|
||||||
(rx/map first)
|
(rx/map first)
|
||||||
(rx/filter #(= % :mouse/up))
|
(rx/filter #(= % :mouse/up))
|
||||||
(rx/take 1))
|
(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/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)
|
(rlocks/acquire! :shape/resize)
|
||||||
(when @wb/alignment-ref
|
(rx/subscribe stream
|
||||||
(st/emit! (uds/initial-vertext-align sid vid)))
|
(partial on-resize shape)
|
||||||
(rx/subscribe stream on-resize nil on-end))))
|
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)
|
;; --- 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
|
(defn start-path-edition
|
||||||
[shape-id index]
|
[shape-id index]
|
||||||
(letfn [(on-move [delta]
|
(letfn [(on-move [delta]
|
||||||
|
@ -181,6 +325,43 @@
|
||||||
:stroke "#28c4d4"
|
:stroke "#28c4d4"
|
||||||
:style {:cursor "pointer"}}])]))
|
: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
|
(mx/defc selection-handlers
|
||||||
{:mixins [mx/reactive mx/static]}
|
{:mixins [mx/reactive mx/static]}
|
||||||
[]
|
[]
|
||||||
|
@ -196,20 +377,10 @@
|
||||||
(> shapes-num 1)
|
(> shapes-num 1)
|
||||||
(multiple-selection-handlers shapes zoom)
|
(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
|
:else
|
||||||
(cond
|
(single-selection-handlers shape zoom))))
|
||||||
(= :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)))))
|
|
||||||
|
|
|
@ -9,15 +9,16 @@
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[potok.core :as ptk]
|
[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.store :as st]
|
||||||
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.main.data.shapes :as uds]
|
[uxbox.main.data.shapes :as uds]
|
||||||
[uxbox.main.ui.shapes.common :as common]
|
[uxbox.main.ui.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
[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.util.rlocks :as rlocks]
|
||||||
[uxbox.main.geom :as geom])
|
[uxbox.util.mixins :as mx :include-macros true])
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
;; --- Events
|
;; --- Events
|
||||||
|
@ -106,8 +107,8 @@
|
||||||
[{:keys [id x1 y1 content] :as shape}]
|
[{:keys [id x1 y1 content] :as shape}]
|
||||||
(let [size (geom/size shape)
|
(let [size (geom/size shape)
|
||||||
style (make-style shape)
|
style (make-style shape)
|
||||||
rfm (geom/transformation-matrix shape)
|
;; rfm (geom/transformation-matrix shape)
|
||||||
props {:x x1 :y y1 :transform (str rfm)}
|
props {:x x1 :y y1} ;; :transform (str rfm)}
|
||||||
props (merge props size)]
|
props (merge props size)]
|
||||||
(letfn [#_(on-blur [ev]
|
(letfn [#_(on-blur [ev]
|
||||||
(rlocks/release! :ui/text-edit)
|
(rlocks/release! :ui/text-edit)
|
||||||
|
@ -126,15 +127,38 @@
|
||||||
|
|
||||||
;; --- Text Shape
|
;; --- 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
|
(mx/defc text-shape
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]
|
||||||
[{:keys [id x1 y1 content] :as shape}]
|
:did-update text-shape-did-update}
|
||||||
(let [key (str "shape-" id)
|
[{:keys [tmp-resize-xform] :as shape}]
|
||||||
rfm (geom/transformation-matrix shape)
|
(let [shape (cond-> (geom/size shape)
|
||||||
size (geom/size shape)
|
tmp-resize-xform (geom/transform shape tmp-resize-xform))
|
||||||
props {:x x1 :y y1
|
|
||||||
:transform (str rfm)}
|
{:keys [id x1 y1 content
|
||||||
attrs (merge props size)
|
width height
|
||||||
style (make-style shape)]
|
tmp-displacement]} (geom/size shape)
|
||||||
[:foreignObject attrs
|
|
||||||
[:p {:style style} content]]))
|
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]]))
|
||||||
|
|
|
@ -65,5 +65,10 @@
|
||||||
|
|
||||||
(defn encode
|
(defn encode
|
||||||
[data]
|
[data]
|
||||||
(let [w (t/writer :json {:handlers +write-handlers+})]
|
(try
|
||||||
(t/write w data)))
|
(let [w (t/writer :json {:handlers +write-handlers+})]
|
||||||
|
(t/write w data))
|
||||||
|
(catch :default e
|
||||||
|
(println "data:" data)
|
||||||
|
(throw e))))
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
[{:keys [x1 y1 rotation] :as shape}
|
[{:keys [x1 y1 rotation] :as shape}
|
||||||
{:keys [resize-width resize-height easing
|
{:keys [resize-width resize-height easing
|
||||||
element delay duration direction] :as opts}]
|
element delay duration direction] :as opts}]
|
||||||
(if (= direction :reverse)
|
#_(if (= direction :reverse)
|
||||||
(let [end (geom/transformation-matrix shape)]
|
(let [end (geom/transformation-matrix shape)]
|
||||||
(animate :targets [(str "#shape-" element)]
|
(animate :targets [(str "#shape-" element)]
|
||||||
:transform (str end)
|
:transform (str end)
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
|
|
||||||
(defn- run-rotate-interaction
|
(defn- run-rotate-interaction
|
||||||
[{:keys [element rotation direction easing delay duration] :as opts}]
|
[{: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))
|
dom (dom/get-element (str "shape-" element))
|
||||||
mtx1 (geom/transformation-matrix (update shape :rotation + rotation))
|
mtx1 (geom/transformation-matrix (update shape :rotation + rotation))
|
||||||
mtx2 (geom/transformation-matrix shape)]
|
mtx2 (geom/transformation-matrix shape)]
|
||||||
|
|
|
@ -52,15 +52,15 @@
|
||||||
{:pre [(map? shape)]}
|
{:pre [(map? shape)]}
|
||||||
(let [show-itx? (and (mx/react itx-flag-ref)
|
(let [show-itx? (and (mx/react itx-flag-ref)
|
||||||
(not (empty? (:interactions shape))))
|
(not (empty? (:interactions shape))))
|
||||||
rect (geom/inner-rect shape)]
|
rect (geom/shape->rect-shape shape)]
|
||||||
[:g {:id (str "itx-" (:id shape))
|
[:g {:id (str "itx-" (:id shape))
|
||||||
:style (when show-itx?
|
:style (when show-itx?
|
||||||
{:cursor "pointer"})}
|
{:cursor "pointer"})}
|
||||||
(factory shape)
|
(factory shape)
|
||||||
(when show-itx?
|
(when show-itx?
|
||||||
[:circle {:fill "#78dbbe"
|
[:circle {:fill "#78dbbe"
|
||||||
:cx (:x rect)
|
:cx (:x1 rect)
|
||||||
:cy (:y rect)
|
:cy (:y1 rect)
|
||||||
:r 5}])]))
|
:r 5}])]))
|
||||||
|
|
||||||
;; --- Shapes
|
;; --- Shapes
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue