mirror of
https://github.com/penpot/penpot.git
synced 2025-05-25 12:06:11 +02:00
✨ Clip boolean selrects
This commit is contained in:
parent
c56f024a86
commit
56e2db22eb
8 changed files with 120 additions and 91 deletions
|
@ -73,7 +73,7 @@
|
||||||
(d/export indices/update-z-index)
|
(d/export indices/update-z-index)
|
||||||
(d/export indices/generate-child-all-parents-index)
|
(d/export indices/generate-child-all-parents-index)
|
||||||
(d/export indices/generate-child-parent-index)
|
(d/export indices/generate-child-parent-index)
|
||||||
(d/export indices/create-mask-index)
|
(d/export indices/create-clip-index)
|
||||||
|
|
||||||
;; Process changes
|
;; Process changes
|
||||||
(d/export changes/process-changes)
|
(d/export changes/process-changes)
|
||||||
|
|
|
@ -95,16 +95,24 @@
|
||||||
(map #(vector (:id %) (shape->parents %)))
|
(map #(vector (:id %) (shape->parents %)))
|
||||||
(into {})))))
|
(into {})))))
|
||||||
|
|
||||||
(defn create-mask-index
|
(defn create-clip-index
|
||||||
"Retrieves the mask information for an object"
|
"Retrieves the mask information for an object"
|
||||||
[objects parents-index]
|
[objects parents-index]
|
||||||
(let [retrieve-masks
|
(let [retrieve-clips
|
||||||
(fn [_ parents]
|
(fn [_ parents]
|
||||||
;; TODO: use transducers?
|
(let [lookup-object (fn [id] (get objects id))
|
||||||
(->> parents
|
get-clip-parents
|
||||||
(map #(get objects %))
|
(fn [shape]
|
||||||
(filter #(:masked-group? %))
|
(cond-> []
|
||||||
;; Retrieve the masking element
|
(:masked-group? shape)
|
||||||
(mapv #(get objects (->> % :shapes first)))))]
|
(conj (get objects (->> shape :shapes first)))
|
||||||
|
|
||||||
|
(= :bool (:type shape))
|
||||||
|
(conj shape)))]
|
||||||
|
|
||||||
|
(into []
|
||||||
|
(comp (map lookup-object)
|
||||||
|
(mapcat get-clip-parents))
|
||||||
|
parents)))]
|
||||||
(->> parents-index
|
(->> parents-index
|
||||||
(d/mapm retrieve-masks))))
|
(d/mapm retrieve-clips))))
|
||||||
|
|
|
@ -12,6 +12,23 @@
|
||||||
[app.common.path.commands :as upc]
|
[app.common.path.commands :as upc]
|
||||||
[app.common.path.subpaths :as ups]))
|
[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
|
(defn- split-command
|
||||||
[cmd values]
|
[cmd values]
|
||||||
(case (:command cmd)
|
(case (:command cmd)
|
||||||
|
@ -142,7 +159,12 @@
|
||||||
(d/concat
|
(d/concat
|
||||||
[]
|
[]
|
||||||
(->> content-a-split (filter #(not (contains-segment? % content-b))))
|
(->> 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]
|
(defn create-intersection [content-a content-a-split content-b content-b-split]
|
||||||
;; Pick all segments in content-a that are inside content-b
|
;; Pick all segments in content-a that are inside content-b
|
||||||
|
@ -152,22 +174,6 @@
|
||||||
(->> content-a-split (filter #(contains-segment? % content-b)))
|
(->> content-a-split (filter #(contains-segment? % content-b)))
|
||||||
(->> content-b-split (filter #(contains-segment? % content-a)))))
|
(->> 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]
|
(defn create-exclusion [content-a content-b]
|
||||||
;; Pick all segments but reverse content-b (so it makes an exclusion)
|
;; Pick all segments but reverse content-b (so it makes an exclusion)
|
||||||
|
|
|
@ -180,7 +180,9 @@
|
||||||
|
|
||||||
(defn convert-to-path
|
(defn convert-to-path
|
||||||
"Transforms the given shape to a path"
|
"Transforms the given shape to a path"
|
||||||
[{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as 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))
|
(assert (map? objects))
|
||||||
(cond
|
(cond
|
||||||
(= (:type shape) :group)
|
(= (:type shape) :group)
|
||||||
|
@ -209,4 +211,4 @@
|
||||||
(d/without-keys dissoc-attrs)))
|
(d/without-keys dissoc-attrs)))
|
||||||
:else
|
:else
|
||||||
;; Do nothing if the shape is not of a correct type
|
;; Do nothing if the shape is not of a correct type
|
||||||
shape))
|
shape)))
|
||||||
|
|
|
@ -98,7 +98,9 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [objects (wsh/lookup-page-objects 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
|
(defn bool-to-group
|
||||||
[shape-id]
|
[shape-id]
|
||||||
|
@ -106,7 +108,9 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [objects (wsh/lookup-page-objects 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
|
(defn change-bool-type
|
||||||
|
@ -114,6 +118,6 @@
|
||||||
(ptk/reify ::change-bool-type
|
(ptk/reify ::change-bool-type
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(rx/of (dch/update-shapes
|
(let [change-type
|
||||||
[shape-id]
|
(fn [shape] (assoc shape :bool-type bool-type))]
|
||||||
#(assoc % :bool-type bool-type))))))
|
(rx/of (dch/update-shapes [shape-id] change-type {:reg-objects? true}))))))
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
bool-content
|
bool-content
|
||||||
(mf/use-memo
|
(mf/use-memo
|
||||||
(mf/deps childs)
|
(mf/deps shape childs)
|
||||||
(fn []
|
(fn []
|
||||||
(let [childs (d/mapm #(gsh/transform-shape %2) childs)]
|
(let [childs (d/mapm #(gsh/transform-shape %2) childs)]
|
||||||
(->> (:shapes shape)
|
(->> (:shapes shape)
|
||||||
|
@ -40,6 +40,3 @@
|
||||||
(assoc :type :path)
|
(assoc :type :path)
|
||||||
(assoc :content bool-content))
|
(assoc :content bool-content))
|
||||||
:frame frame}])))
|
:frame frame}])))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@
|
||||||
(defn setup-hover-shapes [page-id move-stream objects transform selected ctrl? hover hover-ids hover-disabled? zoom]
|
(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
|
(let [;; We use ref so we don't recreate the stream on a change
|
||||||
zoom-ref (mf/use-ref zoom)
|
zoom-ref (mf/use-ref zoom)
|
||||||
|
ctrl-ref (mf/use-ref @ctrl?)
|
||||||
transform-ref (mf/use-ref nil)
|
transform-ref (mf/use-ref nil)
|
||||||
selected-ref (mf/use-ref selected)
|
selected-ref (mf/use-ref selected)
|
||||||
hover-disabled-ref (mf/use-ref hover-disabled?)
|
hover-disabled-ref (mf/use-ref hover-disabled?)
|
||||||
|
@ -101,6 +102,7 @@
|
||||||
(mf/deps page-id)
|
(mf/deps page-id)
|
||||||
(fn [point]
|
(fn [point]
|
||||||
(let [zoom (mf/ref-val zoom-ref)
|
(let [zoom (mf/ref-val zoom-ref)
|
||||||
|
ctrl? (mf/ref-val ctrl-ref)
|
||||||
rect (gsh/center->rect point (/ 5 zoom) (/ 5 zoom))]
|
rect (gsh/center->rect point (/ 5 zoom) (/ 5 zoom))]
|
||||||
(if (mf/ref-val hover-disabled-ref)
|
(if (mf/ref-val hover-disabled-ref)
|
||||||
(rx/of nil)
|
(rx/of nil)
|
||||||
|
@ -109,6 +111,7 @@
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:rect rect
|
:rect rect
|
||||||
:include-frames? true
|
:include-frames? true
|
||||||
|
:clip-children? (not ctrl?)
|
||||||
:reverse? true}))))) ;; we want the topmost shape to be selected first
|
:reverse? true}))))) ;; we want the topmost shape to be selected first
|
||||||
|
|
||||||
over-shapes-stream
|
over-shapes-stream
|
||||||
|
@ -120,7 +123,6 @@
|
||||||
(rx/switch-map query-point))))]
|
(rx/switch-map query-point))))]
|
||||||
|
|
||||||
;; Refresh the refs on a value change
|
;; Refresh the refs on a value change
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps transform)
|
(mf/deps transform)
|
||||||
#(mf/set-ref-val! transform-ref transform))
|
#(mf/set-ref-val! transform-ref transform))
|
||||||
|
@ -129,6 +131,10 @@
|
||||||
(mf/deps zoom)
|
(mf/deps zoom)
|
||||||
#(mf/set-ref-val! zoom-ref 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/use-effect
|
||||||
(mf/deps selected)
|
(mf/deps selected)
|
||||||
#(mf/set-ref-val! selected-ref selected))
|
#(mf/set-ref-val! selected-ref selected))
|
||||||
|
@ -143,10 +149,14 @@
|
||||||
(fn [ids]
|
(fn [ids]
|
||||||
(let [selected (mf/ref-val selected-ref)
|
(let [selected (mf/ref-val selected-ref)
|
||||||
remove-id? (into #{} (mapcat #(cp/get-parents % objects)) selected)
|
remove-id? (into #{} (mapcat #(cp/get-parents % objects)) selected)
|
||||||
remove-id? (if @ctrl?
|
|
||||||
(d/concat remove-id?
|
is-group?
|
||||||
(->> ids
|
(fn [id]
|
||||||
(filterv #(= :group (get-in objects [% :type])))))
|
(contains? #{:group :bool} (get-in objects [id :type])))
|
||||||
|
|
||||||
|
remove-id?
|
||||||
|
(if @ctrl?
|
||||||
|
(d/concat remove-id? (filterv is-group? ids))
|
||||||
remove-id?)
|
remove-id?)
|
||||||
ids (->> ids (filterv (comp not remove-id?)))]
|
ids (->> ids (filterv (comp not remove-id?)))]
|
||||||
(reset! hover (get objects (first ids)))
|
(reset! hover (get objects (first ids)))
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
(defonce state (l/atom {}))
|
(defonce state (l/atom {}))
|
||||||
|
|
||||||
(defn index-shape
|
(defn index-shape
|
||||||
[objects parents-index masks-index]
|
[objects parents-index clip-parents-index]
|
||||||
(fn [index shape]
|
(fn [index shape]
|
||||||
(let [{:keys [x y width height]} (gsh/points->selrect (:points shape))
|
(let [{:keys [x y width height]} (gsh/points->selrect (:points shape))
|
||||||
shape-bound #js {:x x :y y :width width :height height}
|
shape-bound #js {:x x :y y :width width :height height}
|
||||||
|
|
||||||
parents (get parents-index (:id shape))
|
parents (get parents-index (:id shape))
|
||||||
masks (get masks-index (:id shape))
|
clip-parents (get clip-parents-index (:id shape))
|
||||||
|
|
||||||
frame (when (and (not= :frame (:type shape))
|
frame (when (and (not= :frame (:type shape))
|
||||||
(not= (:frame-id shape) uuid/zero))
|
(not= (:frame-id shape) uuid/zero))
|
||||||
|
@ -32,19 +32,22 @@
|
||||||
(qdt/insert index
|
(qdt/insert index
|
||||||
(:id shape)
|
(:id shape)
|
||||||
shape-bound
|
shape-bound
|
||||||
(assoc shape :frame frame :masks masks :parents parents)))))
|
(assoc shape
|
||||||
|
:frame frame
|
||||||
|
:clip-parents clip-parents
|
||||||
|
:parents parents)))))
|
||||||
|
|
||||||
(defn- create-index
|
(defn- create-index
|
||||||
[objects]
|
[objects]
|
||||||
(let [shapes (-> objects (dissoc uuid/zero) (vals))
|
(let [shapes (-> objects (dissoc uuid/zero) (vals))
|
||||||
parents-index (cp/generate-child-all-parents-index objects)
|
parents-index (cp/generate-child-all-parents-index objects)
|
||||||
masks-index (cp/create-mask-index objects parents-index)
|
clip-parents-index (cp/create-clip-index objects parents-index)
|
||||||
bounds #js {:x (int -0.5e7)
|
bounds #js {:x (int -0.5e7)
|
||||||
:y (int -0.5e7)
|
:y (int -0.5e7)
|
||||||
:width (int 1e7)
|
:width (int 1e7)
|
||||||
:height (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)
|
(qdt/create bounds)
|
||||||
shapes)
|
shapes)
|
||||||
|
|
||||||
|
@ -68,11 +71,11 @@
|
||||||
|
|
||||||
shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?)))
|
shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?)))
|
||||||
parents-index (cp/generate-child-all-parents-index new-objects shapes)
|
parents-index (cp/generate-child-all-parents-index new-objects shapes)
|
||||||
masks-index (cp/create-mask-index new-objects parents-index)
|
clip-parents-index (cp/create-clip-index new-objects parents-index)
|
||||||
|
|
||||||
new-index (qdt/remove-all index changed-ids)
|
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
|
new-index
|
||||||
shapes)
|
shapes)
|
||||||
|
|
||||||
|
@ -84,7 +87,7 @@
|
||||||
(create-index new-objects)))
|
(create-index new-objects)))
|
||||||
|
|
||||||
(defn- query-index
|
(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))
|
(let [result (-> (qdt/search index (clj->js rect))
|
||||||
(es6-iterator-seq))
|
(es6-iterator-seq))
|
||||||
|
|
||||||
|
@ -96,7 +99,6 @@
|
||||||
(or (not frame-id) (= frame-id (:frame-id shape)))
|
(or (not frame-id) (= frame-id (:frame-id shape)))
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:frame include-frames?
|
:frame include-frames?
|
||||||
:group include-groups?
|
|
||||||
true)
|
true)
|
||||||
|
|
||||||
(or (not full-frame?)
|
(or (not full-frame?)
|
||||||
|
@ -107,11 +109,9 @@
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(gsh/overlaps? shape rect))
|
(gsh/overlaps? shape rect))
|
||||||
|
|
||||||
overlaps-masks?
|
overlaps-parent?
|
||||||
(fn [masks]
|
(fn [clip-parents]
|
||||||
(->> masks
|
(->> clip-parents (some (comp not overlaps?)) not))
|
||||||
(some (comp not overlaps?))
|
|
||||||
not))
|
|
||||||
|
|
||||||
add-z-index
|
add-z-index
|
||||||
(fn [{:keys [id frame-id] :as shape}]
|
(fn [{:keys [id frame-id] :as shape}]
|
||||||
|
@ -125,7 +125,9 @@
|
||||||
(filter match-criteria?)
|
(filter match-criteria?)
|
||||||
(filter overlaps?)
|
(filter overlaps?)
|
||||||
(filter (comp overlaps? :frame))
|
(filter (comp overlaps? :frame))
|
||||||
(filter (comp overlaps-masks? :masks))
|
(filter (if clip-children?
|
||||||
|
(comp overlaps-parent? :clip-parents)
|
||||||
|
(constantly true)))
|
||||||
(map add-z-index))
|
(map add-z-index))
|
||||||
result)
|
result)
|
||||||
|
|
||||||
|
@ -155,10 +157,10 @@
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(defmethod impl/handler :selection/query
|
(defmethod impl/handler :selection/query
|
||||||
[{:keys [page-id rect frame-id include-frames? full-frame? include-groups? reverse?]
|
[{:keys [page-id rect frame-id reverse? full-frame? include-frames? clip-children?]
|
||||||
:or {include-groups? true reverse? false include-frames? false full-frame? false} :as message}]
|
:or {reverse? false full-frame? false include-frames? false include-booleans? true include-groups? true} :as message}]
|
||||||
(when-let [index (get @state page-id)]
|
(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
|
(defmethod impl/handler :selection/query-z-index
|
||||||
[{:keys [page-id objects ids]}]
|
[{:keys [page-id objects ids]}]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue