From 56e2db22eb06db608804dc9653c40050878fd1ad Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 20 Sep 2021 11:47:02 +0200 Subject: [PATCH] :sparkles: Clip boolean selrects --- common/src/app/common/pages.cljc | 2 +- common/src/app/common/pages/indices.cljc | 26 +++++---- common/src/app/common/path/bool.cljc | 40 ++++++++------ .../src/app/common/path/shapes_to_path.cljc | 54 ++++++++++--------- .../src/app/main/data/workspace/booleans.cljs | 14 +++-- frontend/src/app/main/ui/shapes/bool.cljs | 5 +- .../app/main/ui/workspace/viewport/hooks.cljs | 22 +++++--- frontend/src/app/worker/selection.cljs | 48 +++++++++-------- 8 files changed, 120 insertions(+), 91 deletions(-) diff --git a/common/src/app/common/pages.cljc b/common/src/app/common/pages.cljc index 640a6858e..accb623ad 100644 --- a/common/src/app/common/pages.cljc +++ b/common/src/app/common/pages.cljc @@ -73,7 +73,7 @@ (d/export indices/update-z-index) (d/export indices/generate-child-all-parents-index) (d/export indices/generate-child-parent-index) -(d/export indices/create-mask-index) +(d/export indices/create-clip-index) ;; Process changes (d/export changes/process-changes) diff --git a/common/src/app/common/pages/indices.cljc b/common/src/app/common/pages/indices.cljc index c1681fef1..5a5f40595 100644 --- a/common/src/app/common/pages/indices.cljc +++ b/common/src/app/common/pages/indices.cljc @@ -95,16 +95,24 @@ (map #(vector (:id %) (shape->parents %))) (into {}))))) -(defn create-mask-index +(defn create-clip-index "Retrieves the mask information for an object" [objects parents-index] - (let [retrieve-masks + (let [retrieve-clips (fn [_ parents] - ;; TODO: use transducers? - (->> parents - (map #(get objects %)) - (filter #(:masked-group? %)) - ;; Retrieve the masking element - (mapv #(get objects (->> % :shapes first)))))] + (let [lookup-object (fn [id] (get objects id)) + get-clip-parents + (fn [shape] + (cond-> [] + (:masked-group? shape) + (conj (get objects (->> shape :shapes first))) + + (= :bool (:type shape)) + (conj shape)))] + + (into [] + (comp (map lookup-object) + (mapcat get-clip-parents)) + parents)))] (->> parents-index - (d/mapm retrieve-masks)))) + (d/mapm retrieve-clips)))) diff --git a/common/src/app/common/path/bool.cljc b/common/src/app/common/path/bool.cljc index 3d61ff98c..d12763c5d 100644 --- a/common/src/app/common/path/bool.cljc +++ b/common/src/app/common/path/bool.cljc @@ -12,6 +12,23 @@ [app.common.path.commands :as upc] [app.common.path.subpaths :as ups])) +(defn- reverse-command + "Reverses a single command" + [command] + + (let [{old-x :x old-y :y} (:params command) + {:keys [x y]} (:prev command) + {:keys [c1x c1y c2x c2y]} (:params command)] + + (-> command + (assoc :prev (gpt/point old-x old-y)) + (update :params assoc :x x :y y) + + (cond-> (= :curve-to (:command command)) + (update :params assoc + :c1x c2x :c1y c2y + :c2x c1x :c2y c1y))))) + (defn- split-command [cmd values] (case (:command cmd) @@ -142,7 +159,12 @@ (d/concat [] (->> content-a-split (filter #(not (contains-segment? % content-b)))) - (->> content-b-split (filter #(contains-segment? % content-a))))) + + ;; Reverse second content so we can have holes inside other shapes + (->> content-b-split + (reverse) + (mapv reverse-command) + (filter #(contains-segment? % content-a))))) (defn create-intersection [content-a content-a-split content-b content-b-split] ;; Pick all segments in content-a that are inside content-b @@ -152,22 +174,6 @@ (->> content-a-split (filter #(contains-segment? % content-b))) (->> content-b-split (filter #(contains-segment? % content-a))))) -(defn reverse-command - "Reverses a single command" - [command] - - (let [{old-x :x old-y :y} (:params command) - {:keys [x y]} (:prev command) - {:keys [c1x c1y c2x c2y]} (:params command)] - - (-> command - (assoc :prev (gpt/point old-x old-y)) - (update :params assoc :x x :y y) - - (cond-> (= :curve-to (:command command)) - (update :params assoc - :c1x c2x :c1y c2y - :c2x c1x :c2y c1y))))) (defn create-exclusion [content-a content-b] ;; Pick all segments but reverse content-b (so it makes an exclusion) diff --git a/common/src/app/common/path/shapes_to_path.cljc b/common/src/app/common/path/shapes_to_path.cljc index 8bd1f24f6..83a9358c8 100644 --- a/common/src/app/common/path/shapes_to_path.cljc +++ b/common/src/app/common/path/shapes_to_path.cljc @@ -180,33 +180,35 @@ (defn convert-to-path "Transforms the given shape to a path" - [{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as shape} objects] - (assert (map? objects)) - (cond - (= (:type shape) :group) - (group-to-path shape objects) + ([shape] + (convert-to-path shape {})) + ([{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as shape} objects] + (assert (map? objects)) + (cond + (= (:type shape) :group) + (group-to-path shape objects) - (= (:type shape) :bool) - (bool-to-path shape objects) + (= (:type shape) :bool) + (bool-to-path shape objects) - (contains? allowed-transform-types type) - (let [new-content - (case type - :circle (circle->path x y width height) - #_:else (rect->path x y width height r1 r2 r3 r4 rx)) + (contains? allowed-transform-types type) + (let [new-content + (case type + :circle (circle->path x y width height) + #_:else (rect->path x y width height r1 r2 r3 r4 rx)) - ;; Apply the transforms that had the shape - transform (:transform shape) - new-content (cond-> new-content - (some? transform) - (gsp/transform-content (gmt/transform-in (gsc/center-shape shape) transform)))] + ;; Apply the transforms that had the shape + transform (:transform shape) + new-content (cond-> new-content + (some? transform) + (gsp/transform-content (gmt/transform-in (gsc/center-shape shape) transform)))] - (-> shape - (assoc :type :path) - (assoc :content new-content) - (cond-> (= :image type) - (assoc :fill-image metadata)) - (d/without-keys dissoc-attrs))) - :else - ;; Do nothing if the shape is not of a correct type - shape)) + (-> shape + (assoc :type :path) + (assoc :content new-content) + (cond-> (= :image type) + (assoc :fill-image metadata)) + (d/without-keys dissoc-attrs))) + :else + ;; Do nothing if the shape is not of a correct type + shape))) diff --git a/frontend/src/app/main/data/workspace/booleans.cljs b/frontend/src/app/main/data/workspace/booleans.cljs index f1aa65474..4482d1da6 100644 --- a/frontend/src/app/main/data/workspace/booleans.cljs +++ b/frontend/src/app/main/data/workspace/booleans.cljs @@ -98,7 +98,9 @@ ptk/WatchEvent (watch [_ state _] (let [objects (wsh/lookup-page-objects state)] - (rx/of (dch/update-shapes [shape-id] #(group->bool % bool-type objects))))))) + (let [change-to-bool + (fn [shape] (group->bool shape bool-type objects))] + (rx/of (dch/update-shapes [shape-id] change-to-bool {:reg-objects? true}))))))) (defn bool-to-group [shape-id] @@ -106,7 +108,9 @@ ptk/WatchEvent (watch [_ state _] (let [objects (wsh/lookup-page-objects state)] - (rx/of (dch/update-shapes [shape-id] #(bool->group % objects))))))) + (let [change-to-group + (fn [shape] (bool->group shape objects))] + (rx/of (dch/update-shapes [shape-id] change-to-group {:reg-objects? true}))))))) (defn change-bool-type @@ -114,6 +118,6 @@ (ptk/reify ::change-bool-type ptk/WatchEvent (watch [_ _ _] - (rx/of (dch/update-shapes - [shape-id] - #(assoc % :bool-type bool-type)))))) + (let [change-type + (fn [shape] (assoc shape :bool-type bool-type))] + (rx/of (dch/update-shapes [shape-id] change-type {:reg-objects? true})))))) diff --git a/frontend/src/app/main/ui/shapes/bool.cljs b/frontend/src/app/main/ui/shapes/bool.cljs index c4c0fd994..8a3db6d19 100644 --- a/frontend/src/app/main/ui/shapes/bool.cljs +++ b/frontend/src/app/main/ui/shapes/bool.cljs @@ -27,7 +27,7 @@ bool-content (mf/use-memo - (mf/deps childs) + (mf/deps shape childs) (fn [] (let [childs (d/mapm #(gsh/transform-shape %2) childs)] (->> (:shapes shape) @@ -40,6 +40,3 @@ (assoc :type :path) (assoc :content bool-content)) :frame frame}]))) - - - diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 754523c73..aab5f7f08 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -92,6 +92,7 @@ (defn setup-hover-shapes [page-id move-stream objects transform selected ctrl? hover hover-ids hover-disabled? zoom] (let [;; We use ref so we don't recreate the stream on a change zoom-ref (mf/use-ref zoom) + ctrl-ref (mf/use-ref @ctrl?) transform-ref (mf/use-ref nil) selected-ref (mf/use-ref selected) hover-disabled-ref (mf/use-ref hover-disabled?) @@ -101,6 +102,7 @@ (mf/deps page-id) (fn [point] (let [zoom (mf/ref-val zoom-ref) + ctrl? (mf/ref-val ctrl-ref) rect (gsh/center->rect point (/ 5 zoom) (/ 5 zoom))] (if (mf/ref-val hover-disabled-ref) (rx/of nil) @@ -109,6 +111,7 @@ :page-id page-id :rect rect :include-frames? true + :clip-children? (not ctrl?) :reverse? true}))))) ;; we want the topmost shape to be selected first over-shapes-stream @@ -120,7 +123,6 @@ (rx/switch-map query-point))))] ;; Refresh the refs on a value change - (mf/use-effect (mf/deps transform) #(mf/set-ref-val! transform-ref transform)) @@ -129,6 +131,10 @@ (mf/deps zoom) #(mf/set-ref-val! zoom-ref zoom)) + (mf/use-effect + (mf/deps @ctrl?) + #(mf/set-ref-val! ctrl-ref @ctrl?)) + (mf/use-effect (mf/deps selected) #(mf/set-ref-val! selected-ref selected)) @@ -143,11 +149,15 @@ (fn [ids] (let [selected (mf/ref-val selected-ref) remove-id? (into #{} (mapcat #(cp/get-parents % objects)) selected) - remove-id? (if @ctrl? - (d/concat remove-id? - (->> ids - (filterv #(= :group (get-in objects [% :type]))))) - remove-id?) + + is-group? + (fn [id] + (contains? #{:group :bool} (get-in objects [id :type]))) + + remove-id? + (if @ctrl? + (d/concat remove-id? (filterv is-group? ids)) + remove-id?) ids (->> ids (filterv (comp not remove-id?)))] (reset! hover (get objects (first ids))) (reset! hover-ids ids)))))) diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index d93fcfaf4..47cb834b1 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -18,13 +18,13 @@ (defonce state (l/atom {})) (defn index-shape - [objects parents-index masks-index] + [objects parents-index clip-parents-index] (fn [index shape] (let [{:keys [x y width height]} (gsh/points->selrect (:points shape)) shape-bound #js {:x x :y y :width width :height height} - parents (get parents-index (:id shape)) - masks (get masks-index (:id shape)) + parents (get parents-index (:id shape)) + clip-parents (get clip-parents-index (:id shape)) frame (when (and (not= :frame (:type shape)) (not= (:frame-id shape) uuid/zero)) @@ -32,19 +32,22 @@ (qdt/insert index (:id shape) shape-bound - (assoc shape :frame frame :masks masks :parents parents))))) + (assoc shape + :frame frame + :clip-parents clip-parents + :parents parents))))) (defn- create-index [objects] - (let [shapes (-> objects (dissoc uuid/zero) (vals)) - parents-index (cp/generate-child-all-parents-index objects) - masks-index (cp/create-mask-index objects parents-index) + (let [shapes (-> objects (dissoc uuid/zero) (vals)) + parents-index (cp/generate-child-all-parents-index objects) + clip-parents-index (cp/create-clip-index objects parents-index) bounds #js {:x (int -0.5e7) :y (int -0.5e7) :width (int 1e7) :height (int 1e7)} - index (reduce (index-shape objects parents-index masks-index) + index (reduce (index-shape objects parents-index clip-parents-index) (qdt/create bounds) shapes) @@ -66,13 +69,13 @@ (set/union (set (keys old-objects)) (set (keys new-objects)))) - shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?))) - parents-index (cp/generate-child-all-parents-index new-objects shapes) - masks-index (cp/create-mask-index new-objects parents-index) + shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?))) + parents-index (cp/generate-child-all-parents-index new-objects shapes) + clip-parents-index (cp/create-clip-index new-objects parents-index) new-index (qdt/remove-all index changed-ids) - index (reduce (index-shape new-objects parents-index masks-index) + index (reduce (index-shape new-objects parents-index clip-parents-index) new-index shapes) @@ -84,7 +87,7 @@ (create-index new-objects))) (defn- query-index - [{index :index z-index :z-index} rect frame-id include-frames? full-frame? include-groups? reverse?] + [{index :index z-index :z-index} rect frame-id full-frame? include-frames? clip-children? reverse?] (let [result (-> (qdt/search index (clj->js rect)) (es6-iterator-seq)) @@ -96,7 +99,6 @@ (or (not frame-id) (= frame-id (:frame-id shape))) (case (:type shape) :frame include-frames? - :group include-groups? true) (or (not full-frame?) @@ -107,11 +109,9 @@ (fn [shape] (gsh/overlaps? shape rect)) - overlaps-masks? - (fn [masks] - (->> masks - (some (comp not overlaps?)) - not)) + overlaps-parent? + (fn [clip-parents] + (->> clip-parents (some (comp not overlaps?)) not)) add-z-index (fn [{:keys [id frame-id] :as shape}] @@ -125,7 +125,9 @@ (filter match-criteria?) (filter overlaps?) (filter (comp overlaps? :frame)) - (filter (comp overlaps-masks? :masks)) + (filter (if clip-children? + (comp overlaps-parent? :clip-parents) + (constantly true))) (map add-z-index)) result) @@ -155,10 +157,10 @@ nil) (defmethod impl/handler :selection/query - [{:keys [page-id rect frame-id include-frames? full-frame? include-groups? reverse?] - :or {include-groups? true reverse? false include-frames? false full-frame? false} :as message}] + [{:keys [page-id rect frame-id reverse? full-frame? include-frames? clip-children?] + :or {reverse? false full-frame? false include-frames? false include-booleans? true include-groups? true} :as message}] (when-let [index (get @state page-id)] - (query-index index rect frame-id include-frames? full-frame? include-groups? reverse?))) + (query-index index rect frame-id full-frame? include-frames? clip-children? reverse?))) (defmethod impl/handler :selection/query-z-index [{:keys [page-id objects ids]}]