♻️ Adds new properties to shapes

This commit is contained in:
alonso.torres 2020-05-22 16:45:39 +02:00
parent b5a6d9981d
commit e06d8e754f
20 changed files with 288 additions and 136 deletions

View file

@ -12,7 +12,6 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[next.jdbc :as jdbc])) [next.jdbc :as jdbc]))
(s/def ::name string?) (s/def ::name string?)
(s/def ::step (s/keys :req-un [::name ::desc ::fn])) (s/def ::step (s/keys :req-un [::name ::desc ::fn]))
(s/def ::steps (s/every ::step :kind vector?)) (s/def ::steps (s/every ::step :kind vector?))

View file

@ -105,6 +105,11 @@
(defn zip [col1 col2] (defn zip [col1 col2]
(map vector col1 col2)) (map vector col1 col2))
(defn mapm
"Map over the values of a map"
[mfn coll]
(into {} (map (fn [[key val]] [key (mfn key val)]) coll)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Parsing / Conversion ;; Data Parsing / Conversion
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -182,3 +182,7 @@
(multiply (multiply
v2-unit v2-unit
(point scalar-projection scalar-projection)))) (point scalar-projection scalar-projection))))
(defn center-points [points]
(let [k (point (count points))]
(reduce #(add %1 (divide %2 k)) (point) points)))

View file

@ -17,6 +17,15 @@
[uxbox.common.math :as mth] [uxbox.common.math :as mth]
[uxbox.common.data :as d])) [uxbox.common.data :as d]))
(defn- nilf
"Returns a new function that if you pass nil as any argument will
return nil"
[f]
(fn [& args]
(if (some nil? args)
nil
(apply f args))))
;; --- Relative Movement ;; --- Relative Movement
(declare move-rect) (declare move-rect)
@ -31,29 +40,23 @@
(defn move (defn move
"Move the shape relativelly to its current "Move the shape relativelly to its current
position applying the provided delta." position applying the provided delta."
[shape dpoint]
(case (:type shape)
:curve (move-path shape dpoint)
:path (move-path shape dpoint)
(move-rect shape dpoint)))
(defn- move-rect
"A specialized function for relative movement
for rect-like shapes."
[shape {dx :x dy :y}] [shape {dx :x dy :y}]
(assoc shape (let [inc-x (nilf (fn [x] (+ (-chk x) (-chk dx))))
:x (+ (-chk (:x shape)) (-chk dx)) inc-y (nilf (fn [y] (+ (-chk y) (-chk dy))))
:y (+ (-chk (:y shape)) (-chk dy)))) inc-point (nilf (fn [p] (-> p
(update :x inc-x)
(defn- move-path (update :y inc-y))))]
"A specialized function for relative movement (-> shape
for path shapes." (update :x inc-x)
[shape {dx :x dy :y}] (update :y inc-y)
(let [segments (:segments shape) (update-in [:selrect :x] inc-x)
xf (comp (update-in [:selrect :x1] inc-x)
(map #(update % :x + dx)) (update-in [:selrect :x2] inc-x)
(map #(update % :y + dy)))] (update-in [:selrect :y] inc-y)
(assoc shape :segments (into [] xf segments)))) (update-in [:selrect :y1] inc-y)
(update-in [:selrect :y2] inc-y)
(update :points #(mapv inc-point %))
(update :segments #(mapv inc-point %)))))
(defn recursive-move (defn recursive-move
"Move the shape and all its recursive children." "Move the shape and all its recursive children."
@ -70,8 +73,7 @@
"Move the shape to the exactly specified position." "Move the shape to the exactly specified position."
[shape position] [shape position]
(case (:type shape) (case (:type shape)
:path shape (:curve :path) shape
:curve shape
(absolute-move-rect shape position))) (absolute-move-rect shape position)))
(defn- absolute-move-rect (defn- absolute-move-rect
@ -198,14 +200,19 @@
:image (setup-image shape props) :image (setup-image shape props)
(setup-rect shape props))) (setup-rect shape props)))
(declare shape->points)
(declare points->selrect)
(defn- setup-rect (defn- setup-rect
"A specialized function for setup rect-like shapes." "A specialized function for setup rect-like shapes."
[shape {:keys [x y width height]}] [shape {:keys [x y width height]}]
(assoc shape (as-> shape $
:x x (assoc $ :x x
:y y :y y
:width width :width width
:height height)) :height height)
(assoc $ :points (shape->points $))
(assoc $ :selrect (points->selrect (:points $)))))
(defn- setup-image (defn- setup-image
[{:keys [metadata] :as shape} {:keys [x y width height] :as props}] [{:keys [metadata] :as shape} {:keys [x y width height] :as props}]
@ -228,8 +235,7 @@
"Coerce shape to rect like shape." "Coerce shape to rect like shape."
[{:keys [type] :as shape}] [{:keys [type] :as shape}]
(case type (case type
:path (path->rect-shape shape) (:curve :path) (path->rect-shape shape)
:curve (path->rect-shape shape)
(rect->rect-shape shape))) (rect->rect-shape shape)))
(defn shapes->rect-shape (defn shapes->rect-shape
@ -249,13 +255,43 @@
:height (- maxy miny) :height (- maxy miny)
:type :rect})) :type :rect}))
;; -- Points
(declare transform-shape-point)
(defn shape->points [shape]
(let [points
(case (:type shape)
(:curve :path) (:segments shape)
(let [{:keys [x y width height]} shape]
[(gpt/point x y)
(gpt/point (+ x width) y)
(gpt/point (+ x width) (+ y height))
(gpt/point x (+ y height))]))]
(mapv #(transform-shape-point % shape (:transform shape (gmt/matrix))) points)))
(defn points->selrect [points]
(let [minx (transduce (map :x) min points)
miny (transduce (map :y) min points)
maxx (transduce (map :x) max points)
maxy (transduce (map :y) max points)]
{:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny)
:type :rect}))
;; Shape->PATH
(declare rect->path) (declare rect->path)
(defn shape->path (defn shape->path
[shape] [shape]
(case (:type shape) (case (:type shape)
:path shape (:curve :path) shape
:curve shape
(rect->path shape))) (rect->path shape)))
(defn rect->path (defn rect->path
@ -282,20 +318,9 @@
(defn- path->rect-shape (defn- path->rect-shape
[{:keys [segments] :as shape}] [{:keys [segments] :as shape}]
(let [minx (transduce (map :x) min segments) (merge shape
miny (transduce (map :y) min segments) {:type :rect}
maxx (transduce (map :x) max segments) (:selrect shape)))
maxy (transduce (map :y) max segments)]
(assoc shape
:type :rect
:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny))))
;; --- Resolve Shape ;; --- Resolve Shape
@ -365,19 +390,11 @@
;; --- Outer Rect ;; --- Outer Rect
(declare transform-apply-modifiers)
(defn selection-rect-shape
[shape]
(-> shape
(transform-apply-modifiers)
(shape->rect-shape)))
(defn selection-rect (defn selection-rect
"Returns a rect that contains all the shapes and is aware of the "Returns a rect that contains all the shapes and is aware of the
rotation of each shape. Mainly used for multiple selection." rotation of each shape. Mainly used for multiple selection."
[shapes] [shapes]
(let [xf-resolve-shape (map selection-rect-shape) (let [xf-resolve-shape (map :selrect)
shapes (into [] xf-resolve-shape shapes) shapes (into [] xf-resolve-shape shapes)
minx (transduce (map :x1) min shapes) minx (transduce (map :x1) min shapes)
miny (transduce (map :y1) min shapes) miny (transduce (map :y1) min shapes)
@ -685,19 +702,27 @@
(gmt/skew (- skew-angle) 0) (gmt/skew (- skew-angle) 0)
(gmt/rotate (- rotation-angle))) (gmt/rotate (- rotation-angle)))
new-shape (-> shape new-shape (as-> shape $
(merge rec) (merge $ rec)
(update :x #(mth/precision % 2)) (update $ :x #(mth/precision % 2))
(update :y #(mth/precision % 2)) (update $ :y #(mth/precision % 2))
(fix-invalid-rect-values) (fix-invalid-rect-values $)
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix)) (update $ :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
(update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))] (update $ :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix))))
(assoc $ :points (shape->points $))
(assoc $ :selrect (points->selrect (:points $)))
(update $ :rotation #(mod (+ % (get-in $ [:modifiers :rotation] 0)) 360))
)]
new-shape)) new-shape))
(declare update-path-selrect)
(defn transform-path-shape (defn transform-path-shape
[shape] [shape]
(transform-apply-modifiers shape) (-> shape
transform-apply-modifiers
update-path-selrect)
;; TODO: Addapt for paths is not working ;; TODO: Addapt for paths is not working
#_(let [shape-path (transform-apply-modifiers shape) #_(let [shape-path (transform-apply-modifiers shape)
shape-path-center (center shape-path) shape-path-center (center shape-path)
@ -715,14 +740,15 @@
"Transform the shape properties given the modifiers" "Transform the shape properties given the modifiers"
([shape] (transform-shape nil shape)) ([shape] (transform-shape nil shape))
([frame shape] ([frame shape]
(let [new-shape (case (:type shape) (let [new-shape
:path (transform-path-shape shape) (if (:modifiers shape)
:curve (transform-path-shape shape) (as-> (case (:type shape)
(transform-rect-shape shape))] (:curve :path) (transform-path-shape shape)
(transform-rect-shape shape)) $
(dissoc $ :modifiers))
shape)]
(-> new-shape (-> new-shape
(translate-to-frame frame) (translate-to-frame frame)))))
(update :rotation #(mod (+ % (get-in shape [:modifiers :rotation] 0)) 360))
(dissoc :modifiers)))))
(defn transform-matrix (defn transform-matrix
@ -735,6 +761,15 @@
(gmt/multiply (:transform shape (gmt/matrix))) (gmt/multiply (:transform shape (gmt/matrix)))
(gmt/translate (gpt/negate shape-center)))))) (gmt/translate (gpt/negate shape-center))))))
(defn update-path-selrect [shape]
(as-> shape $
(assoc $ :points (shape->points $))
(assoc $ :selrect (points->selrect (:points $)))
(assoc $ :x (get-in $ [:selrect :x]))
(assoc $ :y (get-in $ [:selrect :y]))
(assoc $ :width (get-in $ [:selrect :width]))
(assoc $ :height (get-in $ [:selrect :height]))))
(defn adjust-to-viewport (defn adjust-to-viewport
([viewport srect] (adjust-to-viewport viewport srect nil)) ([viewport srect] (adjust-to-viewport viewport srect nil))
([viewport srect {:keys [padding] :or {padding 0}}] ([viewport srect {:keys [padding] :or {padding 0}}]

View file

@ -0,0 +1,63 @@
(ns uxbox.common.migrations
(:require
[uxbox.common.pages :as p]
[uxbox.common.geom.shapes :as gsh]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.uuid :as uuid]
[uxbox.common.data :as d]))
(defmulti migrate :version)
(defn migrate-page
([page from-version to-version]
(-> page
(assoc :version to-version)
(migrate)))
([{:keys [version] :as page}]
(reduce #(migrate-page % (:version %1) %2)
page
(range version (inc p/page-version)))))
;; Default handler, noop
(defmethod migrate :default [page] page)
;; -- MIGRATIONS --
(defmethod migrate 4 [page]
(prn "Migrate " (:id page))
;; We changed the internal model of the shapes so they have their selection rect
;; and the vertices
(letfn [;; Creates a new property `points` that stores the transformed points inside the shape
;; this will be used for the snaps and the selection rect
(calculate-shape-points [objects]
(->> objects
(d/mapm
(fn [id shape]
(if (= (:id shape) uuid/zero)
shape
(assoc shape :points (gsh/shape->points shape)))))))
;; Creates a new property `selrect` that stores the selection rect for the shape
(calculate-shape-selrects [objects]
(->> objects
(d/mapm
(fn [id shape]
(if (= (:id shape) uuid/zero)
shape
(assoc shape :selrect (gsh/points->selrect (:points shape))))))))]
(-> page
;; We only store the version in the page data
(update :data dissoc :version )
;; Adds vertices to shapes
(update-in [:data :objects] calculate-shape-points)
;; Creates selection rects for shapes
(update-in [:data :objects] calculate-shape-selrects))))

View file

@ -16,6 +16,8 @@
[uxbox.common.exceptions :as ex] [uxbox.common.exceptions :as ex]
[uxbox.common.spec :as us])) [uxbox.common.spec :as us]))
(def page-version 4)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Page Data Structure Helpers ;; Page Data Structure Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -184,6 +186,22 @@
(s/def ::width number?) (s/def ::width number?)
(s/def ::height number?) (s/def ::height number?)
(s/def ::index integer?) (s/def ::index integer?)
(s/def ::x1 number?)
(s/def ::y1 number?)
(s/def ::x2 number?)
(s/def ::y2 number?)
(s/def ::selrect (s/keys :req-un [::x
::y
::x1
::y1
::x2
::y2
::width
::height]))
(s/def ::point (s/keys :req-un [::x ::y]))
(s/def ::points (s/coll-of ::point :kind vector?))
(s/def ::shape-attrs (s/def ::shape-attrs
(s/keys :opt-un [::blocked (s/keys :opt-un [::blocked
@ -211,7 +229,9 @@
::stroke-alignment ::stroke-alignment
::text-align ::text-align
::width ::height ::width ::height
::interactions])) ::interactions
::selrect
::points]))
(s/def ::minimal-shape (s/def ::minimal-shape
(s/keys :req-un [::type ::name] (s/keys :req-un [::type ::name]
@ -282,8 +302,7 @@
(def default-page-data (def default-page-data
"A reference value of the empty page data." "A reference value of the empty page data."
{:version 3 {:options {}
:options {}
:objects :objects
{root {root
{:id root {:id root
@ -469,5 +488,3 @@
[shape op] [shape op]
(ex/raise :type :operation-not-implemented (ex/raise :type :operation-not-implemented
:context {:type (:type op)})) :context {:type (:type op)}))

View file

@ -9,6 +9,7 @@
(ns uxbox.main.data.workspace (ns uxbox.main.data.workspace
(:require (:require
[uxbox.util.debug :refer [logjs]]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[clojure.set :as set] [clojure.set :as set]
@ -16,6 +17,7 @@
[uxbox.common.data :as d] [uxbox.common.data :as d]
[uxbox.common.exceptions :as ex] [uxbox.common.exceptions :as ex]
[uxbox.common.pages :as cp] [uxbox.common.pages :as cp]
[uxbox.common.migrations :as mg]
[uxbox.common.spec :as us] [uxbox.common.spec :as us]
[uxbox.common.uuid :as uuid] [uxbox.common.uuid :as uuid]
[uxbox.config :as cfg] [uxbox.config :as cfg]
@ -149,6 +151,7 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [page (get-in state [:workspace-pages page-id]) (let [page (get-in state [:workspace-pages page-id])
page (mg/migrate-page page)
local (get-in state [:workspace-cache page-id] workspace-default)] local (get-in state [:workspace-cache page-id] workspace-default)]
(-> state (-> state
(assoc :current-page-id page-id ; mainly used by events (assoc :current-page-id page-id ; mainly used by events

View file

@ -165,9 +165,8 @@
(defn- calculate-frame-overlap (defn- calculate-frame-overlap
[frames shape] [frames shape]
(let [shape (geom/shape->rect-shape shape) (let [xf (comp
xf (comp (filter #(geom/overlaps? % (:selrect shape)))
(filter #(geom/overlaps? % shape))
(take 1)) (take 1))
frame (first (into [] xf frames))] frame (first (into [] xf frames))]
(or (:id frame) uuid/zero))) (or (:id frame) uuid/zero)))

View file

@ -210,7 +210,8 @@
params {:name name params {:name name
:file-id file-id :file-id file-id
:ordering ordering :ordering ordering
:data cp/default-page-data}] :data cp/default-page-data
:version cp/page-version}]
(->> (rp/mutation :create-page params) (->> (rp/mutation :create-page params)
(rx/map page-created)))))) (rx/map page-created))))))

View file

@ -80,6 +80,8 @@
(let [{:keys [width height rotation]} shape (let [{:keys [width height rotation]} shape
shapev (-> (gpt/point width height)) shapev (-> (gpt/point width height))
rotation (if (#{:curve :path} (:type shape)) 0 rotation)
;; Vector modifiers depending on the handler ;; Vector modifiers depending on the handler
handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y)) handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y))
@ -129,7 +131,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [shape (gsh/shape->rect-shape shape) (let [;;shape (gsh/shape->rect-shape shape)
initial @ms/mouse-position initial @ms/mouse-position
stoper (rx/filter ms/mouse-up? stream) stoper (rx/filter ms/mouse-up? stream)
page-id (get state :current-page-id) page-id (get state :current-page-id)

View file

@ -44,8 +44,7 @@
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
(let [{:keys [x y width height] (let [{:keys [x y width height]
:as shape} (->> (unchecked-get props "shape") :as shape} (->> (unchecked-get props "shape") :selrect)
(geom/selection-rect-shape))
childs (unchecked-get props "childs") childs (unchecked-get props "childs")
frame (unchecked-get props "frame") frame (unchecked-get props "frame")

View file

@ -196,7 +196,8 @@
(assoc-in [:workspace-local :drawing ::initialized?] true))) (assoc-in [:workspace-local :drawing ::initialized?] true)))
(insert-point-segment [state point] (insert-point-segment [state point]
(update-in state [:workspace-local :drawing :segments] (fnil conj []) point)) (-> state
(update-in [:workspace-local :drawing :segments] (fnil conj []) point)))
(update-point-segment [state index point] (update-point-segment [state index point]
(let [segments (count (get-in state [:workspace-local :drawing :segments])) (let [segments (count (get-in state [:workspace-local :drawing :segments]))
@ -204,8 +205,12 @@
(cond-> state (cond-> state
exists? (assoc-in [:workspace-local :drawing :segments index] point)))) exists? (assoc-in [:workspace-local :drawing :segments index] point))))
(remove-dangling-segmnet [state] (finish-drawing-path [state]
(update-in state [:workspace-local :drawing :segments] #(vec (butlast %))))] (update-in
state [:workspace-local :drawing]
(fn [shape] (-> shape
(update :segments #(vec (butlast %)))
(geom/update-path-selrect)))))]
(ptk/reify ::handle-drawing-path (ptk/reify ::handle-drawing-path
ptk/WatchEvent ptk/WatchEvent
@ -263,9 +268,11 @@
point)] point)]
#(update-point-segment % index point)))) #(update-point-segment % index point))))
(rx/take-until stoper)) (rx/take-until stoper))
(rx/of remove-dangling-segmnet (rx/of finish-drawing-path
handle-finish-drawing)))))))) handle-finish-drawing))))))))
(def simplify-tolerance 0.3)
(def handle-drawing-curve (def handle-drawing-curve
(letfn [(stoper-event? [{:keys [type shift] :as event}] (letfn [(stoper-event? [{:keys [type shift] :as event}]
(ms/mouse-event? event) (= type :up)) (ms/mouse-event? event) (= type :up))
@ -276,8 +283,13 @@
(insert-point-segment [state point] (insert-point-segment [state point]
(update-in state [:workspace-local :drawing :segments] (fnil conj []) point)) (update-in state [:workspace-local :drawing :segments] (fnil conj []) point))
(simplify-drawing-path [state tolerance] (finish-drawing-curve [state]
(update-in state [:workspace-local :drawing :segments] path/simplify tolerance))] (update-in
state [:workspace-local :drawing]
(fn [shape]
(-> shape
(update :segments #(path/simplify % simplify-tolerance))
(geom/update-path-selrect)))))]
(ptk/reify ::handle-drawing-curve (ptk/reify ::handle-drawing-curve
ptk/WatchEvent ptk/WatchEvent
@ -290,7 +302,7 @@
(->> mouse (->> mouse
(rx/map (fn [pt] #(insert-point-segment % pt))) (rx/map (fn [pt] #(insert-point-segment % pt)))
(rx/take-until stoper)) (rx/take-until stoper))
(rx/of #(simplify-drawing-path % 0.3) (rx/of finish-drawing-curve
handle-finish-drawing))))))) handle-finish-drawing)))))))
(def handle-finish-drawing (def handle-finish-drawing
@ -308,10 +320,8 @@
:text 16 :text 16
5) 5)
shape (-> shape shape (-> shape
(geom/transform-shape) geom/transform-shape
(update :width #(max shape-min-width %)) (dissoc ::initialized?)) ]
(update :height #(max shape-min-height %))
(dissoc shape ::initialized?))]
;; Add & select the created shape to the workspace ;; Add & select the created shape to the workspace
(rx/of dw/deselect-all (rx/of dw/deselect-all
(dw/add-shape shape))))))))) (dw/add-shape shape)))))))))
@ -336,7 +346,7 @@
(mf/defc generic-draw-area (mf/defc generic-draw-area
[{:keys [shape zoom]}] [{:keys [shape zoom]}]
(let [{:keys [x y width height]} (geom/selection-rect-shape shape)] (let [{:keys [x y width height]} (:selrect shape)]
(when (and x y) (when (and x y)
[:g [:g
[:& shapes/shape-wrapper {:shape shape}] [:& shapes/shape-wrapper {:shape shape}]

View file

@ -187,7 +187,7 @@
[:g.controls [:g.controls
;; Selection rect ;; Selection rect
[:& selection-rect {:rect shape [:& selection-rect {:rect selrect
:transform transform :transform transform
:zoom zoom}] :zoom zoom}]
@ -283,7 +283,6 @@
(let [shape-id (:id shape) (let [shape-id (:id shape)
shape (geom/transform-shape shape) shape (geom/transform-shape shape)
shape' (if (debug? :simple-selection) (geom/selection-rect [shape]) shape) shape' (if (debug? :simple-selection) (geom/selection-rect [shape]) shape)
on-resize on-resize
#(do (dom/stop-propagation %2) #(do (dom/stop-propagation %2)
(st/emit! (dw/start-resize %1 #{shape-id} shape'))) (st/emit! (dw/start-resize %1 #{shape-id} shape')))

View file

@ -20,7 +20,7 @@
;; namespace under uxbox.ui.workspace.shapes.* prefix, all the ;; namespace under uxbox.ui.workspace.shapes.* prefix, all the
;; others are defined using a generic wrapper implemented in ;; others are defined using a generic wrapper implemented in
;; common. ;; common.
[uxbox.main.ui.workspace.shapes.bbox :as bbox] [uxbox.main.ui.workspace.shapes.bounding-box :refer [bounding-box]]
[uxbox.main.ui.workspace.shapes.common :as common] [uxbox.main.ui.workspace.shapes.common :as common]
[uxbox.main.ui.workspace.shapes.frame :as frame] [uxbox.main.ui.workspace.shapes.frame :as frame]
[uxbox.main.ui.workspace.shapes.group :as group] [uxbox.main.ui.workspace.shapes.group :as group]
@ -71,7 +71,8 @@
;; Only used when drawing a new frame. ;; Only used when drawing a new frame.
:frame [:> frame-wrapper {:shape shape}] :frame [:> frame-wrapper {:shape shape}]
nil) nil)
[:& bbox/bounding-box {:shape shape :frame frame}]]))) [:& bounding-box {:shape (->> shape (geom/transform-shape frame)) :frame frame}]])))
(def group-wrapper (group/group-wrapper-factory shape-wrapper)) (def group-wrapper (group/group-wrapper-factory shape-wrapper))
(def frame-wrapper (frame/frame-wrapper-factory shape-wrapper)) (def frame-wrapper (frame/frame-wrapper-factory shape-wrapper))

View file

@ -4,7 +4,7 @@
;; ;;
;; Copyright (c) 2020 UXBOX Labs SL ;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.workspace.shapes.bbox (ns uxbox.main.ui.workspace.shapes.bounding-box
(:require (:require
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.alpha :as mf] [rumext.alpha :as mf]
@ -13,37 +13,61 @@
[uxbox.common.geom.matrix :as gmt] [uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt] [uxbox.common.geom.point :as gpt]
[uxbox.util.debug :refer [debug?]] [uxbox.util.debug :refer [debug?]]
[uxbox.main.refs :as refs]
["randomcolor" :as rdcolor])) ["randomcolor" :as rdcolor]))
(defn fixed (defn fixed
[num] [num]
(when num (.toFixed num 2))) (when num (.toFixed num 2)))
(mf/defc cross-point [{:keys [point zoom color]}]
(let [width (/ 10 zoom)]
[:g.point
[:line {:x1 (- (:x point) 10) :y1 (- (:y point) 10)
:x2 (+ (:x point) 10) :y2 (+ (:y point) 10)
:stroke color
:stroke-width "1px"
:stroke-opacity 0.5}]
[:line {:x1 (+ (:x point) 10) :y1 (- (:y point) 10)
:x2 (- (:x point) 10) :y2 (+ (:y point) 10)
:stroke color
:stroke-width "1px"
:stroke-opacity 0.5}]]))
(mf/defc bounding-box (mf/defc bounding-box
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
(when (debug? :bounding-boxes) (when (debug? :bounding-boxes)
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame") frame (unchecked-get props "frame")
selrect (-> shape selrect (-> shape :selrect)
(geom/selection-rect-shape) shape-center (geom/center shape)
(geom/translate-to-frame frame)) line-color (rdcolor #js {:seed (str (:id shape))})
shape-center (geom/center selrect) zoom (mf/deref refs/selected-zoom)]
line-color (rdcolor #js {:seed (str (:id shape))})] [:g.bounding-box
[:g
[:text {:x (:x selrect) [:text {:x (:x selrect)
:y (- (:y selrect) 5) :y (- (:y selrect) 5)
:font-size 10 :font-size 10
:fill "red" :fill line-color
:stroke "white" :stroke "white"
:stroke-width 0.1} :stroke-width 0.1}
(str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fixed (:x shape)) (fixed (:y shape)))] (str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fixed (:x shape)) (fixed (:y shape)))]
[:& cross-point {:point shape-center
:zoom zoom
:color line-color}]
(for [point (:points shape)]
[:& cross-point {:point point
:zoom zoom
:color line-color}])
[:rect {:x (:x selrect) [:rect {:x (:x selrect)
:y (:y selrect) :y (:y selrect)
:width (:width selrect) :width (:width selrect)
:height (:height selrect) :height (:height selrect)
:style {:stroke "red" :style {:stroke line-color
:fill "transparent" :fill "transparent"
:stroke-width "1px" :stroke-width "1px"
:stroke-opacity 0.5 :stroke-opacity 0.5

View file

@ -40,8 +40,8 @@
"Calculate the best position to draw an interaction line "Calculate the best position to draw an interaction line
between two shapes" between two shapes"
[orig-shape dest-shape] [orig-shape dest-shape]
(let [orig-rect (geom/selection-rect-shape orig-shape) (let [orig-rect (:selrect orig-shape)
dest-rect (geom/selection-rect-shape dest-shape) dest-rect (:selrect dest-shape)
orig-x-left (:x orig-rect) orig-x-left (:x orig-rect)
orig-x-right (+ orig-x-left (:width orig-rect)) orig-x-right (+ orig-x-left (:width orig-rect))
@ -71,7 +71,7 @@
"Calculate the best position to draw an interaction line "Calculate the best position to draw an interaction line
between one shape and one point" between one shape and one point"
[orig-shape dest-point] [orig-shape dest-point]
(let [orig-rect (geom/selection-rect-shape orig-shape) (let [orig-rect (:selrect orig-shape)
orig-x-left (:x orig-rect) orig-x-left (:x orig-rect)
orig-x-right (+ orig-x-left (:width orig-rect)) orig-x-right (+ orig-x-left (:width orig-rect))
@ -159,7 +159,7 @@
(mf/defc interaction-handle (mf/defc interaction-handle
[{:keys [shape selected zoom] :as props}] [{:keys [shape selected zoom] :as props}]
(let [shape-rect (geom/selection-rect-shape shape) (let [shape-rect (:selrect shape)
handle-x (+ (:x shape-rect) (:width shape-rect)) handle-x (+ (:x shape-rect) (:width shape-rect))
handle-y (+ (:y shape-rect) (/ (:height shape-rect) 2))] handle-y (+ (:y shape-rect) (/ (:height shape-rect) 2))]
[:g {:on-mouse-down #(on-mouse-down % shape selected)} [:g {:on-mouse-down #(on-mouse-down % shape selected)}

View file

@ -25,13 +25,9 @@
(defn user-coords-vector (defn user-coords-vector
[shape] [shape]
(let [{sel-x :x sel-y :y :as selrect} (let [oldselrec (-> shape gsh/shape->path (gsh/center-transform (:transform shape)) gsh/shape->rect-shape)
(-> shape {sel-x :x sel-y :y :as selrec} #_(:selrect shape) oldselrec
gsh/shape->path
(gsh/center-transform (:transform shape))
gsh/shape->rect-shape)
{rec-x :x rec-y :y} (-> shape gsh/shape->rect-shape) {rec-x :x rec-y :y} (-> shape gsh/shape->rect-shape)
dx (- rec-x sel-x) dx (- rec-x sel-x)
dy (- rec-y sel-y)] dy (- rec-y sel-y)]
(-> (gpt/point dx dy) (-> (gpt/point dx dy)
@ -141,13 +137,13 @@
:type "number" :type "number"
:no-validate true :no-validate true
:on-change on-pos-x-change :on-change on-pos-x-change
:value (:x shape)}]] :value (-> shape :x (math/precision 2))}]]
[:div.input-element.Yaxis [:div.input-element.Yaxis
[:input.input-text {:placeholder "y" [:input.input-text {:placeholder "y"
:type "number" :type "number"
:no-validate true :no-validate true
:on-change on-pos-y-change :on-change on-pos-y-change
:value (:y shape)}]]]) :value (-> shape :y (math/precision 2))}]]])
(when (options :rotation) (when (options :rotation)
[:div.row-flex [:div.row-flex

View file

@ -3,7 +3,7 @@
(def debug-options #{:bounding-boxes :group :events :rotation-handler :resize-handler :selection-center #_:simple-selection }) (def debug-options #{:bounding-boxes :group :events :rotation-handler :resize-handler :selection-center #_:simple-selection })
(defonce ^:dynamic *debug* (atom #{})) (defonce ^:dynamic *debug* (atom #{:bounding-boxes}))
(defn debug-all! [] (reset! *debug* debug-options)) (defn debug-all! [] (reset! *debug* debug-options))
(defn debug-none! [] (reset! *debug* #{})) (defn debug-none! [] (reset! *debug* #{}))

View file

@ -26,9 +26,8 @@
(defn shape-snap-points (defn shape-snap-points
[shape] [shape]
(let [modified-path (gsh/transform-apply-modifiers shape) (let [shape (gsh/transform-shape shape)
shape-center (gsh/center modified-path)] shape-center (gsh/center shape)]
(case (:type shape) (case (:type shape)
:frame (-> modified-path gsh/shape->rect-shape frame-snap-points) :frame (-> shape gsh/shape->rect-shape frame-snap-points)
(:path :curve) (into #{shape-center} (-> modified-path gsh/shape->rect-shape :segments)) (into #{shape-center} (-> shape :points)))))
(into #{shape-center} (-> modified-path :segments)))))

View file

@ -12,6 +12,7 @@
[okulary.core :as l] [okulary.core :as l]
[uxbox.common.uuid :as uuid] [uxbox.common.uuid :as uuid]
[uxbox.common.pages :as cp] [uxbox.common.pages :as cp]
[uxbox.common.data :as d]
[uxbox.worker.impl :as impl] [uxbox.worker.impl :as impl]
[uxbox.util.range-tree :as rt] [uxbox.util.range-tree :as rt]
[uxbox.util.geom.snap-points :as snap] [uxbox.util.geom.snap-points :as snap]
@ -38,11 +39,6 @@
(mapcat (process-shape coord)) (mapcat (process-shape coord))
(reduce into-tree (rt/make-tree))))) (reduce into-tree (rt/make-tree)))))
(defn- mapm
"Map over the values of a map"
[mfn coll]
(into {} (map (fn [[key val]] [key (mfn key val)]) coll)))
(defn- initialize-snap-data (defn- initialize-snap-data
"Initialize the snap information with the current workspace information" "Initialize the snap information with the current workspace information"
[objects] [objects]
@ -51,16 +47,16 @@
(group-by :frame-id)) (group-by :frame-id))
frame-shapes (->> (cp/select-frames objects) frame-shapes (->> (cp/select-frames objects)
(reduce #(update %1 (:id %2) conj %2) frame-shapes))] (reduce #(update %1 (:id %2) conj %2) frame-shapes))]
(mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x) (d/mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x)
:y (create-coord-data frame-id shapes :y)}) :y (create-coord-data frame-id shapes :y)})
frame-shapes))) frame-shapes)))
(defn- log-state (defn- log-state
"Helper function to print a friendly version of the snap tree. Debugging purposes" "Helper function to print a friendly version of the snap tree. Debugging purposes"
[] []
(let [process-frame-data #(mapm rt/as-map %) (let [process-frame-data #(d/mapm rt/as-map %)
process-page-data #(mapm process-frame-data %)] process-page-data #(d/mapm process-frame-data %)]
(js/console.log "STATE" (clj->js (mapm process-page-data @state))))) (js/console.log "STATE" (clj->js (d/mapm process-page-data @state)))))
(defn- index-page [state page-id objects] (defn- index-page [state page-id objects]
(let [snap-data (initialize-snap-data objects)] (let [snap-data (initialize-snap-data objects)]