Merge pull request #2738 from penpot/alotor-polishing

Polishing
This commit is contained in:
Alejandro 2023-01-09 12:35:43 +01:00 committed by GitHub
commit ae6ea7744e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 145 additions and 137 deletions

View file

@ -82,24 +82,12 @@
(update :height (comp inc inc)))))) (update :height (comp inc inc))))))
(defn selrect->areas [bounds selrect] (defn selrect->areas [bounds selrect]
(let [make-selrect (let [{bound-x1 :x1 bound-x2 :x2 bound-y1 :y1 bound-y2 :y2} bounds
(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
{sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2} selrect] {sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2} selrect]
{:left (make-selrect bound-x1 sr-y1 sr-x1 sr-y2) {:left (gpr/corners->selrect bound-x1 sr-y1 sr-x1 sr-y2)
:top (make-selrect sr-x1 bound-y1 sr-x2 sr-y1) :top (gpr/corners->selrect sr-x1 bound-y1 sr-x2 sr-y1)
:right (make-selrect sr-x2 sr-y1 bound-x2 sr-y2) :right (gpr/corners->selrect sr-x2 sr-y1 bound-x2 sr-y2)
:bottom (make-selrect sr-x1 sr-y2 sr-x2 bound-y2)})) :bottom (gpr/corners->selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
(defn distance-selrect [selrect other] (defn distance-selrect [selrect other]
(let [{:keys [x1 y1]} other (let [{:keys [x1 y1]} other
@ -161,6 +149,7 @@
(dm/export gpr/contains-selrect?) (dm/export gpr/contains-selrect?)
(dm/export gpr/contains-point?) (dm/export gpr/contains-point?)
(dm/export gpr/close-selrect?) (dm/export gpr/close-selrect?)
(dm/export gpr/clip-selrect)
(dm/export gtr/move) (dm/export gtr/move)
(dm/export gtr/absolute-move) (dm/export gtr/absolute-move)

View file

@ -35,9 +35,8 @@
ratio-width (/ target-width curr-width) ratio-width (/ target-width curr-width)
ratio-height (/ target-height curr-height) ratio-height (/ target-height curr-height)
scalev (gpt/point ratio-width ratio-height)] scalev (gpt/point ratio-width ratio-height)]
(-> modifiers (-> modifiers
(ctm/resize scalev origin transform transform-inverse)))) (ctm/resize scalev origin transform transform-inverse {:precise? true}))))
(defn position-pixel-precision (defn position-pixel-precision
[modifiers _ points] [modifiers _ points]

View file

@ -212,9 +212,13 @@
(<= (:y2 sr2) (:y2 sr1)))) (<= (:y2 sr2) (:y2 sr1))))
(defn corners->selrect (defn corners->selrect
[p1 p2] ([p1 p2]
(let [xp1 (:x p1) (corners->selrect (:x p1) (:y p1) (:x p2) (:y p2)))
xp2 (:x p2) ([xp1 yp1 xp2 yp2]
yp1 (:y p1) (make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2)))))
yp2 (:y p2)]
(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)))))

View file

@ -177,17 +177,19 @@
(defn- maybe-add-resize (defn- maybe-add-resize
"Check the last operation to check if we can stack it over the last one" "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) ([operations op {:keys [precise?]}]
[op] (if (c/empty? operations)
(let [head (peek operations)] [op]
(if (mergeable-resize? head op) (let [head (peek operations)]
(let [item (merge-resize head op)] (if (mergeable-resize? head op)
(cond-> (pop operations) (let [item (merge-resize head op)]
(resize-vec? (dm/get-prop item :vector)) (cond-> (pop operations)
(conj item))) (or precise? (resize-vec? (dm/get-prop item :vector)))
(conj operations op))))) (conj item)))
(conj operations op))))))
(defn valid-vector? (defn valid-vector?
[vector] [vector]
@ -259,12 +261,16 @@
(update :geometry-child maybe-add-resize (resize-op order vector origin))))) (update :geometry-child maybe-add-resize (resize-op order vector origin)))))
([modifiers vector origin transform transform-inverse] ([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))) (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(let [modifiers (or modifiers (empty)) (let [modifiers (or modifiers (empty))
order (inc (dm/get-prop modifiers :last-order)) order (inc (dm/get-prop modifiers :last-order))
modifiers (assoc modifiers :last-order order)] modifiers (assoc modifiers :last-order order)]
(cond-> modifiers (cond-> modifiers
(resize-vec? vector) (or precise? (resize-vec? vector))
(update :geometry-child maybe-add-resize (resize-op order vector origin transform transform-inverse)))))) (update :geometry-child maybe-add-resize (resize-op order vector origin transform transform-inverse))))))
(defn rotation (defn rotation

View file

@ -93,29 +93,32 @@
(->> (rx/from-atom commits) (->> (rx/from-atom commits)
(rx/filter (complement empty?)) (rx/filter (complement empty?))
(rx/sample-when (rx/sample-when
(->> (rx/merge (rx/merge
(rx/interval 5000) (rx/filter #(= ::force-persist %) stream)
(rx/filter #(= ::force-persist %) stream) (->> (rx/merge
(->> (rx/from-atom commits) (rx/interval 5000)
(rx/filter (complement empty?)) (->> (rx/from-atom commits)
(rx/debounce 2000))) (rx/filter (complement empty?))
;; Not sample while saving so there are no race conditions (rx/debounce 2000)))
(rx/filter #(not @saving?)))) ;; Not sample while saving so there are no race conditions
(rx/filter #(not @saving?)))))
(rx/tap #(reset! commits [])) (rx/tap #(reset! commits []))
(rx/tap on-saving) (rx/tap on-saving)
(rx/mapcat (fn [changes] (rx/mapcat (fn [changes]
;; NOTE: this is needed for don't start the ;; NOTE: this is needed for don't start the
;; next persistence before this one is ;; next persistence before this one is
;; finished. ;; finished.
(rx/merge (if-let [file-revn (dm/get-in @st/state [:workspace-file :revn])]
(->> (rx/of (persist-changes file-id changes commits)) (rx/merge
(rx/observe-on :async)) (->> (rx/of (persist-changes file-id file-revn changes commits))
(->> stream (rx/observe-on :async))
;; We wait for every change to be persisted (->> stream
(rx/filter (ptk/type? ::shapes-changes-persisted-finished)) ;; We wait for every change to be persisted
(rx/take 1) (rx/filter (ptk/type? ::shapes-changes-persisted-finished))
(rx/tap on-saved) (rx/take 1)
(rx/ignore))))) (rx/tap on-saved)
(rx/ignore)))
(rx/empty))))
(rx/take-until (rx/delay 100 stoper)) (rx/take-until (rx/delay 100 stoper))
(rx/finalize (fn [] (rx/finalize (fn []
(log/debug :hint "finalize persistence: save loop")))) (log/debug :hint "finalize persistence: save loop"))))
@ -132,7 +135,7 @@
(log/debug :hint "finalize persistence: synchronous save loop"))))))))) (log/debug :hint "finalize persistence: synchronous save loop")))))))))
(defn persist-changes (defn persist-changes
[file-id changes pending-commits] [file-id file-revn changes pending-commits]
(log/debug :hint "persist changes" :changes (count changes)) (log/debug :hint "persist changes" :changes (count changes))
(us/verify ::us/uuid file-id) (us/verify ::us/uuid file-id)
(ptk/reify ::persist-changes (ptk/reify ::persist-changes
@ -146,48 +149,46 @@
(features/active-feature? state :components-v2) (features/active-feature? state :components-v2)
(conj "components/v2")) (conj "components/v2"))
sid (:session-id state) sid (:session-id state)
file (get state :workspace-file) params {:id file-id
params {:id (:id file) :revn file-revn
:revn (:revn file)
:session-id sid :session-id sid
:changes-with-metadata (into [] changes) :changes-with-metadata (into [] changes)
:features features}] :features features}]
(when (= file-id (:id params)) (->> (rp/cmd! :update-file params)
(->> (rp/cmd! :update-file params) (rx/mapcat (fn [lagged]
(rx/mapcat (fn [lagged] (log/debug :hint "changes persisted" :lagged (count lagged))
(log/debug :hint "changes persisted" :lagged (count lagged)) (let [frame-updates
(let [frame-updates (-> (group-by :page-id changes)
(-> (group-by :page-id changes) (update-vals #(into #{} (mapcat :frames) %)))
(update-vals #(into #{} (mapcat :frames) %)))
commits commits
(->> @pending-commits (->> @pending-commits
(map #(assoc % :revn (:revn file))))] (map #(assoc % :revn file-revn)))]
(rx/concat (rx/concat
(rx/merge (rx/merge
(->> (rx/from frame-updates) (->> (rx/from frame-updates)
(rx/mapcat (fn [[page-id frames]] (rx/mapcat (fn [[page-id frames]]
(->> frames (map #(vector page-id %))))) (->> frames (map #(vector page-id %)))))
(rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail (:id file) page-id frame-id)))) (rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail file-id page-id frame-id))))
(->> (rx/from (concat lagged commits)) (->> (rx/from (concat lagged commits))
(rx/merge-map (rx/merge-map
(fn [{:keys [changes] :as entry}] (fn [{:keys [changes] :as entry}]
(rx/merge (rx/merge
(rx/from (rx/from
(for [[page-id changes] (group-by :page-id changes)] (for [[page-id changes] (group-by :page-id changes)]
(dch/update-indices page-id changes))) (dch/update-indices page-id changes)))
(rx/of (shapes-changes-persisted file-id entry))))))) (rx/of (shapes-changes-persisted file-id entry)))))))
(rx/of (shapes-changes-persisted-finished)))))) (rx/of (shapes-changes-persisted-finished))))))
(rx/catch (fn [cause] (rx/catch (fn [cause]
(rx/concat (rx/concat
(if (= :authentication (:type cause)) (if (= :authentication (:type cause))
(rx/empty) (rx/empty)
(rx/of (rt/assign-exception cause))) (rx/of (rt/assign-exception cause)))
(rx/throw cause)))))))))) (rx/throw cause)))))))))
;; Event to be thrown after the changes have been persisted ;; Event to be thrown after the changes have been persisted
(defn shapes-changes-persisted-finished (defn shapes-changes-persisted-finished

View file

@ -144,17 +144,6 @@
dist-lt (fn [other] (sr-distance coord (:selrect other) selrect)) dist-lt (fn [other] (sr-distance coord (:selrect other) selrect))
dist-gt (fn [other] (sr-distance coord selrect (:selrect other))) 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 ;; Calculates the distance between all the shapes given as argument
inner-distance inner-distance
(fn [selrects] (fn [selrects]
@ -164,14 +153,6 @@
#(overlap? coord %1 %2) #(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 ;; Distance between the elements in an area, these are the snap
;; candidates to either side ;; candidates to either side
lt-cand (inner-distance (mapv :selrect shapes-lt)) lt-cand (inner-distance (mapv :selrect shapes-lt))
@ -182,17 +163,46 @@
lt-dist (into #{} (map dist-lt) shapes-lt) lt-dist (into #{} (map dist-lt) shapes-lt)
gt-dist (into #{} (map dist-gt) shapes-gt) gt-dist (into #{} (map dist-gt) shapes-gt)
;; Calculate the snaps, we need to reverse depending on area get-side-snaps
lt-snap (d/join lt-cand lt-dist -) (fn [candidates distances]
gt-snap (d/join gt-dist gt-cand -) ;; 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 get-middle-snaps
between-snap (->> (d/join shapes-lt shapes-gt) (fn [lt-dist gt-dist]
(map between-snap)) (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 ;; Search the minimum snap
snap-list (d/concat-vec lt-snap gt-snap between-snap) snap-list (d/concat-vec lt-snap gt-snap md-snap)
min-snap (reduce best-snap ##Inf snap-list)]
min-snap (reduce min ##Inf snap-list)]
(if (d/num? min-snap) [0 min-snap] nil))) (if (d/num? min-snap) [0 min-snap] nil)))
@ -202,14 +212,14 @@
(calculate-snap coord selrect shapes-lt shapes-gt zoom))))) (calculate-snap coord selrect shapes-lt shapes-gt zoom)))))
(defn select-shapes-area (defn select-shapes-area
[page-id shapes objects area-selrect] [page-id frame-id selected objects area]
(->> (uw/ask! {:cmd :selection/query (->> (uw/ask! {:cmd :selection/query
:page-id page-id :page-id page-id
:frame-id (->> shapes first :frame-id) :frame-id frame-id
:include-frames? true :include-frames? true
:rect area-selrect}) :rect area})
(rx/map #(cph/clean-loops objects %)) (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) %)))) (rx/map #(map (d/getf objects) %))))
(defn closest-distance-snap (defn closest-distance-snap
@ -220,9 +230,14 @@
(->> (rx/of (vector frame selrect)) (->> (rx/of (vector frame selrect))
(rx/merge-map (rx/merge-map
(fn [[frame selrect]] (fn [[frame selrect]]
(let [areas (->> (gsh/selrect->areas (or (:selrect frame) (let [vbox (gsh/rect->selrect @refs/vbox)
(gsh/rect->selrect @refs/vbox)) selrect) frame-id (->> shapes first :frame-id)
(d/mapm #(select-shapes-area page-id shapes objects %2))) 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-x (search-snap-distance selrect :x (:left areas) (:right areas) zoom)
snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas) zoom)] snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas) zoom)]
(rx/combine-latest snap-x snap-y)))) (rx/combine-latest snap-x snap-y))))

View file

@ -9,11 +9,10 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.pages.helpers :as cph]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.snap :as ams]
[app.main.ui.formats :as fmt] [app.main.ui.formats :as fmt]
[app.main.worker :as uw]
[beicon.core :as rx] [beicon.core :as rx]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -152,7 +151,7 @@
check-in-set check-in-set
(fn [value number-set] (fn [value number-set]
(->> 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) ;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter)
@ -218,21 +217,16 @@
(fn [[selrect selected frame]] (fn [[selrect selected frame]]
(let [lt-side (if (= coord :x) :left :top) (let [lt-side (if (= coord :x) :left :top)
gt-side (if (= coord :x) :right :bottom) gt-side (if (= coord :x) :right :bottom)
container-selrec (or (:selrect frame)
(gsh/rect->selrect @refs/vbox)) vbox (gsh/rect->selrect @refs/vbox)
areas (gsh/selrect->areas container-selrec selrect) areas (gsh/selrect->areas
(or (gsh/clip-selrect (:selrect frame) vbox) vbox)
selrect)
query-side (fn [side] query-side (fn [side]
(let [rect (get areas side)] (let [rect (get areas side)]
(if (and (> (:width rect) 0) (> (:height rect) 0)) (if (and (> (:width rect) 0) (> (:height rect) 0))
(->> (uw/ask! {:cmd :selection/query (ams/select-shapes-area page-id (:id frame) selected @refs/workspace-page-objects rect)
: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)))))
(rx/of nil))))] (rx/of nil))))]
(rx/combine-latest (query-side lt-side) (rx/combine-latest (query-side lt-side)
(query-side gt-side)))) (query-side gt-side))))

View file

@ -107,7 +107,7 @@
(->> (http/send! request) (->> (http/send! request)
(rx/map http/conditional-decode-transit) (rx/map http/conditional-decode-transit)
(rx/mapcat handle-response) (rx/mapcat handle-response)
(rx/catch body-too-large? (constantly nil)) (rx/catch body-too-large? (constantly (rx/of nil)))
(rx/map (constantly params))))) (rx/map (constantly params)))))
(defmethod impl/handler :thumbnails/generate (defmethod impl/handler :thumbnails/generate