mirror of
https://github.com/penpot/penpot.git
synced 2025-04-01 09:31:26 -05:00
commit
ae6ea7744e
8 changed files with 145 additions and 137 deletions
|
@ -82,24 +82,12 @@
|
|||
(update :height (comp inc inc))))))
|
||||
|
||||
(defn selrect->areas [bounds selrect]
|
||||
(let [make-selrect
|
||||
(fn [x1 y1 x2 y2]
|
||||
(let [x1 (min x1 x2)
|
||||
x2 (max x1 x2)
|
||||
y1 (min y1 y2)
|
||||
y2 (max y1 y2)]
|
||||
{:x1 x1 :y1 y1
|
||||
:x2 x2 :y2 y2
|
||||
:x x1 :y y1
|
||||
:width (- x2 x1)
|
||||
:height (- y2 y1)
|
||||
:type :rect}))
|
||||
{bound-x1 :x1 bound-x2 :x2 bound-y1 :y1 bound-y2 :y2} bounds
|
||||
(let [{bound-x1 :x1 bound-x2 :x2 bound-y1 :y1 bound-y2 :y2} bounds
|
||||
{sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2} selrect]
|
||||
{:left (make-selrect bound-x1 sr-y1 sr-x1 sr-y2)
|
||||
:top (make-selrect sr-x1 bound-y1 sr-x2 sr-y1)
|
||||
:right (make-selrect sr-x2 sr-y1 bound-x2 sr-y2)
|
||||
:bottom (make-selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
|
||||
{:left (gpr/corners->selrect bound-x1 sr-y1 sr-x1 sr-y2)
|
||||
:top (gpr/corners->selrect sr-x1 bound-y1 sr-x2 sr-y1)
|
||||
:right (gpr/corners->selrect sr-x2 sr-y1 bound-x2 sr-y2)
|
||||
:bottom (gpr/corners->selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
|
||||
|
||||
(defn distance-selrect [selrect other]
|
||||
(let [{:keys [x1 y1]} other
|
||||
|
@ -161,6 +149,7 @@
|
|||
(dm/export gpr/contains-selrect?)
|
||||
(dm/export gpr/contains-point?)
|
||||
(dm/export gpr/close-selrect?)
|
||||
(dm/export gpr/clip-selrect)
|
||||
|
||||
(dm/export gtr/move)
|
||||
(dm/export gtr/absolute-move)
|
||||
|
|
|
@ -35,9 +35,8 @@
|
|||
ratio-width (/ target-width curr-width)
|
||||
ratio-height (/ target-height curr-height)
|
||||
scalev (gpt/point ratio-width ratio-height)]
|
||||
|
||||
(-> modifiers
|
||||
(ctm/resize scalev origin transform transform-inverse))))
|
||||
(ctm/resize scalev origin transform transform-inverse {:precise? true}))))
|
||||
|
||||
(defn position-pixel-precision
|
||||
[modifiers _ points]
|
||||
|
|
|
@ -212,9 +212,13 @@
|
|||
(<= (:y2 sr2) (:y2 sr1))))
|
||||
|
||||
(defn corners->selrect
|
||||
[p1 p2]
|
||||
(let [xp1 (:x p1)
|
||||
xp2 (:x p2)
|
||||
yp1 (:y p1)
|
||||
yp2 (:y p2)]
|
||||
(make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2)))))
|
||||
([p1 p2]
|
||||
(corners->selrect (:x p1) (:y p1) (:x p2) (:y p2)))
|
||||
([xp1 yp1 xp2 yp2]
|
||||
(make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2)))))
|
||||
|
||||
(defn clip-selrect
|
||||
[{:keys [x1 y1 x2 y2] :as sr} bounds]
|
||||
(when (some? sr)
|
||||
(let [{bx1 :x1 by1 :y1 bx2 :x2 by2 :y2} (rect->selrect bounds)]
|
||||
(corners->selrect (max bx1 x1) (max by1 y1) (min bx2 x2) (min by2 y2)))))
|
||||
|
|
|
@ -177,17 +177,19 @@
|
|||
|
||||
(defn- maybe-add-resize
|
||||
"Check the last operation to check if we can stack it over the last one"
|
||||
[operations op]
|
||||
([operations op]
|
||||
(maybe-add-resize operations op nil))
|
||||
|
||||
(if (c/empty? operations)
|
||||
[op]
|
||||
(let [head (peek operations)]
|
||||
(if (mergeable-resize? head op)
|
||||
(let [item (merge-resize head op)]
|
||||
(cond-> (pop operations)
|
||||
(resize-vec? (dm/get-prop item :vector))
|
||||
(conj item)))
|
||||
(conj operations op)))))
|
||||
([operations op {:keys [precise?]}]
|
||||
(if (c/empty? operations)
|
||||
[op]
|
||||
(let [head (peek operations)]
|
||||
(if (mergeable-resize? head op)
|
||||
(let [item (merge-resize head op)]
|
||||
(cond-> (pop operations)
|
||||
(or precise? (resize-vec? (dm/get-prop item :vector)))
|
||||
(conj item)))
|
||||
(conj operations op))))))
|
||||
|
||||
(defn valid-vector?
|
||||
[vector]
|
||||
|
@ -259,12 +261,16 @@
|
|||
(update :geometry-child maybe-add-resize (resize-op order vector origin)))))
|
||||
|
||||
([modifiers vector origin transform transform-inverse]
|
||||
(resize modifiers vector origin transform transform-inverse nil))
|
||||
|
||||
;; `precise?` works so we don't remove almost empty resizes. This will be used in the pixel-precision
|
||||
([modifiers vector origin transform transform-inverse {:keys [precise?]}]
|
||||
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
|
||||
(let [modifiers (or modifiers (empty))
|
||||
order (inc (dm/get-prop modifiers :last-order))
|
||||
modifiers (assoc modifiers :last-order order)]
|
||||
(cond-> modifiers
|
||||
(resize-vec? vector)
|
||||
(or precise? (resize-vec? vector))
|
||||
(update :geometry-child maybe-add-resize (resize-op order vector origin transform transform-inverse))))))
|
||||
|
||||
(defn rotation
|
||||
|
|
|
@ -93,29 +93,32 @@
|
|||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/sample-when
|
||||
(->> (rx/merge
|
||||
(rx/interval 5000)
|
||||
(rx/filter #(= ::force-persist %) stream)
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/debounce 2000)))
|
||||
;; Not sample while saving so there are no race conditions
|
||||
(rx/filter #(not @saving?))))
|
||||
(rx/merge
|
||||
(rx/filter #(= ::force-persist %) stream)
|
||||
(->> (rx/merge
|
||||
(rx/interval 5000)
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/debounce 2000)))
|
||||
;; Not sample while saving so there are no race conditions
|
||||
(rx/filter #(not @saving?)))))
|
||||
(rx/tap #(reset! commits []))
|
||||
(rx/tap on-saving)
|
||||
(rx/mapcat (fn [changes]
|
||||
;; NOTE: this is needed for don't start the
|
||||
;; next persistence before this one is
|
||||
;; finished.
|
||||
(rx/merge
|
||||
(->> (rx/of (persist-changes file-id changes commits))
|
||||
(rx/observe-on :async))
|
||||
(->> stream
|
||||
;; We wait for every change to be persisted
|
||||
(rx/filter (ptk/type? ::shapes-changes-persisted-finished))
|
||||
(rx/take 1)
|
||||
(rx/tap on-saved)
|
||||
(rx/ignore)))))
|
||||
(if-let [file-revn (dm/get-in @st/state [:workspace-file :revn])]
|
||||
(rx/merge
|
||||
(->> (rx/of (persist-changes file-id file-revn changes commits))
|
||||
(rx/observe-on :async))
|
||||
(->> stream
|
||||
;; We wait for every change to be persisted
|
||||
(rx/filter (ptk/type? ::shapes-changes-persisted-finished))
|
||||
(rx/take 1)
|
||||
(rx/tap on-saved)
|
||||
(rx/ignore)))
|
||||
(rx/empty))))
|
||||
(rx/take-until (rx/delay 100 stoper))
|
||||
(rx/finalize (fn []
|
||||
(log/debug :hint "finalize persistence: save loop"))))
|
||||
|
@ -132,7 +135,7 @@
|
|||
(log/debug :hint "finalize persistence: synchronous save loop")))))))))
|
||||
|
||||
(defn persist-changes
|
||||
[file-id changes pending-commits]
|
||||
[file-id file-revn changes pending-commits]
|
||||
(log/debug :hint "persist changes" :changes (count changes))
|
||||
(us/verify ::us/uuid file-id)
|
||||
(ptk/reify ::persist-changes
|
||||
|
@ -146,48 +149,46 @@
|
|||
(features/active-feature? state :components-v2)
|
||||
(conj "components/v2"))
|
||||
sid (:session-id state)
|
||||
file (get state :workspace-file)
|
||||
params {:id (:id file)
|
||||
:revn (:revn file)
|
||||
params {:id file-id
|
||||
:revn file-revn
|
||||
:session-id sid
|
||||
:changes-with-metadata (into [] changes)
|
||||
:features features}]
|
||||
|
||||
(when (= file-id (:id params))
|
||||
(->> (rp/cmd! :update-file params)
|
||||
(rx/mapcat (fn [lagged]
|
||||
(log/debug :hint "changes persisted" :lagged (count lagged))
|
||||
(let [frame-updates
|
||||
(-> (group-by :page-id changes)
|
||||
(update-vals #(into #{} (mapcat :frames) %)))
|
||||
(->> (rp/cmd! :update-file params)
|
||||
(rx/mapcat (fn [lagged]
|
||||
(log/debug :hint "changes persisted" :lagged (count lagged))
|
||||
(let [frame-updates
|
||||
(-> (group-by :page-id changes)
|
||||
(update-vals #(into #{} (mapcat :frames) %)))
|
||||
|
||||
commits
|
||||
(->> @pending-commits
|
||||
(map #(assoc % :revn (:revn file))))]
|
||||
commits
|
||||
(->> @pending-commits
|
||||
(map #(assoc % :revn file-revn)))]
|
||||
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> (rx/from frame-updates)
|
||||
(rx/mapcat (fn [[page-id frames]]
|
||||
(->> frames (map #(vector page-id %)))))
|
||||
(rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail (:id file) page-id frame-id))))
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> (rx/from frame-updates)
|
||||
(rx/mapcat (fn [[page-id frames]]
|
||||
(->> frames (map #(vector page-id %)))))
|
||||
(rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail file-id page-id frame-id))))
|
||||
|
||||
(->> (rx/from (concat lagged commits))
|
||||
(rx/merge-map
|
||||
(fn [{:keys [changes] :as entry}]
|
||||
(rx/merge
|
||||
(rx/from
|
||||
(for [[page-id changes] (group-by :page-id changes)]
|
||||
(dch/update-indices page-id changes)))
|
||||
(rx/of (shapes-changes-persisted file-id entry)))))))
|
||||
(->> (rx/from (concat lagged commits))
|
||||
(rx/merge-map
|
||||
(fn [{:keys [changes] :as entry}]
|
||||
(rx/merge
|
||||
(rx/from
|
||||
(for [[page-id changes] (group-by :page-id changes)]
|
||||
(dch/update-indices page-id changes)))
|
||||
(rx/of (shapes-changes-persisted file-id entry)))))))
|
||||
|
||||
(rx/of (shapes-changes-persisted-finished))))))
|
||||
(rx/catch (fn [cause]
|
||||
(rx/concat
|
||||
(if (= :authentication (:type cause))
|
||||
(rx/empty)
|
||||
(rx/of (rt/assign-exception cause)))
|
||||
(rx/throw cause))))))))))
|
||||
(rx/of (shapes-changes-persisted-finished))))))
|
||||
(rx/catch (fn [cause]
|
||||
(rx/concat
|
||||
(if (= :authentication (:type cause))
|
||||
(rx/empty)
|
||||
(rx/of (rt/assign-exception cause)))
|
||||
(rx/throw cause)))))))))
|
||||
|
||||
;; Event to be thrown after the changes have been persisted
|
||||
(defn shapes-changes-persisted-finished
|
||||
|
|
|
@ -144,17 +144,6 @@
|
|||
dist-lt (fn [other] (sr-distance coord (:selrect other) selrect))
|
||||
dist-gt (fn [other] (sr-distance coord selrect (:selrect other)))
|
||||
|
||||
;; Calculates the snap distance when in the middle of two shapes
|
||||
between-snap
|
||||
(fn [[sh-lt sh-gt]]
|
||||
;; To calculate the middle snap.
|
||||
;; Given x, the distance to a left shape and y to a right shape
|
||||
;; x - v = y + v => v = (x - y)/2
|
||||
;; v will be the vector that we need to move the shape so it "snaps"
|
||||
;; in the middle
|
||||
(/ (- (dist-gt sh-gt)
|
||||
(dist-lt sh-lt)) 2))
|
||||
|
||||
;; Calculates the distance between all the shapes given as argument
|
||||
inner-distance
|
||||
(fn [selrects]
|
||||
|
@ -164,14 +153,6 @@
|
|||
#(overlap? coord %1 %2)
|
||||
#{})))
|
||||
|
||||
best-snap
|
||||
(fn [acc val]
|
||||
;; Using a number is faster than accessing the variable.
|
||||
;; Keep up to date with `snap-distance-accuracy`
|
||||
(if (and (<= val snap-distance-accuracy) (>= val (- snap-distance-accuracy)))
|
||||
(min acc val)
|
||||
acc))
|
||||
|
||||
;; Distance between the elements in an area, these are the snap
|
||||
;; candidates to either side
|
||||
lt-cand (inner-distance (mapv :selrect shapes-lt))
|
||||
|
@ -182,17 +163,46 @@
|
|||
lt-dist (into #{} (map dist-lt) shapes-lt)
|
||||
gt-dist (into #{} (map dist-gt) shapes-gt)
|
||||
|
||||
;; Calculate the snaps, we need to reverse depending on area
|
||||
lt-snap (d/join lt-cand lt-dist -)
|
||||
gt-snap (d/join gt-dist gt-cand -)
|
||||
get-side-snaps
|
||||
(fn [candidates distances]
|
||||
;; We add to the range tree the distrances between elements
|
||||
;; then, for each distance from the selection we query the tree
|
||||
;; to find a snap
|
||||
(let [range-tree (rt/make-tree)
|
||||
range-tree
|
||||
(->> candidates
|
||||
(reduce #(rt/insert %1 %2 %2) range-tree))]
|
||||
(->> distances
|
||||
(mapcat
|
||||
(fn [cd]
|
||||
(->> (rt/range-query
|
||||
range-tree
|
||||
(- cd snap-distance-accuracy)
|
||||
(+ cd snap-distance-accuracy))
|
||||
(map #(- (first %) cd ))))))))
|
||||
|
||||
;; Calculate snap-between
|
||||
between-snap (->> (d/join shapes-lt shapes-gt)
|
||||
(map between-snap))
|
||||
get-middle-snaps
|
||||
(fn [lt-dist gt-dist]
|
||||
(let [range-tree (rt/make-tree)
|
||||
range-tree (->> lt-dist
|
||||
(reduce #(rt/insert %1 %2 %2) range-tree))]
|
||||
(->> gt-dist
|
||||
(mapcat (fn [cd]
|
||||
(->> (rt/range-query
|
||||
range-tree
|
||||
(- cd (* snap-distance-accuracy 2))
|
||||
(+ cd (* snap-distance-accuracy 2)))
|
||||
(map #(/ (- cd (first %)) 2))))))))
|
||||
|
||||
;; Calculate the snaps, we need to reverse depending on area
|
||||
lt-snap (get-side-snaps lt-cand lt-dist)
|
||||
gt-snap (get-side-snaps gt-dist gt-cand)
|
||||
md-snap (get-middle-snaps lt-dist gt-dist)
|
||||
|
||||
;; Search the minimum snap
|
||||
snap-list (d/concat-vec lt-snap gt-snap between-snap)
|
||||
min-snap (reduce best-snap ##Inf snap-list)]
|
||||
snap-list (d/concat-vec lt-snap gt-snap md-snap)
|
||||
|
||||
min-snap (reduce min ##Inf snap-list)]
|
||||
|
||||
(if (d/num? min-snap) [0 min-snap] nil)))
|
||||
|
||||
|
@ -202,14 +212,14 @@
|
|||
(calculate-snap coord selrect shapes-lt shapes-gt zoom)))))
|
||||
|
||||
(defn select-shapes-area
|
||||
[page-id shapes objects area-selrect]
|
||||
[page-id frame-id selected objects area]
|
||||
(->> (uw/ask! {:cmd :selection/query
|
||||
:page-id page-id
|
||||
:frame-id (->> shapes first :frame-id)
|
||||
:frame-id frame-id
|
||||
:include-frames? true
|
||||
:rect area-selrect})
|
||||
:rect area})
|
||||
(rx/map #(cph/clean-loops objects %))
|
||||
(rx/map #(set/difference % (into #{} (map :id shapes))))
|
||||
(rx/map #(set/difference % selected))
|
||||
(rx/map #(map (d/getf objects) %))))
|
||||
|
||||
(defn closest-distance-snap
|
||||
|
@ -220,9 +230,14 @@
|
|||
(->> (rx/of (vector frame selrect))
|
||||
(rx/merge-map
|
||||
(fn [[frame selrect]]
|
||||
(let [areas (->> (gsh/selrect->areas (or (:selrect frame)
|
||||
(gsh/rect->selrect @refs/vbox)) selrect)
|
||||
(d/mapm #(select-shapes-area page-id shapes objects %2)))
|
||||
(let [vbox (gsh/rect->selrect @refs/vbox)
|
||||
frame-id (->> shapes first :frame-id)
|
||||
selected (into #{} (map :id shapes))
|
||||
areas (->> (gsh/selrect->areas
|
||||
(or (gsh/clip-selrect (:selrect frame) vbox)
|
||||
vbox)
|
||||
selrect)
|
||||
(d/mapm #(select-shapes-area page-id frame-id selected objects %2)))
|
||||
snap-x (search-snap-distance selrect :x (:left areas) (:right areas) zoom)
|
||||
snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas) zoom)]
|
||||
(rx/combine-latest snap-x snap-y))))
|
||||
|
|
|
@ -9,11 +9,10 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.snap :as ams]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.worker :as uw]
|
||||
[beicon.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -152,7 +151,7 @@
|
|||
check-in-set
|
||||
(fn [value number-set]
|
||||
(->> number-set
|
||||
(some #(<= (mth/abs (- value %)) 0.01))))
|
||||
(some #(<= (mth/abs (- value %)) 1.5))))
|
||||
|
||||
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter)
|
||||
|
||||
|
@ -218,21 +217,16 @@
|
|||
(fn [[selrect selected frame]]
|
||||
(let [lt-side (if (= coord :x) :left :top)
|
||||
gt-side (if (= coord :x) :right :bottom)
|
||||
container-selrec (or (:selrect frame)
|
||||
(gsh/rect->selrect @refs/vbox))
|
||||
areas (gsh/selrect->areas container-selrec selrect)
|
||||
|
||||
vbox (gsh/rect->selrect @refs/vbox)
|
||||
areas (gsh/selrect->areas
|
||||
(or (gsh/clip-selrect (:selrect frame) vbox) vbox)
|
||||
selrect)
|
||||
|
||||
query-side (fn [side]
|
||||
(let [rect (get areas side)]
|
||||
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
||||
(->> (uw/ask! {:cmd :selection/query
|
||||
:page-id page-id
|
||||
:frame-id (:id frame)
|
||||
:include-frames? true
|
||||
:rect rect})
|
||||
(rx/map #(cph/clean-loops @refs/workspace-page-objects %))
|
||||
(rx/map #(set/difference % selected))
|
||||
(rx/map #(->> % (map (partial get @refs/workspace-page-objects)))))
|
||||
(ams/select-shapes-area page-id (:id frame) selected @refs/workspace-page-objects rect)
|
||||
(rx/of nil))))]
|
||||
(rx/combine-latest (query-side lt-side)
|
||||
(query-side gt-side))))
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
(->> (http/send! request)
|
||||
(rx/map http/conditional-decode-transit)
|
||||
(rx/mapcat handle-response)
|
||||
(rx/catch body-too-large? (constantly nil))
|
||||
(rx/catch body-too-large? (constantly (rx/of nil)))
|
||||
(rx/map (constantly params)))))
|
||||
|
||||
(defmethod impl/handler :thumbnails/generate
|
||||
|
|
Loading…
Add table
Reference in a new issue