0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-01 09:31:26 -05:00

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))))))
(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)

View file

@ -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]

View file

@ -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)))))

View file

@ -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

View file

@ -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

View file

@ -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))))

View file

@ -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))))

View file

@ -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