diff --git a/common/src/app/common/geom/point.cljc b/common/src/app/common/geom/point.cljc index 5dd41501e..6c640173a 100644 --- a/common/src/app/common/geom/point.cljc +++ b/common/src/app/common/geom/point.cljc @@ -274,12 +274,12 @@ (Point. (mth/precision (dm/get-prop pt :x) decimals) (mth/precision (dm/get-prop pt :y) decimals)))) -(defn half-round +(defn round-step "Round the coordinates to the closest half-point" - [pt] + [pt step] (assert (point? pt) "expected point instance") - (Point. (mth/half-round (dm/get-prop pt :x)) - (mth/half-round (dm/get-prop pt :y)))) + (Point. (mth/round (dm/get-prop pt :x) step) + (mth/round (dm/get-prop pt :y) step))) (defn transform "Transform a point applying a matrix transformation." diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 4b11007a3..ff6e05f52 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -382,15 +382,24 @@ result)) (defn set-objects-modifiers - ([modif-tree objects ignore-constraints snap-pixel?] - (set-objects-modifiers nil modif-tree objects ignore-constraints snap-pixel?)) + ([modif-tree objects] + (set-objects-modifiers modif-tree objects nil)) - ([old-modif-tree modif-tree objects ignore-constraints snap-pixel?] + ([modif-tree objects params] + (set-objects-modifiers nil modif-tree objects params)) + + ([old-modif-tree modif-tree objects + {:keys [ignore-constraints snap-pixel? snap-precision] + :or {ignore-constraints false snap-pixel? false snap-precision 1}}] (let [objects (-> objects (cond-> (some? old-modif-tree) (apply-structure-modifiers old-modif-tree)) (apply-structure-modifiers modif-tree)) + modif-tree + (cond-> modif-tree + snap-pixel? (gpp/adjust-pixel-precision objects snap-precision)) + bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points])) bounds (cond-> bounds (some? old-modif-tree) @@ -417,11 +426,7 @@ modif-tree (if old-modif-tree (merge-modif-tree old-modif-tree modif-tree) - modif-tree) - - modif-tree - (cond-> modif-tree - snap-pixel? (gpp/adjust-pixel-precision objects))] + modif-tree)] ;;#?(:cljs ;; (.log js/console ">result" (modif->js modif-tree objects))) diff --git a/common/src/app/common/geom/shapes/pixel_precision.cljc b/common/src/app/common/geom/shapes/pixel_precision.cljc index c1236046f..f75b650d9 100644 --- a/common/src/app/common/geom/shapes/pixel_precision.cljc +++ b/common/src/app/common/geom/shapes/pixel_precision.cljc @@ -18,7 +18,7 @@ [app.common.types.modifiers :as ctm])) (defn size-pixel-precision - [modifiers shape points] + [modifiers shape points precision] (let [origin (gpo/origin points) curr-width (gpo/width-points points) curr-height (gpo/height-points points) @@ -29,8 +29,8 @@ vertical-line? (and path? (<= curr-width 0.01)) horizontal-line? (and path? (<= curr-height 0.01)) - target-width (if vertical-line? curr-width (max 1 (mth/round curr-width))) - target-height (if horizontal-line? curr-height (max 1 (mth/round curr-height))) + target-width (if vertical-line? curr-width (max 1 (mth/round curr-width precision))) + target-height (if horizontal-line? curr-height (max 1 (mth/round curr-height precision))) ratio-width (/ target-width curr-width) ratio-height (/ target-height curr-height) @@ -39,23 +39,23 @@ (ctm/resize scalev origin transform transform-inverse {:precise? true})))) (defn position-pixel-precision - [modifiers _ points] + [modifiers _ points precision] (let [bounds (gpr/bounds->rect points) corner (gpt/point bounds) - target-corner (gpt/round corner) + target-corner (gpt/round-step corner precision) deltav (gpt/to-vec corner target-corner)] (ctm/move modifiers deltav))) (defn set-pixel-precision "Adjust modifiers so they adjust to the pixel grid" - [modifiers shape] + [modifiers shape precision] (let [points (-> shape :points (gco/transform-points (ctm/modifiers->transform modifiers))) has-resize? (not (ctm/only-move? modifiers)) [modifiers points] (let [modifiers (cond-> modifiers - has-resize? (size-pixel-precision shape points)) + has-resize? (size-pixel-precision shape points precision)) points (if has-resize? @@ -63,16 +63,16 @@ (gco/transform-points (ctm/modifiers->transform modifiers)) ) points)] [modifiers points])] - (position-pixel-precision modifiers shape points))) + (position-pixel-precision modifiers shape points precision))) (defn adjust-pixel-precision - [modif-tree objects] + [modif-tree objects precision] (let [update-modifiers (fn [modif-tree shape] (let [modifiers (dm/get-in modif-tree [(:id shape) :modifiers])] (cond-> modif-tree (ctm/has-geometry? modifiers) - (update-in [(:id shape) :modifiers] set-pixel-precision shape))))] + (update-in [(:id shape) :modifiers] set-pixel-precision shape precision))))] (->> (keys modif-tree) (map (d/getf objects)) diff --git a/common/src/app/common/math.cljc b/common/src/app/common/math.cljc index 5ed4f01f9..9def09aff 100644 --- a/common/src/app/common/math.cljc +++ b/common/src/app/common/math.cljc @@ -104,15 +104,16 @@ (defn round "Returns the value of a number rounded to - the nearest integer." - [v] - #?(:cljs (js/Math.round v) - :clj (Math/round (float v)))) + the nearest integer. + If given step rounds to the next closest step, for example: + (round 13.4 0.5) => 13.5 + (round 13.4 0.3) => 13.3" + ([v step] + (* (round (/ v step)) step)) -(defn half-round - "Returns a value rounded to the next point or half point" - [v] - (/ (round (* v 2)) 2)) + ([v] + #?(:cljs (js/Math.round v) + :clj (Math/round (float v))))) (defn ceil "Returns the smallest integer greater than diff --git a/common/test/common_tests/geom_point_test.cljc b/common/test/common_tests/geom_point_test.cljc index c98052315..9be0bc153 100644 --- a/common/test/common_tests/geom_point_test.cljc +++ b/common/test/common_tests/geom_point_test.cljc @@ -203,7 +203,7 @@ (t/deftest halft-round-point (let [p1 (gpt/point 1.34567 3.34567) - rs (gpt/half-round p1)] + rs (gpt/round-step p1 0.5)] (t/is (gpt/point? rs)) (t/is (mth/close? 1.5 (:x rs))) (t/is (mth/close? 3.5 (:y rs))))) diff --git a/frontend/src/app/main/constants.cljs b/frontend/src/app/main/constants.cljs index 968a56a13..acae0096e 100644 --- a/frontend/src/app/main/constants.cljs +++ b/frontend/src/app/main/constants.cljs @@ -190,3 +190,5 @@ {:name "YouTube thumb" :width 1280 :height 720}]) + +(def zoom-half-pixel-precision 8) diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs index 54e5456fe..140d58bbd 100644 --- a/frontend/src/app/main/data/workspace/drawing/box.cljs +++ b/frontend/src/app/main/data/workspace/drawing/box.cljs @@ -16,6 +16,7 @@ [app.common.types.shape-tree :as ctst] [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] + [app.main.constants :refer [zoom-half-pixel-precision]] [app.main.data.workspace.drawing.common :as common] [app.main.data.workspace.state-helpers :as wsh] [app.main.snap :as snap] @@ -70,14 +71,15 @@ (let [stoper? #(or (ms/mouse-up? %) (= % :interrupt)) stoper (rx/filter stoper? stream) layout (get state :workspace-layout) + zoom (get-in state [:workspace-local :zoom] 1) snap-pixel? (contains? layout :snap-pixel-grid) - initial (cond-> @ms/mouse-position snap-pixel? gpt/round) + snap-precision (if (>= zoom zoom-half-pixel-precision) 0.5 1) + initial (cond-> @ms/mouse-position snap-pixel? (gpt/round-step snap-precision)) page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) focus (:workspace-focus-selected state) - zoom (get-in state [:workspace-local :zoom] 1) fid (ctst/top-nested-frame objects initial) layout? (ctl/layout? objects fid) @@ -119,7 +121,7 @@ (rx/map #(conj current %))))) (rx/map (fn [[_ shift? point]] - #(update-drawing % initial (cond-> point snap-pixel? gpt/round) shift?))))) + #(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step snap-precision)) shift?))))) (rx/take-until stoper)) (->> (rx/of (common/handle-finish-drawing)) diff --git a/frontend/src/app/main/data/workspace/media.cljs b/frontend/src/app/main/data/workspace/media.cljs index 8fd501f59..766b29406 100644 --- a/frontend/src/app/main/data/workspace/media.cljs +++ b/frontend/src/app/main/data/workspace/media.cljs @@ -8,6 +8,7 @@ (:require [app.common.exceptions :as ex] [app.common.logging :as log] + [app.common.math :as mth] [app.common.pages.changes-builder :as pcb] [app.common.spec :as us] [app.common.types.container :as ctn] @@ -52,8 +53,8 @@ shape {:name name :width width :height height - :x (- x (/ width 2)) - :y (- y (/ height 2)) + :x (mth/round (- x (/ width 2))) + :y (mth/round (- y (/ height 2))) :metadata {:width width :height height :mtype mtype diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 514fc32a7..a93bb08f7 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -17,6 +17,7 @@ [app.common.spec :as us] [app.common.types.modifiers :as ctm] [app.common.types.shape.layout :as ctl] + [app.main.constants :refer [zoom-half-pixel-precision]] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.comments :as-alias dwcm] [app.main.data.workspace.guides :as-alias dwg] @@ -244,12 +245,15 @@ (wsh/lookup-page-objects state) snap-pixel? - (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))] + (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) + + zoom (dm/get-in state [:workspace-local :zoom]) + snap-precision (if (>= zoom zoom-half-pixel-precision) 0.5 1)] (as-> objects $ (apply-text-modifiers $ (get state :workspace-text-modifier)) ;;(apply-path-modifiers $ (get-in state [:workspace-local :edit-path])) - (gsh/set-objects-modifiers modif-tree $ ignore-constraints snap-pixel?))))) + (gsh/set-objects-modifiers modif-tree $ {:ignore-constraints ignore-constraints :snap-pixel? snap-pixel? :snap-precision snap-precision}))))) (defn- calculate-update-modifiers [old-modif-tree state ignore-constraints ignore-snap-pixel modif-tree] @@ -259,10 +263,13 @@ snap-pixel? (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) + zoom (dm/get-in state [:workspace-local :zoom]) + + snap-precision (if (>= zoom zoom-half-pixel-precision) 0.5 1) objects (-> objects (apply-text-modifiers (get state :workspace-text-modifier)))] - (gsh/set-objects-modifiers old-modif-tree modif-tree objects ignore-constraints snap-pixel?))) + (gsh/set-objects-modifiers old-modif-tree modif-tree objects {:ignore-constraints ignore-constraints :snap-pixel? snap-pixel? :snap-precision snap-precision}))) (defn update-modifiers ([modif-tree] @@ -312,7 +319,7 @@ modif-tree (-> (build-modif-tree ids objects get-modifier) - (gsh/set-objects-modifiers objects false false))] + (gsh/set-objects-modifiers objects))] (assoc state :workspace-modifiers modif-tree)))))) diff --git a/frontend/src/app/main/data/workspace/path/streams.cljs b/frontend/src/app/main/data/workspace/path/streams.cljs index db1a09464..147fbd511 100644 --- a/frontend/src/app/main/data/workspace/path/streams.cljs +++ b/frontend/src/app/main/data/workspace/path/streams.cljs @@ -8,6 +8,7 @@ (:require [app.common.geom.point :as gpt] [app.common.geom.shapes.path :as upg] + [app.main.constants :refer [zoom-half-pixel-precision]] [app.main.data.workspace.path.state :as state] [app.main.snap :as snap] [app.main.store :as st] @@ -17,7 +18,6 @@ [potok.core :as ptk])) (defonce drag-threshold 5) -(def zoom-half-pixel-precision 8) (defn dragging? [start zoom] (fn [current] @@ -36,7 +36,7 @@ position (>= zoom zoom-half-pixel-precision) - (gpt/half-round position) + (gpt/round-step position 0.5) :else (gpt/round position)))) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index f299fa8d6..94ded56b6 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -11,6 +11,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] + [app.common.math :as mth] [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [app.common.spec :as us :refer [max-safe-int min-safe-int]] @@ -477,15 +478,17 @@ (rx/reduce (fn [acc [url image]] (assoc acc url image)) {}))) (defn create-svg-shapes - [svg-data {:keys [x y] :as position} objects frame-id parent-id selected center?] + [svg-data {:keys [x y]} objects frame-id parent-id selected center?] (try (let [[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data) - x (if center? - (- x vb-x (/ vb-width 2)) - x) - y (if center? - (- y vb-y (/ vb-height 2)) - y) + x (mth/round + (if center? + (- x vb-x (/ vb-width 2)) + x)) + y (mth/round + (if center? + (- y vb-y (/ vb-height 2)) + y)) unames (ctst/retrieve-used-names objects) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index ce9798f77..5525b7302 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -240,14 +240,12 @@ ptk/UpdateEvent (update [_ state] (let [objects (wsh/lookup-page-objects state) - snap-pixel? (and (contains? (:workspace-layout state) :snap-pixel-grid) - (int? value)) get-modifier (fn [shape] (ctm/change-dimensions-modifiers shape attr value)) modif-tree (-> (dwm/build-modif-tree ids objects get-modifier) - (gsh/set-objects-modifiers objects false snap-pixel?))] + (gsh/set-objects-modifiers objects))] (assoc state :workspace-modifiers modif-tree))) @@ -265,14 +263,13 @@ ptk/UpdateEvent (update [_ state] (let [objects (wsh/lookup-page-objects state) - snap-pixel? (contains? (get state :workspace-layout) :snap-pixel-grid) get-modifier (fn [shape] (ctm/change-orientation-modifiers shape orientation)) modif-tree (-> (dwm/build-modif-tree ids objects get-modifier) - (gsh/set-objects-modifiers objects false snap-pixel?))] + (gsh/set-objects-modifiers objects))] (assoc state :workspace-modifiers modif-tree))) @@ -623,7 +620,7 @@ (->> move-events (rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0)) (rx/map #(dwm/create-modif-tree selected (ctm/move-modifiers %))) - (rx/map (partial dwm/set-modifiers)) + (rx/map #(dwm/set-modifiers % false true)) (rx/take-until stopper)) (rx/of (nudge-selected-shapes direction shift?))) @@ -669,11 +666,12 @@ cpos (gpt/point (:x bbox) (:y bbox)) pos (gpt/point (or (:x position) (:x bbox)) (or (:y position) (:y bbox))) + delta (gpt/subtract pos cpos) modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))] - (rx/of (dwm/set-modifiers modif-tree) + (rx/of (dwm/set-modifiers modif-tree false true) (dwm/apply-modifiers)))))) (defn- move-shapes-to-frame @@ -688,7 +686,6 @@ shapes (->> ids (cph/clean-loops objects) (keep lookup)) - moving-shapes (cond->> shapes (not layout?)