mirror of
https://github.com/penpot/penpot.git
synced 2026-04-27 04:08:23 +00:00
Merge pull request #1690 from penpot/feat/pixel-precision
Pixel precision
This commit is contained in:
commit
4d2de63374
@ -406,15 +406,24 @@
|
||||
[v default]
|
||||
(if (some? v) v default))
|
||||
|
||||
(defn num?
|
||||
"Checks if a value `val` is a number but not an Infinite or NaN"
|
||||
([val]
|
||||
(and (number? val)
|
||||
(mth/finite? val)
|
||||
(not (mth/nan? val))))
|
||||
|
||||
([val & vals]
|
||||
(and (num? val)
|
||||
(->> vals (every? num?)))))
|
||||
|
||||
(defn check-num
|
||||
"Function that checks if a number is nil or nan. Will return 0 when not
|
||||
valid and the number otherwise."
|
||||
([v]
|
||||
(check-num v 0))
|
||||
([v default]
|
||||
(if (or (not v)
|
||||
(not (mth/finite? v))
|
||||
(mth/nan? v)) default v)))
|
||||
(if (num? v) v default)))
|
||||
|
||||
(defn any-key? [element & rest]
|
||||
(some #(contains? element %) rest))
|
||||
|
||||
@ -21,6 +21,11 @@
|
||||
(def conjv (fnil conj []))
|
||||
(def conjs (fnil conj #{}))
|
||||
|
||||
(defn- raise
|
||||
[err-str]
|
||||
#?(:clj (throw (Exception. err-str))
|
||||
:cljs (throw (js/Error. err-str))))
|
||||
|
||||
(defn- commit-change
|
||||
([file change]
|
||||
(commit-change file change nil))
|
||||
@ -75,10 +80,12 @@
|
||||
|
||||
(commit-change file change {:add-container? true :fail-on-spec? fail-on-spec?})))
|
||||
|
||||
(defn setup-rect-selrect [obj]
|
||||
(let [rect (select-keys obj [:x :y :width :height])
|
||||
(defn setup-rect-selrect [{:keys [x y width height transform] :as obj}]
|
||||
(when-not (d/num? x y width height)
|
||||
(raise "Coords not valid for object"))
|
||||
|
||||
(let [rect (gsh/make-rect x y width height)
|
||||
center (gsh/center-rect rect)
|
||||
transform (:transform obj (gmt/matrix))
|
||||
selrect (gsh/rect->selrect rect)
|
||||
|
||||
points (-> (gsh/rect->points rect)
|
||||
@ -89,17 +96,13 @@
|
||||
(assoc :points points))))
|
||||
|
||||
(defn- setup-path-selrect
|
||||
[obj]
|
||||
(let [content (:content obj)
|
||||
center (:center obj)
|
||||
[{:keys [content center transform transform-inverse] :as obj}]
|
||||
|
||||
transform-inverse
|
||||
(->> (:transform-inverse obj (gmt/matrix))
|
||||
(gmt/transform-in center))
|
||||
(when (or (empty? content) (nil? center))
|
||||
(raise "Path not valid"))
|
||||
|
||||
transform
|
||||
(->> (:transform obj (gmt/matrix))
|
||||
(gmt/transform-in center))
|
||||
(let [transform (gmt/transform-in center transform)
|
||||
transform-inverse (gmt/transform-in center transform-inverse)
|
||||
|
||||
content' (gsh/transform-content content transform-inverse)
|
||||
selrect (gsh/content->selrect content')
|
||||
@ -310,21 +313,30 @@
|
||||
children (->> bool :shapes (mapv #(lookup-shape file %)))
|
||||
|
||||
file
|
||||
(let [objects (lookup-objects file)
|
||||
bool' (gsh/update-bool-selrect bool children objects)]
|
||||
(cond
|
||||
(empty? children)
|
||||
(commit-change
|
||||
file
|
||||
{:type :mod-obj
|
||||
:id bool-id
|
||||
:operations
|
||||
[{:type :set :attr :selrect :val (:selrect bool')}
|
||||
{:type :set :attr :points :val (:points bool')}
|
||||
{:type :set :attr :x :val (-> bool' :selrect :x)}
|
||||
{:type :set :attr :y :val (-> bool' :selrect :y)}
|
||||
{:type :set :attr :width :val (-> bool' :selrect :width)}
|
||||
{:type :set :attr :height :val (-> bool' :selrect :height)}]}
|
||||
{:type :del-obj
|
||||
:id bool-id}
|
||||
{:add-container? true})
|
||||
|
||||
{:add-container? true}))]
|
||||
:else
|
||||
(let [objects (lookup-objects file)
|
||||
bool' (gsh/update-bool-selrect bool children objects)]
|
||||
(commit-change
|
||||
file
|
||||
{:type :mod-obj
|
||||
:id bool-id
|
||||
:operations
|
||||
[{:type :set :attr :selrect :val (:selrect bool')}
|
||||
{:type :set :attr :points :val (:points bool')}
|
||||
{:type :set :attr :x :val (-> bool' :selrect :x)}
|
||||
{:type :set :attr :y :val (-> bool' :selrect :y)}
|
||||
{:type :set :attr :width :val (-> bool' :selrect :width)}
|
||||
{:type :set :attr :height :val (-> bool' :selrect :height)}]}
|
||||
|
||||
{:add-container? true})))]
|
||||
|
||||
(-> file
|
||||
(update :parent-stack pop))))
|
||||
|
||||
@ -108,9 +108,12 @@
|
||||
(= v base))
|
||||
|
||||
(defn translate-matrix
|
||||
[{x :x y :y :as pt}]
|
||||
(assert (gpt/point? pt))
|
||||
(Matrix. 1 0 0 1 x y))
|
||||
([{x :x y :y :as pt}]
|
||||
(assert (gpt/point? pt))
|
||||
(Matrix. 1 0 0 1 x y))
|
||||
|
||||
([x y]
|
||||
(translate-matrix (gpt/point x y))))
|
||||
|
||||
(defn scale-matrix
|
||||
([pt center]
|
||||
@ -184,7 +187,7 @@
|
||||
(defmethod pp/simple-dispatch Matrix [obj] (pr obj))
|
||||
|
||||
(defn transform-in [pt mtx]
|
||||
(if (some? pt)
|
||||
(if (and (some? pt) (some? mtx))
|
||||
(-> (matrix)
|
||||
(translate pt)
|
||||
(multiply mtx)
|
||||
|
||||
@ -132,9 +132,8 @@
|
||||
(assert (point? other))
|
||||
(let [dx (- x ox)
|
||||
dy (- y oy)]
|
||||
(-> (mth/sqrt (+ (mth/pow dx 2)
|
||||
(mth/pow dy 2)))
|
||||
(mth/precision 6))))
|
||||
(mth/sqrt (+ (mth/pow dx 2)
|
||||
(mth/pow dy 2)))))
|
||||
|
||||
(defn length
|
||||
[{x :x y :y :as p}]
|
||||
@ -168,8 +167,7 @@
|
||||
(* y oy))
|
||||
(* length-p length-other))
|
||||
a (mth/acos (if (< a -1) -1 (if (> a 1) 1 a)))
|
||||
d (-> (mth/degrees a)
|
||||
(mth/precision 6))]
|
||||
d (mth/degrees a)]
|
||||
(if (mth/nan? d) 0 d)))))
|
||||
|
||||
(defn angle-sign [v1 v2]
|
||||
@ -194,14 +192,23 @@
|
||||
(if (>= y 0) 2 3)))
|
||||
|
||||
(defn round
|
||||
"Change the precision of the point coordinates."
|
||||
([point] (round point 0))
|
||||
"Round the coordinates of the point to a precision"
|
||||
([point]
|
||||
(round point 0))
|
||||
|
||||
([{:keys [x y] :as p} decimals]
|
||||
(assert (point? p))
|
||||
(assert (number? decimals))
|
||||
(Point. (mth/precision x decimals)
|
||||
(mth/precision y decimals))))
|
||||
|
||||
(defn half-round
|
||||
"Round the coordinates to the closest half-point"
|
||||
[{:keys [x y] :as p}]
|
||||
(assert (point? p))
|
||||
(Point. (mth/half-round x)
|
||||
(mth/half-round y)))
|
||||
|
||||
(defn transform
|
||||
"Transform a point applying a matrix transformation."
|
||||
[{:keys [x y] :as p} {:keys [a b c d e f]}]
|
||||
|
||||
@ -17,39 +17,6 @@
|
||||
[app.common.geom.shapes.transforms :as gtr]
|
||||
[app.common.math :as mth]))
|
||||
|
||||
;; --- Setup (Initialize)
|
||||
;; FIXME: Is this the correct place for these functions?
|
||||
|
||||
(defn- setup-rect
|
||||
"A specialized function for setup rect-like shapes."
|
||||
[shape {:keys [x y width height]}]
|
||||
(let [rect {:x x :y y :width width :height height}
|
||||
points (gpr/rect->points rect)
|
||||
selrect (gpr/points->selrect points)]
|
||||
(assoc shape
|
||||
:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:points points
|
||||
:selrect selrect)))
|
||||
|
||||
(defn- setup-image
|
||||
[{:keys [metadata] :as shape} props]
|
||||
(-> (setup-rect shape props)
|
||||
(assoc
|
||||
:proportion (/ (:width metadata)
|
||||
(:height metadata))
|
||||
:proportion-lock true)))
|
||||
|
||||
(defn setup
|
||||
"A function that initializes the first coordinates for
|
||||
the shape. Used mainly for draw operations."
|
||||
[shape props]
|
||||
(case (:type shape)
|
||||
:image (setup-image shape props)
|
||||
(setup-rect shape props)))
|
||||
|
||||
;; --- Outer Rect
|
||||
|
||||
(defn selection-rect
|
||||
@ -106,12 +73,12 @@
|
||||
:width (- x2 x1)
|
||||
:height (- y2 y1)
|
||||
:type :rect}))
|
||||
{frame-x1 :x1 frame-x2 :x2 frame-y1 :y1 frame-y2 :y2} bounds
|
||||
{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 frame-x1 sr-y1 (- sr-x1 2) sr-y2)
|
||||
:top (make-selrect sr-x1 frame-y1 sr-x2 (- sr-y1 2))
|
||||
:right (make-selrect (+ sr-x2 2) sr-y1 frame-x2 sr-y2)
|
||||
:bottom (make-selrect sr-x1 (+ sr-y2 2) sr-x2 frame-y2)}))
|
||||
{: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)}))
|
||||
|
||||
(defn distance-selrect [selrect other]
|
||||
(let [{:keys [x1 y1]} other
|
||||
@ -121,13 +88,6 @@
|
||||
(defn distance-shapes [shape other]
|
||||
(distance-selrect (:selrect shape) (:selrect other)))
|
||||
|
||||
(defn setup-selrect [shape]
|
||||
(let [selrect (gpr/rect->selrect shape)
|
||||
points (gpr/rect->points shape)]
|
||||
(-> shape
|
||||
(assoc :selrect selrect
|
||||
:points points))))
|
||||
|
||||
(defn shape-stroke-margin
|
||||
[shape stroke-width]
|
||||
(if (= (:type shape) :path)
|
||||
@ -141,15 +101,17 @@
|
||||
(dm/export gco/center-selrect)
|
||||
(dm/export gco/center-rect)
|
||||
(dm/export gco/center-points)
|
||||
(dm/export gco/make-centered-rect)
|
||||
(dm/export gco/transform-points)
|
||||
|
||||
(dm/export gpr/make-rect)
|
||||
(dm/export gpr/rect->selrect)
|
||||
(dm/export gpr/rect->points)
|
||||
(dm/export gpr/points->selrect)
|
||||
(dm/export gpr/points->rect)
|
||||
(dm/export gpr/center->rect)
|
||||
(dm/export gpr/center->selrect)
|
||||
(dm/export gpr/join-rects)
|
||||
(dm/export gpr/join-selrects)
|
||||
(dm/export gpr/contains-selrect?)
|
||||
|
||||
(dm/export gtr/move)
|
||||
@ -166,6 +128,7 @@
|
||||
(dm/export gtr/merge-modifiers)
|
||||
(dm/export gtr/transform-shape)
|
||||
(dm/export gtr/transform-selrect)
|
||||
(dm/export gtr/transform-bounds)
|
||||
(dm/export gtr/modifiers->transform)
|
||||
(dm/export gtr/empty-modifiers?)
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.geom.shapes.transforms :as gtr]
|
||||
[app.common.path.bool :as pb]
|
||||
[app.common.path.shapes-to-path :as stp]))
|
||||
@ -30,15 +29,13 @@
|
||||
"Calculates the selrect+points for the boolean shape"
|
||||
[shape children objects]
|
||||
|
||||
(let [content (calc-bool-content shape objects)
|
||||
[points selrect]
|
||||
(if (empty? content)
|
||||
(let [selrect (gtr/selection-rect children)
|
||||
points (gpr/rect->points selrect)]
|
||||
[points selrect])
|
||||
(gsp/content->points+selrect shape content))]
|
||||
(-> shape
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points)
|
||||
(assoc :bool-content content))))
|
||||
(let [bool-content (calc-bool-content shape objects)
|
||||
shape (assoc shape :bool-content bool-content)
|
||||
[points selrect] (gsp/content->points+selrect shape bool-content)]
|
||||
|
||||
(if (and (some? selrect) (d/not-empty? points))
|
||||
(-> shape
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points))
|
||||
(gtr/update-group-selrect shape children))))
|
||||
|
||||
|
||||
@ -6,30 +6,24 @@
|
||||
|
||||
(ns app.common.geom.shapes.common
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as mth]))
|
||||
[app.common.geom.point :as gpt]))
|
||||
|
||||
(defn center-rect
|
||||
[{:keys [x y width height]}]
|
||||
(when (and (mth/finite? x)
|
||||
(mth/finite? y)
|
||||
(mth/finite? width)
|
||||
(mth/finite? height))
|
||||
(when (d/num? x y width height)
|
||||
(gpt/point (+ x (/ width 2.0))
|
||||
(+ y (/ height 2.0)))))
|
||||
|
||||
(defn center-selrect
|
||||
"Calculate the center of the shape."
|
||||
"Calculate the center of the selrect."
|
||||
[selrect]
|
||||
(center-rect selrect))
|
||||
|
||||
(def map-x-xf (comp (map :x) (remove nil?)))
|
||||
(def map-y-xf (comp (map :y) (remove nil?)))
|
||||
|
||||
(defn center-points [points]
|
||||
(let [ptx (into [] map-x-xf points)
|
||||
pty (into [] map-y-xf points)
|
||||
(let [ptx (into [] (keep :x) points)
|
||||
pty (into [] (keep :y) points)
|
||||
minx (reduce min ##Inf ptx)
|
||||
miny (reduce min ##Inf pty)
|
||||
maxx (reduce max ##-Inf ptx)
|
||||
@ -42,35 +36,12 @@
|
||||
[shape]
|
||||
(center-rect (:selrect shape)))
|
||||
|
||||
(defn make-centered-rect
|
||||
"Creates a rect given a center and a width and height"
|
||||
[center width height]
|
||||
{:x (- (:x center) (/ width 2.0))
|
||||
:y (- (:y center) (/ height 2.0))
|
||||
:width width
|
||||
:height height})
|
||||
|
||||
(defn make-centered-selrect
|
||||
"Creates a rect given a center and a width and height"
|
||||
[center width height]
|
||||
(let [x1 (- (:x center) (/ width 2.0))
|
||||
y1 (- (:y center) (/ height 2.0))
|
||||
x2 (+ x1 width)
|
||||
y2 (+ y1 height)]
|
||||
{:x x1
|
||||
:y y1
|
||||
:x1 x1
|
||||
:x2 x2
|
||||
:y1 y1
|
||||
:y2 y2
|
||||
:width width
|
||||
:height height}))
|
||||
|
||||
(defn transform-points
|
||||
([points matrix]
|
||||
(transform-points points nil matrix))
|
||||
|
||||
([points center matrix]
|
||||
(if (some? matrix)
|
||||
(if (and (d/not-empty? points) (gmt/matrix? matrix))
|
||||
(let [prev (if center (gmt/translate-matrix center) (gmt/matrix))
|
||||
post (if center (gmt/translate-matrix (gpt/negate center)) (gmt/matrix))
|
||||
|
||||
|
||||
@ -333,11 +333,8 @@
|
||||
(command->point command :c2)]]
|
||||
(->> (curve-extremities curve)
|
||||
(mapv #(curve-values curve %)))))
|
||||
[])
|
||||
selrect (gpr/points->selrect points)]
|
||||
(-> selrect
|
||||
(update :width #(if (mth/almost-zero? %) 1 %))
|
||||
(update :height #(if (mth/almost-zero? %) 1 %))))))
|
||||
[])]
|
||||
(gpr/points->selrect points))))
|
||||
|
||||
(defn content->selrect [content]
|
||||
(let [calc-extremities
|
||||
@ -362,13 +359,8 @@
|
||||
|
||||
extremities (mapcat calc-extremities
|
||||
content
|
||||
(concat [nil] content))
|
||||
|
||||
selrect (gpr/points->selrect extremities)]
|
||||
|
||||
(-> selrect
|
||||
(update :width #(if (mth/almost-zero? %) 1 %))
|
||||
(update :height #(if (mth/almost-zero? %) 1 %)))))
|
||||
(concat [nil] content))]
|
||||
(gpr/points->selrect extremities)))
|
||||
|
||||
(defn move-content [content move-vec]
|
||||
(let [dx (:x move-vec)
|
||||
@ -376,40 +368,49 @@
|
||||
|
||||
set-tr
|
||||
(fn [params px py]
|
||||
(-> params
|
||||
(update px + dx)
|
||||
(update py + dy)))
|
||||
(cond-> params
|
||||
(d/num? dx)
|
||||
(update px + dx)
|
||||
|
||||
(d/num? dy)
|
||||
(update py + dy)))
|
||||
|
||||
transform-params
|
||||
(fn [{:keys [x c1x c2x] :as params}]
|
||||
(fn [{:keys [x y c1x c1y c2x c2y] :as params}]
|
||||
(cond-> params
|
||||
(some? x) (set-tr :x :y)
|
||||
(some? c1x) (set-tr :c1x :c1y)
|
||||
(some? c2x) (set-tr :c2x :c2y)))]
|
||||
(d/num? x y) (set-tr :x :y)
|
||||
(d/num? c1x c1y) (set-tr :c1x :c1y)
|
||||
(d/num? c2x c2y) (set-tr :c2x :c2y)))
|
||||
|
||||
(into []
|
||||
(map #(update % :params transform-params))
|
||||
content)))
|
||||
update-command
|
||||
(fn [command]
|
||||
(update command :params transform-params))]
|
||||
|
||||
(->> content
|
||||
(into [] (map update-command)))))
|
||||
|
||||
(defn transform-content
|
||||
[content transform]
|
||||
(let [set-tr (fn [params px py]
|
||||
(let [tr-point (-> (gpt/point (get params px) (get params py))
|
||||
(gpt/transform transform))]
|
||||
(assoc params
|
||||
px (:x tr-point)
|
||||
py (:y tr-point))))
|
||||
(if (some? transform)
|
||||
(let [set-tr
|
||||
(fn [params px py]
|
||||
(let [tr-point (-> (gpt/point (get params px) (get params py))
|
||||
(gpt/transform transform))]
|
||||
(assoc params
|
||||
px (:x tr-point)
|
||||
py (:y tr-point))))
|
||||
|
||||
transform-params
|
||||
(fn [{:keys [x c1x c2x] :as params}]
|
||||
(cond-> params
|
||||
(some? x) (set-tr :x :y)
|
||||
(some? c1x) (set-tr :c1x :c1y)
|
||||
(some? c2x) (set-tr :c2x :c2y)))]
|
||||
transform-params
|
||||
(fn [{:keys [x c1x c2x] :as params}]
|
||||
(cond-> params
|
||||
(some? x) (set-tr :x :y)
|
||||
(some? c1x) (set-tr :c1x :c1y)
|
||||
(some? c2x) (set-tr :c2x :c2y)))]
|
||||
|
||||
(into []
|
||||
(map #(update % :params transform-params))
|
||||
content)))
|
||||
(into []
|
||||
(map #(update % :params transform-params))
|
||||
content))
|
||||
content))
|
||||
|
||||
(defn segments->content
|
||||
([segments]
|
||||
@ -980,7 +981,6 @@
|
||||
(gpr/points->selrect))]
|
||||
[points selrect]))
|
||||
|
||||
|
||||
(defn open-path?
|
||||
[shape]
|
||||
|
||||
|
||||
@ -6,81 +6,101 @@
|
||||
|
||||
(ns app.common.geom.shapes.rect
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.math :as mth]))
|
||||
|
||||
(defn make-rect
|
||||
[x y width height]
|
||||
(when (d/num? x y width height)
|
||||
(let [width (max width 0.01)
|
||||
height (max height 0.01)]
|
||||
{:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height})))
|
||||
|
||||
(defn make-selrect
|
||||
[x y width height]
|
||||
(when (d/num? x y width height)
|
||||
(let [width (max width 0.01)
|
||||
height (max height 0.01)]
|
||||
{:x x
|
||||
:y y
|
||||
:x1 x
|
||||
:y1 y
|
||||
:x2 (+ x width)
|
||||
:y2 (+ y height)
|
||||
:width width
|
||||
:height height})))
|
||||
|
||||
(defn rect->points [{:keys [x y width height]}]
|
||||
;; (assert (number? x))
|
||||
;; (assert (number? y))
|
||||
;; (assert (and (number? width) (> width 0)))
|
||||
;; (assert (and (number? height) (> height 0)))
|
||||
[(gpt/point x y)
|
||||
(gpt/point (+ x width) y)
|
||||
(gpt/point (+ x width) (+ y height))
|
||||
(gpt/point x (+ y height))])
|
||||
(when (d/num? x y)
|
||||
(let [width (max width 0.01)
|
||||
height (max height 0.01)]
|
||||
[(gpt/point x y)
|
||||
(gpt/point (+ x width) y)
|
||||
(gpt/point (+ x width) (+ y height))
|
||||
(gpt/point x (+ y height))])))
|
||||
|
||||
(defn rect->lines [{:keys [x y width height]}]
|
||||
[[(gpt/point x y) (gpt/point (+ x width) y)]
|
||||
[(gpt/point (+ x width) y) (gpt/point (+ x width) (+ y height))]
|
||||
[(gpt/point (+ x width) (+ y height)) (gpt/point x (+ y height))]
|
||||
[(gpt/point x (+ y height)) (gpt/point x y)]])
|
||||
(when (d/num? x y)
|
||||
(let [width (max width 0.01)
|
||||
height (max height 0.01)]
|
||||
[[(gpt/point x y) (gpt/point (+ x width) y)]
|
||||
[(gpt/point (+ x width) y) (gpt/point (+ x width) (+ y height))]
|
||||
[(gpt/point (+ x width) (+ y height)) (gpt/point x (+ y height))]
|
||||
[(gpt/point x (+ y height)) (gpt/point x y)]])))
|
||||
|
||||
(defn points->rect
|
||||
[points]
|
||||
(let [minx (transduce gco/map-x-xf min ##Inf points)
|
||||
miny (transduce gco/map-y-xf min ##Inf points)
|
||||
maxx (transduce gco/map-x-xf max ##-Inf points)
|
||||
maxy (transduce gco/map-y-xf max ##-Inf points)]
|
||||
{:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)}))
|
||||
(when (d/not-empty? points)
|
||||
(let [minx (transduce (keep :x) min ##Inf points)
|
||||
miny (transduce (keep :y) min ##Inf points)
|
||||
maxx (transduce (keep :x) max ##-Inf points)
|
||||
maxy (transduce (keep :y) max ##-Inf points)]
|
||||
(when (d/num? minx miny maxx maxy)
|
||||
(make-rect minx miny (- maxx minx) (- maxy miny))))))
|
||||
|
||||
(defn points->selrect [points]
|
||||
(let [{:keys [x y width height] :as rect} (points->rect points)]
|
||||
(assoc rect
|
||||
:x1 x
|
||||
:x2 (+ x width)
|
||||
:y1 y
|
||||
:y2 (+ y height))))
|
||||
(when-let [rect (points->rect points)]
|
||||
(let [{:keys [x y width height]} rect]
|
||||
(make-selrect x y width height))))
|
||||
|
||||
(defn rect->selrect [rect]
|
||||
(-> rect rect->points points->selrect))
|
||||
|
||||
(defn join-rects [rects]
|
||||
(let [minx (transduce (comp (map :x) (remove nil?)) min ##Inf rects)
|
||||
miny (transduce (comp (map :y) (remove nil?)) min ##Inf rects)
|
||||
maxx (transduce (comp (map #(+ (:x %) (:width %))) (remove nil?)) max ##-Inf rects)
|
||||
maxy (transduce (comp (map #(+ (:y %) (:height %))) (remove nil?)) max ##-Inf rects)]
|
||||
{:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)}))
|
||||
(when (d/not-empty? rects)
|
||||
(let [minx (transduce (keep :x) min ##Inf rects)
|
||||
miny (transduce (keep :y) min ##Inf rects)
|
||||
maxx (transduce (keep #(when (and (:x %) (:width %)) (+ (:x %) (:width %)))) max ##-Inf rects)
|
||||
maxy (transduce (keep #(when (and (:y %) (:height %))(+ (:y %) (:height %)))) max ##-Inf rects)]
|
||||
(when (d/num? minx miny maxx maxy)
|
||||
(make-rect minx miny (- maxx minx) (- maxy miny))))))
|
||||
|
||||
(defn join-selrects [selrects]
|
||||
(let [minx (transduce (comp (map :x1) (remove nil?)) min ##Inf selrects)
|
||||
miny (transduce (comp (map :y1) (remove nil?)) min ##Inf selrects)
|
||||
maxx (transduce (comp (map :x2) (remove nil?)) max ##-Inf selrects)
|
||||
maxy (transduce (comp (map :y2) (remove nil?)) max ##-Inf selrects)]
|
||||
{:x minx
|
||||
:y miny
|
||||
:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)}))
|
||||
(when (d/not-empty? selrects)
|
||||
(let [minx (transduce (keep :x1) min ##Inf selrects)
|
||||
miny (transduce (keep :y1) min ##Inf selrects)
|
||||
maxx (transduce (keep :x2) max ##-Inf selrects)
|
||||
maxy (transduce (keep :y2) max ##-Inf selrects)]
|
||||
(when (d/num? minx miny maxx maxy)
|
||||
(make-selrect minx miny (- maxx minx) (- maxy miny))))))
|
||||
|
||||
(defn center->rect [center width height]
|
||||
(assert (gpt/point center))
|
||||
(assert (and (number? width) (> width 0)))
|
||||
(assert (and (number? height) (> height 0)))
|
||||
(defn center->rect [{:keys [x y]} width height]
|
||||
(when (d/num? x y width height)
|
||||
(make-rect (- x (/ width 2))
|
||||
(- y (/ height 2))
|
||||
width
|
||||
height)))
|
||||
|
||||
{:x (- (:x center) (/ width 2))
|
||||
:y (- (:y center) (/ height 2))
|
||||
:width width
|
||||
:height height})
|
||||
(defn center->selrect [{:keys [x y]} width height]
|
||||
(when (d/num? x y width height)
|
||||
(make-selrect (- x (/ width 2))
|
||||
(- y (/ height 2))
|
||||
width
|
||||
height)))
|
||||
|
||||
(defn s=
|
||||
[a b]
|
||||
@ -130,10 +150,3 @@
|
||||
(>= (:y1 sr2) (:y1 sr1))
|
||||
(<= (:y2 sr2) (:y2 sr1))))
|
||||
|
||||
(defn round-selrect
|
||||
[selrect]
|
||||
(-> selrect
|
||||
(update :x mth/round)
|
||||
(update :y mth/round)
|
||||
(update :width mth/round)
|
||||
(update :height mth/round)))
|
||||
|
||||
@ -21,38 +21,38 @@
|
||||
|
||||
;; --- Relative Movement
|
||||
|
||||
(defn- move-selrect [selrect pt]
|
||||
(when (and (some? selrect) (some? pt))
|
||||
(let [dx (.-x pt)
|
||||
dy (.-y pt)
|
||||
{:keys [x y x1 y1 x2 y2 width height]} selrect]
|
||||
{:x (if (some? x) (+ dx x) x)
|
||||
:y (if (some? y) (+ dy y) y)
|
||||
:x1 (if (some? x1) (+ dx x1) x1)
|
||||
:y1 (if (some? y1) (+ dy y1) y1)
|
||||
:x2 (if (some? x2) (+ dx x2) x2)
|
||||
:y2 (if (some? y2) (+ dy y2) y2)
|
||||
:width width
|
||||
:height height})))
|
||||
(defn- move-selrect [{:keys [x y x1 y1 x2 y2 width height] :as selrect} {dx :x dy :y :as pt}]
|
||||
(if (and (some? selrect) (some? pt) (d/num? dx dy))
|
||||
{:x (if (d/num? x) (+ dx x) x)
|
||||
:y (if (d/num? y) (+ dy y) y)
|
||||
:x1 (if (d/num? x1) (+ dx x1) x1)
|
||||
:y1 (if (d/num? y1) (+ dy y1) y1)
|
||||
:x2 (if (d/num? x2) (+ dx x2) x2)
|
||||
:y2 (if (d/num? y2) (+ dy y2) y2)
|
||||
:width width
|
||||
:height height}
|
||||
selrect))
|
||||
|
||||
(defn- move-points [points move-vec]
|
||||
(->> points
|
||||
(mapv #(gpt/add % move-vec))))
|
||||
(cond->> points
|
||||
(d/num? (:x move-vec) (:y move-vec))
|
||||
(mapv #(gpt/add % move-vec))))
|
||||
|
||||
(defn move-position-data
|
||||
[position-data dx dy]
|
||||
|
||||
(->> position-data
|
||||
(mapv #(-> %
|
||||
(update :x + dx)
|
||||
(update :y + dy)))))
|
||||
(cond->> position-data
|
||||
(d/num? dx dy)
|
||||
(mapv #(-> %
|
||||
(update :x + dx)
|
||||
(update :y + dy)))))
|
||||
|
||||
(defn move
|
||||
"Move the shape relatively to its current
|
||||
position applying the provided delta."
|
||||
[{:keys [type] :as shape} {dx :x dy :y}]
|
||||
(let [dx (d/check-num dx)
|
||||
dy (d/check-num dy)
|
||||
(let [dx (d/check-num dx 0)
|
||||
dy (d/check-num dy 0)
|
||||
move-vec (gpt/point dx dy)]
|
||||
|
||||
(-> shape
|
||||
@ -138,9 +138,12 @@
|
||||
(defn transform-matrix
|
||||
"Returns a transformation matrix without changing the shape properties.
|
||||
The result should be used in a `transform` attribute in svg"
|
||||
([shape] (transform-matrix shape nil))
|
||||
([shape params] (transform-matrix shape params (or (gco/center-shape shape)
|
||||
(gpt/point 0 0))))
|
||||
([shape]
|
||||
(transform-matrix shape nil))
|
||||
|
||||
([shape params]
|
||||
(transform-matrix shape params (or (gco/center-shape shape) (gpt/point 0 0))))
|
||||
|
||||
([{:keys [flip-x flip-y] :as shape} {:keys [no-flip]} shape-center]
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate shape-center)
|
||||
@ -168,12 +171,13 @@
|
||||
(defn transform-point-center
|
||||
"Transform a point around the shape center"
|
||||
[point center matrix]
|
||||
(when point
|
||||
(if (and (some? point) (some? matrix) (some? center))
|
||||
(gpt/transform
|
||||
point
|
||||
(gmt/multiply (gmt/translate-matrix center)
|
||||
matrix
|
||||
(gmt/translate-matrix (gpt/negate center))))))
|
||||
(gmt/translate-matrix (gpt/negate center))))
|
||||
point))
|
||||
|
||||
(defn transform-rect
|
||||
"Transform a rectangles and changes its attributes"
|
||||
@ -249,9 +253,9 @@
|
||||
|
||||
;; This rectangle is the new data for the current rectangle. We want to change our rectangle
|
||||
;; to have this width, height, x, y
|
||||
new-width (max 1 (:width points-temp-dim))
|
||||
new-height (max 1 (:height points-temp-dim))
|
||||
selrect (gco/make-centered-selrect center new-width new-height)
|
||||
new-width (max 0.01 (:width points-temp-dim))
|
||||
new-height (max 0.01 (:height points-temp-dim))
|
||||
selrect (gpr/center->selrect center new-width new-height)
|
||||
|
||||
rect-points (gpr/rect->points selrect)
|
||||
[matrix matrix-inverse] (calculate-adjust-matrix points-temp rect-points flip-x flip-y)]
|
||||
@ -263,7 +267,7 @@
|
||||
(defn- apply-transform
|
||||
"Given a new set of points transformed, set up the rectangle so it keeps
|
||||
its properties. We adjust de x,y,width,height and create a custom transform"
|
||||
[shape transform-mtx round-coords?]
|
||||
[shape transform-mtx]
|
||||
|
||||
(let [points' (:points shape)
|
||||
points (gco/transform-points points' transform-mtx)
|
||||
@ -276,10 +280,6 @@
|
||||
[(gpr/points->selrect points) nil nil]
|
||||
(adjust-rotated-transform shape points))
|
||||
|
||||
selrect (cond-> selrect
|
||||
round-coords? gpr/round-selrect)
|
||||
|
||||
;; Redondear los points?
|
||||
base-rotation (or (:rotation shape) 0)
|
||||
modif-rotation (or (get-in shape [:modifiers :rotation]) 0)
|
||||
rotation (mod (+ base-rotation modif-rotation) 360)]
|
||||
@ -296,8 +296,10 @@
|
||||
(assoc :transform-inverse transform-inverse)))
|
||||
(cond-> (not transform)
|
||||
(dissoc :transform :transform-inverse))
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points)
|
||||
(cond-> (some? selrect)
|
||||
(assoc :selrect selrect))
|
||||
(cond-> (d/not-empty? points)
|
||||
(assoc :points points))
|
||||
(assoc :rotation rotation))))
|
||||
|
||||
(defn- update-group-viewbox
|
||||
@ -345,7 +347,7 @@
|
||||
;; need to remove the flip flags
|
||||
(assoc :flip-x false)
|
||||
(assoc :flip-y false)
|
||||
(apply-transform (gmt/matrix) true))))
|
||||
(apply-transform (gmt/matrix)))))
|
||||
|
||||
(defn update-mask-selrect
|
||||
[masked-group children]
|
||||
@ -409,13 +411,14 @@
|
||||
width (:width new-size)
|
||||
height (:height new-size)
|
||||
|
||||
shape-transform (:transform shape (gmt/matrix))
|
||||
shape-transform-inv (:transform-inverse shape (gmt/matrix))
|
||||
shape-transform (:transform shape)
|
||||
shape-transform-inv (:transform-inverse shape)
|
||||
shape-center (gco/center-shape shape)
|
||||
{sr-width :width sr-height :height} (:selrect shape)
|
||||
|
||||
origin (-> (gpt/point (:selrect shape))
|
||||
(transform-point-center shape-center shape-transform))
|
||||
origin (cond-> (gpt/point (:selrect shape))
|
||||
(some? shape-transform)
|
||||
(transform-point-center shape-center shape-transform))
|
||||
|
||||
scalev (gpt/divide (gpt/point width height)
|
||||
(gpt/point sr-width sr-height))]
|
||||
@ -464,24 +467,28 @@
|
||||
(normalize-scale (:y resize-v2))))
|
||||
|
||||
|
||||
resize-transform (:resize-transform modifiers (gmt/matrix))
|
||||
resize-transform-inverse (:resize-transform-inverse modifiers (gmt/matrix))
|
||||
resize-transform (:resize-transform modifiers)
|
||||
resize-transform-inverse (:resize-transform-inverse modifiers)
|
||||
|
||||
rt-modif (:rotation modifiers)]
|
||||
|
||||
(cond-> (gmt/matrix)
|
||||
(some? resize-1)
|
||||
(-> (gmt/translate origin-1)
|
||||
(gmt/multiply resize-transform)
|
||||
(cond-> (some? resize-transform)
|
||||
(gmt/multiply resize-transform))
|
||||
(gmt/scale resize-1)
|
||||
(gmt/multiply resize-transform-inverse)
|
||||
(cond-> (some? resize-transform-inverse)
|
||||
(gmt/multiply resize-transform-inverse))
|
||||
(gmt/translate (gpt/negate origin-1)))
|
||||
|
||||
(some? resize-2)
|
||||
(-> (gmt/translate origin-2)
|
||||
(gmt/multiply resize-transform)
|
||||
(cond-> (some? resize-transform)
|
||||
(gmt/multiply resize-transform))
|
||||
(gmt/scale resize-2)
|
||||
(gmt/multiply resize-transform-inverse)
|
||||
(cond-> (some? resize-transform-inverse)
|
||||
(gmt/multiply resize-transform-inverse))
|
||||
(gmt/translate (gpt/negate origin-2)))
|
||||
|
||||
(some? displacement)
|
||||
@ -525,7 +532,6 @@
|
||||
(d/parse-double)
|
||||
(* (get-in modifiers [:resize-vector :x] 1))
|
||||
(* (get-in modifiers [:resize-vector-2 :x] 1))
|
||||
(mth/precision 2)
|
||||
(str))]
|
||||
(attrs/merge attrs {:font-size font-size})))]
|
||||
(update shape :content #(txt/transform-nodes
|
||||
@ -535,64 +541,54 @@
|
||||
shape))
|
||||
|
||||
(defn apply-modifiers
|
||||
[shape modifiers round-coords?]
|
||||
[shape modifiers]
|
||||
(let [center (gco/center-shape shape)
|
||||
transform (modifiers->transform center modifiers)]
|
||||
(apply-transform shape transform round-coords?)))
|
||||
(apply-transform shape transform)))
|
||||
|
||||
(defn transform-shape
|
||||
([shape]
|
||||
(transform-shape shape nil))
|
||||
[shape]
|
||||
(let [modifiers (:modifiers shape)]
|
||||
(cond
|
||||
(nil? modifiers)
|
||||
shape
|
||||
|
||||
([shape {:keys [round-coords?] :or {round-coords? true}}]
|
||||
(let [modifiers (:modifiers shape)]
|
||||
(cond
|
||||
(nil? modifiers)
|
||||
shape
|
||||
(empty-modifiers? modifiers)
|
||||
(dissoc shape :modifiers)
|
||||
|
||||
(empty-modifiers? modifiers)
|
||||
(dissoc shape :modifiers)
|
||||
:else
|
||||
(let [shape (apply-displacement shape)
|
||||
modifiers (:modifiers shape)]
|
||||
(cond-> shape
|
||||
(not (empty-modifiers? modifiers))
|
||||
(-> (set-flip modifiers)
|
||||
(apply-modifiers modifiers)
|
||||
(apply-text-resize modifiers))
|
||||
|
||||
:else
|
||||
(let [shape (apply-displacement shape)
|
||||
modifiers (:modifiers shape)]
|
||||
(cond-> shape
|
||||
(not (empty-modifiers? modifiers))
|
||||
(-> (set-flip modifiers)
|
||||
(apply-modifiers modifiers round-coords?)
|
||||
(apply-text-resize modifiers))
|
||||
:always
|
||||
(dissoc :modifiers))))))
|
||||
|
||||
:always
|
||||
(dissoc :modifiers)))))))
|
||||
|
||||
(defn transform-selrect
|
||||
[selrect {:keys [displacement resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}]
|
||||
(defn transform-bounds
|
||||
[points center {:keys [displacement resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}]
|
||||
;; FIXME: Improve Performance
|
||||
(let [resize-transform-inverse (or resize-transform-inverse (gmt/matrix))
|
||||
|
||||
displacement
|
||||
(when (some? displacement)
|
||||
(gmt/multiply resize-transform-inverse displacement)
|
||||
#_(-> (gpt/point 0 0)
|
||||
(gpt/transform displacement)
|
||||
(gpt/transform resize-transform-inverse)
|
||||
(gmt/translate-matrix)))
|
||||
(gmt/multiply resize-transform-inverse displacement))
|
||||
|
||||
resize-origin
|
||||
(when (some? resize-origin)
|
||||
(transform-point-center resize-origin (gco/center-selrect selrect) resize-transform-inverse))
|
||||
(transform-point-center resize-origin center resize-transform-inverse))
|
||||
|
||||
resize-origin-2
|
||||
(when (some? resize-origin-2)
|
||||
(transform-point-center resize-origin-2 (gco/center-selrect selrect) resize-transform-inverse))]
|
||||
(transform-point-center resize-origin-2 center resize-transform-inverse))]
|
||||
|
||||
(if (and (nil? displacement) (nil? resize-origin) (nil? resize-origin-2))
|
||||
selrect
|
||||
|
||||
(cond-> selrect
|
||||
:always
|
||||
(gpr/rect->points)
|
||||
points
|
||||
|
||||
(cond-> points
|
||||
(some? displacement)
|
||||
(gco/transform-points displacement)
|
||||
|
||||
@ -600,11 +596,15 @@
|
||||
(gco/transform-points resize-origin (gmt/scale-matrix resize-vector))
|
||||
|
||||
(some? resize-origin-2)
|
||||
(gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2))
|
||||
|
||||
:always
|
||||
(gpr/points->selrect)))))
|
||||
(gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2))))))
|
||||
|
||||
(defn transform-selrect
|
||||
[selrect modifiers]
|
||||
(let [center (gco/center-selrect selrect)]
|
||||
(-> selrect
|
||||
(gpr/rect->points)
|
||||
(transform-bounds center modifiers)
|
||||
(gpr/points->selrect))))
|
||||
|
||||
(defn selection-rect
|
||||
"Returns a rect that contains all the shapes and is aware of the
|
||||
|
||||
@ -106,6 +106,11 @@
|
||||
#?(:cljs (js/Math.round v)
|
||||
:clj (Math/round (float v))))
|
||||
|
||||
(defn half-round
|
||||
"Returns a value rounded to the next point or half point"
|
||||
[v]
|
||||
(/ (round (* v 2)) 2))
|
||||
|
||||
(defn ceil
|
||||
"Returns the smallest integer greater than
|
||||
or equal to a given number."
|
||||
@ -115,7 +120,7 @@
|
||||
|
||||
(defn precision
|
||||
[v n]
|
||||
(when (and (number? v) (number? n))
|
||||
(when (and (number? v) (integer? n))
|
||||
(let [d (pow 10 n)]
|
||||
(/ (round (* v d)) d))))
|
||||
|
||||
@ -165,3 +170,7 @@
|
||||
[v0 v1 t]
|
||||
(+ (* (- 1 t) v0)
|
||||
(* t v1)))
|
||||
|
||||
(defn max-abs
|
||||
[a b]
|
||||
(max (abs a) (abs b)))
|
||||
|
||||
@ -42,4 +42,5 @@
|
||||
(dm/export init/make-minimal-shape)
|
||||
(dm/export init/make-minimal-group)
|
||||
(dm/export init/empty-file-data)
|
||||
|
||||
(dm/export init/setup-shape)
|
||||
(dm/export init/setup-rect-selrect)
|
||||
|
||||
@ -104,6 +104,7 @@
|
||||
:group #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
|
||||
:constraints-h
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.common :refer [file-version default-color]]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
@ -91,8 +92,8 @@
|
||||
(def empty-selrect
|
||||
{:x 0 :y 0
|
||||
:x1 0 :y1 0
|
||||
:x2 1 :y2 1
|
||||
:width 1 :height 1})
|
||||
:x2 0.01 :y2 0.01
|
||||
:width 0.01 :height 0.01})
|
||||
|
||||
(defn make-minimal-shape
|
||||
[type]
|
||||
@ -111,16 +112,16 @@
|
||||
(not= :path (:type shape))
|
||||
(assoc :x 0
|
||||
:y 0
|
||||
:width 1
|
||||
:height 1
|
||||
:width 0.01
|
||||
:height 0.01
|
||||
:selrect {:x 0
|
||||
:y 0
|
||||
:x1 0
|
||||
:y1 0
|
||||
:x2 1
|
||||
:y2 1
|
||||
:width 1
|
||||
:height 1}))))
|
||||
:x2 0.01
|
||||
:y2 0.01
|
||||
:width 0.01
|
||||
:height 0.01}))))
|
||||
|
||||
(defn make-minimal-group
|
||||
[frame-id selection-rect group-name]
|
||||
@ -146,3 +147,40 @@
|
||||
(assoc :id file-id)
|
||||
(update :pages conj page-id)
|
||||
(update :pages-index assoc page-id pd)))))
|
||||
|
||||
(defn setup-rect-selrect
|
||||
"Initializes the selrect and points for a shape"
|
||||
[shape]
|
||||
(let [selrect (gsh/rect->selrect shape)
|
||||
points (gsh/rect->points shape)]
|
||||
(-> shape
|
||||
(assoc :selrect selrect
|
||||
:points points))))
|
||||
|
||||
(defn- setup-rect
|
||||
"A specialized function for setup rect-like shapes."
|
||||
[shape {:keys [x y width height]}]
|
||||
(-> shape
|
||||
(assoc :x x :y y :width width :height height)
|
||||
(setup-rect-selrect)))
|
||||
|
||||
(defn- setup-image
|
||||
[{:keys [metadata] :as shape} props]
|
||||
(-> (setup-rect shape props)
|
||||
(assoc
|
||||
:proportion (/ (:width metadata)
|
||||
(:height metadata))
|
||||
:proportion-lock true)))
|
||||
|
||||
(defn setup-shape
|
||||
"A function that initializes the first coordinates for
|
||||
the shape. Used mainly for draw operations."
|
||||
([props]
|
||||
(setup-shape {:type :rect} props))
|
||||
|
||||
([shape props]
|
||||
(case (:type shape)
|
||||
:image (setup-image shape props)
|
||||
(setup-rect shape props))))
|
||||
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
|
||||
(fix-empty-points [shape]
|
||||
(let [shape (cond-> shape
|
||||
(empty? (:selrect shape)) (gsh/setup-selrect))]
|
||||
(empty? (:selrect shape)) (cp/setup-rect-selrect))]
|
||||
(cond-> shape
|
||||
(empty? (:points shape))
|
||||
(assoc :points (gsh/rect->points (:selrect shape))))))
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :refer [close?]]
|
||||
[app.common.math :as mth :refer [close?]]
|
||||
[app.common.pages :refer [make-minimal-shape]]
|
||||
[clojure.test :as t]))
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
(t/testing "Shape without modifiers should stay the same"
|
||||
(t/are [type]
|
||||
(let [shape-before (create-test-shape type)
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(= shape-before shape-after))
|
||||
|
||||
:rect :path))
|
||||
@ -61,7 +61,7 @@
|
||||
(t/are [type]
|
||||
(let [modifiers {:displacement (gmt/translate-matrix (gpt/point 10 -10))}]
|
||||
(let [shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/is (not= shape-before shape-after))
|
||||
|
||||
(t/is (close? (get-in shape-before [:selrect :x])
|
||||
@ -82,7 +82,7 @@
|
||||
(t/are [type]
|
||||
(let [modifiers {:displacement (gmt/matrix)}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/are [prop]
|
||||
(t/is (close? (get-in shape-before [:selrect prop])
|
||||
(get-in shape-after [:selrect prop])))
|
||||
@ -95,7 +95,7 @@
|
||||
:resize-vector (gpt/point 2 2)
|
||||
:resize-transform (gmt/matrix)}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/is (not= shape-before shape-after))
|
||||
|
||||
(t/is (close? (get-in shape-before [:selrect :x])
|
||||
@ -117,7 +117,7 @@
|
||||
:resize-vector (gpt/point 1 1)
|
||||
:resize-transform (gmt/matrix)}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/are [prop]
|
||||
(t/is (close? (get-in shape-before [:selrect prop])
|
||||
(get-in shape-after [:selrect prop])))
|
||||
@ -130,7 +130,7 @@
|
||||
:resize-vector (gpt/point 0 0)
|
||||
:resize-transform (gmt/matrix)}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/is (> (get-in shape-before [:selrect :width])
|
||||
(get-in shape-after [:selrect :width])))
|
||||
(t/is (> (get-in shape-after [:selrect :width]) 0))
|
||||
@ -144,7 +144,7 @@
|
||||
(t/are [type]
|
||||
(let [modifiers {:rotation 30}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
|
||||
(t/is (not= shape-before shape-after))
|
||||
|
||||
@ -168,7 +168,7 @@
|
||||
(t/are [type]
|
||||
(let [modifiers {:rotation 0}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/are [prop]
|
||||
(t/is (close? (get-in shape-before [:selrect prop])
|
||||
(get-in shape-after [:selrect prop])))
|
||||
@ -180,7 +180,7 @@
|
||||
(let [modifiers {:displacement (gmt/matrix)}
|
||||
shape-before (-> (create-test-shape type {:modifiers modifiers})
|
||||
(assoc :selrect selrect))
|
||||
shape-after (gsh/transform-shape shape-before {:round-coords? false})]
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(= (:selrect shape-before)
|
||||
(:selrect shape-after)))
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
(ns app.common.geom-test
|
||||
(:require
|
||||
[clojure.test :as t]
|
||||
[app.common.math :as mth]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.matrix :as gmt]))
|
||||
|
||||
@ -67,7 +68,7 @@
|
||||
p2 (gpt/point 10 10)
|
||||
angle (gpt/angle-with-other p1 p2)]
|
||||
(t/is (number? angle))
|
||||
(t/is (= angle 45.0)))))
|
||||
(t/is (mth/close? angle 45.0)))))
|
||||
|
||||
(t/deftest matrix-constructors-test
|
||||
(let [m (gmt/matrix)]
|
||||
|
||||
@ -241,12 +241,13 @@ $height-palette-max: 80px;
|
||||
position: relative;
|
||||
|
||||
.viewport-overlays {
|
||||
cursor: initial;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
cursor: initial;
|
||||
|
||||
.pixel-overlay {
|
||||
height: 100%;
|
||||
|
||||
@ -395,8 +395,8 @@
|
||||
srect (gsh/selection-rect shapes)
|
||||
local (assoc local :vport size :zoom 1)]
|
||||
(cond
|
||||
(or (not (mth/finite? (:width srect)))
|
||||
(not (mth/finite? (:height srect))))
|
||||
(or (not (d/num? (:width srect)))
|
||||
(not (d/num? (:height srect))))
|
||||
(assoc local :vbox (assoc size :x 0 :y 0))
|
||||
|
||||
(or (> (:width srect) width)
|
||||
@ -1574,7 +1574,7 @@
|
||||
page-id (:current-page-id state)
|
||||
frame-id (-> (wsh/lookup-page-objects state page-id)
|
||||
(cph/frame-id-by-position @ms/mouse-position))
|
||||
shape (gsh/setup-selrect
|
||||
shape (cp/setup-rect-selrect
|
||||
{:id id
|
||||
:type :text
|
||||
:name "Text"
|
||||
@ -1667,7 +1667,7 @@
|
||||
shape (-> (cp/make-minimal-shape :frame)
|
||||
(merge {:x (:x srect) :y (:y srect) :width (:width srect) :height (:height srect)})
|
||||
(assoc :frame-id frame-id)
|
||||
(gsh/setup-selrect))]
|
||||
(cp/setup-rect-selrect))]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction)
|
||||
(dwc/add-shape shape)
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.main.data.workspace.comments
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.workspace :as dw]
|
||||
@ -78,8 +77,8 @@
|
||||
(fn [{:keys [vbox zoom] :as local}]
|
||||
(let [pw (/ 160 zoom)
|
||||
ph (/ 160 zoom)
|
||||
nw (mth/round (- (/ (:width vbox) 2) pw))
|
||||
nh (mth/round (- (/ (:height vbox) 2) ph))
|
||||
nw (- (/ (:width vbox) 2) pw)
|
||||
nh (- (/ (:height vbox) 2) ph)
|
||||
nx (- (:x position) nw)
|
||||
ny (- (:y position) nh)]
|
||||
(update local :vbox assoc :x nx :y ny)))))))
|
||||
|
||||
@ -480,7 +480,7 @@
|
||||
(merge data)
|
||||
(merge {:x x :y y})
|
||||
(assoc :frame-id frame-id)
|
||||
(gsh/setup-selrect))]
|
||||
(cp/setup-rect-selrect))]
|
||||
(rx/of (add-shape shape))))))
|
||||
|
||||
(defn image-uploaded
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.drawing.common :as common]
|
||||
@ -27,8 +28,8 @@
|
||||
shapev (gpt/point width height)
|
||||
deltav (gpt/to-vec initial point)
|
||||
scalev (-> (gpt/divide (gpt/add shapev deltav) shapev)
|
||||
(update :x truncate-zero 1)
|
||||
(update :y truncate-zero 1))
|
||||
(update :x truncate-zero 0.01)
|
||||
(update :y truncate-zero 0.01))
|
||||
scalev (if lock?
|
||||
(let [v (max (:x scalev) (:y scalev))]
|
||||
(gpt/point v v))
|
||||
@ -45,9 +46,7 @@
|
||||
(defn move-drawing
|
||||
[{:keys [x y]}]
|
||||
(fn [state]
|
||||
(let [x (mth/precision x 0)
|
||||
y (mth/precision y 0)]
|
||||
(update-in state [:workspace-drawing :object] gsh/absolute-move (gpt/point x y)))))
|
||||
(update-in state [:workspace-drawing :object] gsh/absolute-move (gpt/point x y))))
|
||||
|
||||
(defn handle-drawing-box []
|
||||
(ptk/reify ::handle-drawing-box
|
||||
@ -55,11 +54,14 @@
|
||||
(watch [_ state stream]
|
||||
(let [stoper? #(or (ms/mouse-up? %) (= % :interrupt))
|
||||
stoper (rx/filter stoper? stream)
|
||||
initial @ms/mouse-position
|
||||
layout (get state :workspace-layout)
|
||||
snap-pixel? (contains? layout :snap-pixel-grid)
|
||||
|
||||
initial (cond-> @ms/mouse-position
|
||||
snap-pixel? gpt/round)
|
||||
|
||||
page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
layout (get state :workspace-layout)
|
||||
focus (:workspace-focus-selected state)
|
||||
zoom (get-in state [:workspace-local :zoom] 1)
|
||||
|
||||
@ -72,7 +74,10 @@
|
||||
|
||||
shape (-> state
|
||||
(get-in [:workspace-drawing :object])
|
||||
(gsh/setup {:x (:x initial) :y (:y initial) :width 1 :height 1})
|
||||
(cp/setup-shape {:x (:x initial)
|
||||
:y (:y initial)
|
||||
:width 0.01
|
||||
:height 0.01})
|
||||
(assoc :frame-id fid)
|
||||
(assoc :initialized? true)
|
||||
(assoc :click-draw? true))]
|
||||
@ -85,7 +90,7 @@
|
||||
(rx/map move-drawing))
|
||||
|
||||
(->> ms/mouse-position
|
||||
(rx/filter #(> (gpt/distance % initial) 2))
|
||||
(rx/filter #(> (gpt/distance % initial) (/ 2 zoom)))
|
||||
(rx/with-latest vector ms/mouse-position-shift)
|
||||
(rx/switch-map
|
||||
(fn [[point :as current]]
|
||||
@ -93,7 +98,7 @@
|
||||
(rx/map #(conj current %)))))
|
||||
(rx/map
|
||||
(fn [[_ shift? point]]
|
||||
#(update-drawing % point shift?)))
|
||||
#(update-drawing % (cond-> point snap-pixel? gpt/round) shift?)))
|
||||
|
||||
(rx/take-until stoper))
|
||||
(rx/of common/handle-finish-drawing))))))
|
||||
|
||||
@ -6,8 +6,10 @@
|
||||
|
||||
(ns app.main.data.workspace.drawing.common
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages :as cp]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.worker :as uw]
|
||||
@ -29,27 +31,30 @@
|
||||
(rx/concat
|
||||
(when (:initialized? shape)
|
||||
(let [page-id (:current-page-id state)
|
||||
shape-click-width (case (:type shape)
|
||||
:text 3
|
||||
20)
|
||||
shape-click-height (case (:type shape)
|
||||
:text 16
|
||||
20)
|
||||
shape (if (:click-draw? shape)
|
||||
(-> shape
|
||||
(assoc-in [:modifiers :resize-vector]
|
||||
(gpt/point shape-click-width shape-click-height))
|
||||
(assoc-in [:modifiers :resize-origin]
|
||||
(gpt/point (:x shape) (:y shape))))
|
||||
shape)
|
||||
|
||||
shape (cond-> shape
|
||||
(= (:type shape) :text) (assoc :grow-type
|
||||
(if (:click-draw? shape) :auto-width :fixed)))
|
||||
click-draw? (:click-draw? shape)
|
||||
text? (= :text (:type shape))
|
||||
|
||||
shape (-> shape
|
||||
(gsh/transform-shape)
|
||||
(dissoc :initialized? :click-draw?))]
|
||||
min-side (min 100
|
||||
(mth/floor (get-in state [:workspace-local :vbox :width]))
|
||||
(mth/floor (get-in state [:workspace-local :vbox :height])))
|
||||
|
||||
shape
|
||||
(cond-> shape
|
||||
(and click-draw? (not text?))
|
||||
(-> (assoc :width min-side :height min-side)
|
||||
(assoc-in [:modifiers :displacement]
|
||||
(gmt/translate-matrix (- (/ min-side 2)) (- (/ min-side 2)))))
|
||||
|
||||
(and click-draw? text?)
|
||||
(assoc :height 17 :width 4 :grow-type :auto-width)
|
||||
|
||||
click-draw?
|
||||
(cp/setup-rect-selrect)
|
||||
|
||||
:always
|
||||
(-> (gsh/transform-shape)
|
||||
(dissoc :initialized? :click-draw?)))]
|
||||
;; Add & select the created shape to the workspace
|
||||
(rx/concat
|
||||
(if (= :text (:type shape))
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
|
||||
selrect (gsh/selection-rect shapes)
|
||||
group (-> (cp/make-minimal-group frame-id selrect gname)
|
||||
(gsh/setup selrect)
|
||||
(cp/setup-shape selrect)
|
||||
(assoc :shapes (mapv :id shapes)
|
||||
:parent-id parent-id
|
||||
:frame-id frame-id
|
||||
|
||||
@ -27,7 +27,9 @@
|
||||
:scale-text
|
||||
:dynamic-alignment
|
||||
:display-artboard-names
|
||||
:snap-guides})
|
||||
:snap-guides
|
||||
:show-pixel-grid
|
||||
:snap-pixel-grid})
|
||||
|
||||
(def presets
|
||||
{:assets
|
||||
@ -53,7 +55,9 @@
|
||||
:snap-grid
|
||||
:dynamic-alignment
|
||||
:display-artboard-names
|
||||
:snap-guides})
|
||||
:snap-guides
|
||||
:show-pixel-grid
|
||||
:snap-pixel-grid})
|
||||
|
||||
(def default-global
|
||||
{:options-mode :design})
|
||||
|
||||
@ -103,20 +103,21 @@
|
||||
|
||||
(defn handle-area-selection
|
||||
[shift?]
|
||||
(letfn [(valid-rect? [{width :width height :height}]
|
||||
(or (> width 10) (> height 10)))]
|
||||
(letfn [(valid-rect? [zoom {width :width height :height}]
|
||||
(or (> width (/ 10 zoom)) (> height (/ 10 zoom))))]
|
||||
|
||||
(ptk/reify ::handle-area-selection
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(let [stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event)))
|
||||
(watch [_ state stream]
|
||||
(let [zoom (get-in state [:workspace-local :zoom] 1)
|
||||
stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event)))
|
||||
stoper (->> stream (rx/filter stop?))
|
||||
from-p @ms/mouse-position]
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
(rx/take-until stoper)
|
||||
(rx/map #(gsh/points->rect [from-p %]))
|
||||
(rx/filter valid-rect?)
|
||||
(rx/filter (partial valid-rect? zoom))
|
||||
(rx/map update-area-selection))
|
||||
|
||||
(rx/of (select-node-area shift?)
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.path.state :as state]
|
||||
[app.main.snap :as snap]
|
||||
[app.main.store :as st]
|
||||
@ -18,6 +17,7 @@
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defonce drag-threshold 5)
|
||||
(def zoom-half-pixel-precision 8)
|
||||
|
||||
(defn dragging? [start zoom]
|
||||
(fn [current]
|
||||
@ -26,19 +26,37 @@
|
||||
(defn finish-edition? [event]
|
||||
(= (ptk/type event) :app.main.data.workspace.common/clear-edition-mode))
|
||||
|
||||
(defn to-pixel-snap [position]
|
||||
(let [zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
layout (get @st/state :workspace-layout)
|
||||
snap-pixel? (contains? layout :snap-pixel-grid)]
|
||||
|
||||
(cond
|
||||
(or (not snap-pixel?) (not (gpt/point? position)))
|
||||
position
|
||||
|
||||
(>= zoom zoom-half-pixel-precision)
|
||||
(gpt/half-round position)
|
||||
|
||||
:else
|
||||
(gpt/round position))))
|
||||
|
||||
(defn drag-stream
|
||||
([to-stream]
|
||||
(drag-stream to-stream (rx/empty)))
|
||||
|
||||
([to-stream not-drag-stream]
|
||||
(let [start @ms/mouse-position
|
||||
zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
mouse-up (->> st/stream (rx/filter #(or (finish-edition? %)
|
||||
(ms/mouse-up? %))))
|
||||
(let [zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
|
||||
start (-> @ms/mouse-position to-pixel-snap)
|
||||
mouse-up (->> st/stream
|
||||
(rx/filter #(or (finish-edition? %)
|
||||
(ms/mouse-up? %))))
|
||||
|
||||
position-stream
|
||||
(->> ms/mouse-position
|
||||
(rx/take-until mouse-up)
|
||||
(rx/map to-pixel-snap)
|
||||
(rx/filter (dragging? start zoom))
|
||||
(rx/take 1))]
|
||||
|
||||
@ -53,10 +71,6 @@
|
||||
(->> position-stream
|
||||
(rx/merge-map (fn [] to-stream)))))))
|
||||
|
||||
(defn to-dec [num]
|
||||
(let [k 50]
|
||||
(* (mth/floor (/ num k)) k)))
|
||||
|
||||
(defn move-points-stream
|
||||
[snap-toggled start-point selected-points points]
|
||||
|
||||
@ -73,6 +87,7 @@
|
||||
(gpt/add position snap))
|
||||
position))]
|
||||
(->> ms/mouse-position
|
||||
(rx/map to-pixel-snap)
|
||||
(rx/map check-path-snap))))
|
||||
|
||||
(defn get-angle [node handler opposite]
|
||||
@ -116,6 +131,7 @@
|
||||
(merge position (gpt/add position snap)))))
|
||||
position))]
|
||||
(->> ms/mouse-position
|
||||
(rx/map to-pixel-snap)
|
||||
(rx/with-latest merge (->> ms/mouse-position-shift (rx/map #(hash-map :shift? %))))
|
||||
(rx/with-latest merge (->> ms/mouse-position-alt (rx/map #(hash-map :alt? %))))
|
||||
(rx/map check-path-snap))))
|
||||
@ -136,6 +152,7 @@
|
||||
(rx/map snap/create-ranges))]
|
||||
|
||||
(->> ms/mouse-position
|
||||
(rx/map to-pixel-snap)
|
||||
(rx/with-latest vector ranges-stream)
|
||||
(rx/map (fn [[position ranges]]
|
||||
(if snap-toggled
|
||||
|
||||
@ -33,17 +33,17 @@
|
||||
;; Shortcuts format https://github.com/ccampbell/mousetrap
|
||||
|
||||
(def base-shortcuts
|
||||
{:toggle-layers {:tooltip (ds/alt "L")
|
||||
:command (ds/a-mod "l")
|
||||
:fn #(st/emit! (dw/go-to-layout :layers))}
|
||||
{:toggle-layers {:tooltip (ds/alt "L")
|
||||
:command (ds/a-mod "l")
|
||||
:fn #(st/emit! (dw/go-to-layout :layers))}
|
||||
|
||||
:toggle-assets {:tooltip (ds/alt "I")
|
||||
:command (ds/a-mod "i")
|
||||
:fn #(st/emit! (dw/go-to-layout :assets))}
|
||||
:toggle-assets {:tooltip (ds/alt "I")
|
||||
:command (ds/a-mod "i")
|
||||
:fn #(st/emit! (dw/go-to-layout :assets))}
|
||||
|
||||
:toggle-history {:tooltip (ds/alt "H")
|
||||
:command (ds/a-mod "h")
|
||||
:fn #(st/emit! (dw/go-to-layout :document-history))}
|
||||
:toggle-history {:tooltip (ds/alt "H")
|
||||
:command (ds/a-mod "h")
|
||||
:fn #(st/emit! (dw/go-to-layout :document-history))}
|
||||
|
||||
:toggle-colorpalette {:tooltip (ds/alt "P")
|
||||
:command (ds/a-mod "p")
|
||||
@ -57,250 +57,250 @@
|
||||
(st/emit! (dw/remove-layout-flag :colorpalette)
|
||||
(toggle-layout-flag :textpalette)))}
|
||||
|
||||
:toggle-rules {:tooltip (ds/meta-shift "R")
|
||||
:command (ds/c-mod "shift+r")
|
||||
:fn #(st/emit! (toggle-layout-flag :rules))}
|
||||
:toggle-rules {:tooltip (ds/meta-shift "R")
|
||||
:command (ds/c-mod "shift+r")
|
||||
:fn #(st/emit! (toggle-layout-flag :rules))}
|
||||
|
||||
:select-all {:tooltip (ds/meta "A")
|
||||
:command (ds/c-mod "a")
|
||||
:fn #(st/emit! (dw/select-all))}
|
||||
:select-all {:tooltip (ds/meta "A")
|
||||
:command (ds/c-mod "a")
|
||||
:fn #(st/emit! (dw/select-all))}
|
||||
|
||||
:toggle-grid {:tooltip (ds/meta "'")
|
||||
:command (ds/c-mod "'")
|
||||
:fn #(st/emit! (toggle-layout-flag :display-grid))}
|
||||
:toggle-grid {:tooltip (ds/meta "'")
|
||||
:command (ds/c-mod "'")
|
||||
:fn #(st/emit! (toggle-layout-flag :display-grid))}
|
||||
|
||||
:toggle-snap-grid {:tooltip (ds/meta-shift "'")
|
||||
:command (ds/c-mod "shift+'")
|
||||
:fn #(st/emit! (toggle-layout-flag :snap-grid))}
|
||||
:toggle-snap-grid {:tooltip (ds/meta-shift "'")
|
||||
:command (ds/c-mod "shift+'")
|
||||
:fn #(st/emit! (toggle-layout-flag :snap-grid))}
|
||||
|
||||
:toggle-snap-guide {:tooltip (ds/meta-shift "G")
|
||||
:command (ds/c-mod "shift+G")
|
||||
:fn #(st/emit! (toggle-layout-flag :snap-guides))}
|
||||
:toggle-snap-guide {:tooltip (ds/meta-shift "G")
|
||||
:command (ds/c-mod "shift+G")
|
||||
:fn #(st/emit! (toggle-layout-flag :snap-guides))}
|
||||
|
||||
:toggle-alignment {:tooltip (ds/meta "\\")
|
||||
:command (ds/c-mod "\\")
|
||||
:fn #(st/emit! (toggle-layout-flag :dynamic-alignment))}
|
||||
:toggle-alignment {:tooltip (ds/meta "\\")
|
||||
:command (ds/c-mod "\\")
|
||||
:fn #(st/emit! (toggle-layout-flag :dynamic-alignment))}
|
||||
|
||||
:toggle-scale-text {:tooltip "K"
|
||||
:command "k"
|
||||
:fn #(st/emit! (toggle-layout-flag :scale-text))}
|
||||
:toggle-scale-text {:tooltip "K"
|
||||
:command "k"
|
||||
:fn #(st/emit! (toggle-layout-flag :scale-text))}
|
||||
|
||||
:increase-zoom {:tooltip "+"
|
||||
:command ["+" "="]
|
||||
:fn #(st/emit! (dw/increase-zoom nil))}
|
||||
:increase-zoom {:tooltip "+"
|
||||
:command ["+" "="]
|
||||
:fn #(st/emit! (dw/increase-zoom nil))}
|
||||
|
||||
:decrease-zoom {:tooltip "-"
|
||||
:command ["-" "_"]
|
||||
:fn #(st/emit! (dw/decrease-zoom nil))}
|
||||
:decrease-zoom {:tooltip "-"
|
||||
:command ["-" "_"]
|
||||
:fn #(st/emit! (dw/decrease-zoom nil))}
|
||||
|
||||
:group {:tooltip (ds/meta "G")
|
||||
:command (ds/c-mod "g")
|
||||
:fn #(st/emit! dw/group-selected)}
|
||||
:group {:tooltip (ds/meta "G")
|
||||
:command (ds/c-mod "g")
|
||||
:fn #(st/emit! dw/group-selected)}
|
||||
|
||||
:ungroup {:tooltip (ds/shift "G")
|
||||
:command "shift+g"
|
||||
:fn #(st/emit! dw/ungroup-selected)}
|
||||
:ungroup {:tooltip (ds/shift "G")
|
||||
:command "shift+g"
|
||||
:fn #(st/emit! dw/ungroup-selected)}
|
||||
|
||||
:mask {:tooltip (ds/meta "M")
|
||||
:command (ds/c-mod "m")
|
||||
:fn #(st/emit! dw/mask-group)}
|
||||
:mask {:tooltip (ds/meta "M")
|
||||
:command (ds/c-mod "m")
|
||||
:fn #(st/emit! dw/mask-group)}
|
||||
|
||||
:unmask {:tooltip (ds/meta-shift "M")
|
||||
:command (ds/c-mod "shift+m")
|
||||
:fn #(st/emit! dw/unmask-group)}
|
||||
:unmask {:tooltip (ds/meta-shift "M")
|
||||
:command (ds/c-mod "shift+m")
|
||||
:fn #(st/emit! dw/unmask-group)}
|
||||
|
||||
:create-component {:tooltip (ds/meta "K")
|
||||
:command (ds/c-mod "k")
|
||||
:fn #(st/emit! (dwl/add-component))}
|
||||
:create-component {:tooltip (ds/meta "K")
|
||||
:command (ds/c-mod "k")
|
||||
:fn #(st/emit! (dwl/add-component))}
|
||||
|
||||
:detach-component {:tooltip (ds/meta-shift "K")
|
||||
:command (ds/c-mod "shift+k")
|
||||
:fn #(st/emit! dwl/detach-selected-components)}
|
||||
:detach-component {:tooltip (ds/meta-shift "K")
|
||||
:command (ds/c-mod "shift+k")
|
||||
:fn #(st/emit! dwl/detach-selected-components)}
|
||||
|
||||
:flip-vertical {:tooltip (ds/shift "V")
|
||||
:command "shift+v"
|
||||
:fn #(st/emit! (dw/flip-vertical-selected))}
|
||||
:flip-vertical {:tooltip (ds/shift "V")
|
||||
:command "shift+v"
|
||||
:fn #(st/emit! (dw/flip-vertical-selected))}
|
||||
|
||||
:flip-horizontal {:tooltip (ds/shift "H")
|
||||
:command "shift+h"
|
||||
:fn #(st/emit! (dw/flip-horizontal-selected))}
|
||||
:flip-horizontal {:tooltip (ds/shift "H")
|
||||
:command "shift+h"
|
||||
:fn #(st/emit! (dw/flip-horizontal-selected))}
|
||||
|
||||
:reset-zoom {:tooltip (ds/shift "0")
|
||||
:command "shift+0"
|
||||
:fn #(st/emit! dw/reset-zoom)}
|
||||
:reset-zoom {:tooltip (ds/shift "0")
|
||||
:command "shift+0"
|
||||
:fn #(st/emit! dw/reset-zoom)}
|
||||
|
||||
:fit-all {:tooltip (ds/shift "1")
|
||||
:command "shift+1"
|
||||
:fn #(st/emit! dw/zoom-to-fit-all)}
|
||||
:fit-all {:tooltip (ds/shift "1")
|
||||
:command "shift+1"
|
||||
:fn #(st/emit! dw/zoom-to-fit-all)}
|
||||
|
||||
:zoom-selected {:tooltip (ds/shift "2")
|
||||
:command "shift+2"
|
||||
:fn #(st/emit! dw/zoom-to-selected-shape)}
|
||||
:zoom-selected {:tooltip (ds/shift "2")
|
||||
:command "shift+2"
|
||||
:fn #(st/emit! dw/zoom-to-selected-shape)}
|
||||
|
||||
:duplicate {:tooltip (ds/meta "D")
|
||||
:command (ds/c-mod "d")
|
||||
:fn #(st/emit! (dw/duplicate-selected true))}
|
||||
:duplicate {:tooltip (ds/meta "D")
|
||||
:command (ds/c-mod "d")
|
||||
:fn #(st/emit! (dw/duplicate-selected true))}
|
||||
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:fn #(st/emit! dwc/undo)}
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:fn #(st/emit! dwc/undo)}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:fn #(st/emit! dwc/redo)}
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:fn #(st/emit! dwc/redo)}
|
||||
|
||||
:clear-undo {:tooltip (ds/meta "Q")
|
||||
:command (ds/c-mod "q")
|
||||
:fn #(st/emit! dwu/reinitialize-undo)}
|
||||
:clear-undo {:tooltip (ds/meta "Q")
|
||||
:command (ds/c-mod "q")
|
||||
:fn #(st/emit! dwu/reinitialize-undo)}
|
||||
|
||||
:draw-frame {:tooltip "A"
|
||||
:command "a"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :frame))}
|
||||
:draw-frame {:tooltip "A"
|
||||
:command "a"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :frame))}
|
||||
|
||||
:move {:tooltip "V"
|
||||
:command "v"
|
||||
:fn #(st/emit! :interrupt)}
|
||||
:move {:tooltip "V"
|
||||
:command "v"
|
||||
:fn #(st/emit! :interrupt)}
|
||||
|
||||
:draw-rect {:tooltip "R"
|
||||
:command "r"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :rect))}
|
||||
:draw-rect {:tooltip "R"
|
||||
:command "r"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :rect))}
|
||||
|
||||
:draw-ellipse {:tooltip "E"
|
||||
:command "e"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :circle))}
|
||||
:draw-ellipse {:tooltip "E"
|
||||
:command "e"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :circle))}
|
||||
|
||||
:draw-text {:tooltip "T"
|
||||
:command "t"
|
||||
:fn #(st/emit! dwtxt/start-edit-if-selected
|
||||
(dwd/select-for-drawing :text))}
|
||||
:draw-text {:tooltip "T"
|
||||
:command "t"
|
||||
:fn #(st/emit! dwtxt/start-edit-if-selected
|
||||
(dwd/select-for-drawing :text))}
|
||||
|
||||
:draw-path {:tooltip "P"
|
||||
:command "p"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :path))}
|
||||
:draw-path {:tooltip "P"
|
||||
:command "p"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :path))}
|
||||
|
||||
:draw-curve {:tooltip (ds/shift "C")
|
||||
:command "shift+c"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :curve))}
|
||||
:draw-curve {:tooltip (ds/shift "C")
|
||||
:command "shift+c"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :curve))}
|
||||
|
||||
:add-comment {:tooltip "C"
|
||||
:command "c"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :comments))}
|
||||
:add-comment {:tooltip "C"
|
||||
:command "c"
|
||||
:fn #(st/emit! (dwd/select-for-drawing :comments))}
|
||||
|
||||
:insert-image {:tooltip (ds/shift "K")
|
||||
:command "shift+k"
|
||||
:fn #(-> "image-upload" dom/get-element dom/click)}
|
||||
:insert-image {:tooltip (ds/shift "K")
|
||||
:command "shift+k"
|
||||
:fn #(-> "image-upload" dom/get-element dom/click)}
|
||||
|
||||
:copy {:tooltip (ds/meta "C")
|
||||
:command (ds/c-mod "c")
|
||||
:fn #(st/emit! (dw/copy-selected))}
|
||||
:copy {:tooltip (ds/meta "C")
|
||||
:command (ds/c-mod "c")
|
||||
:fn #(st/emit! (dw/copy-selected))}
|
||||
|
||||
:cut {:tooltip (ds/meta "X")
|
||||
:command (ds/c-mod "x")
|
||||
:fn #(st/emit! (dw/copy-selected)
|
||||
(dw/delete-selected))}
|
||||
:cut {:tooltip (ds/meta "X")
|
||||
:command (ds/c-mod "x")
|
||||
:fn #(st/emit! (dw/copy-selected)
|
||||
(dw/delete-selected))}
|
||||
|
||||
:paste {:tooltip (ds/meta "V")
|
||||
:disabled true
|
||||
:command (ds/c-mod "v")
|
||||
:fn (constantly nil)}
|
||||
:paste {:tooltip (ds/meta "V")
|
||||
:disabled true
|
||||
:command (ds/c-mod "v")
|
||||
:fn (constantly nil)}
|
||||
|
||||
:delete {:tooltip (ds/supr)
|
||||
:command ["del" "backspace"]
|
||||
:fn #(st/emit! (dw/delete-selected))}
|
||||
:delete {:tooltip (ds/supr)
|
||||
:command ["del" "backspace"]
|
||||
:fn #(st/emit! (dw/delete-selected))}
|
||||
|
||||
:bring-forward {:tooltip (ds/meta ds/up-arrow)
|
||||
:command (ds/c-mod "up")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :up))}
|
||||
:bring-forward {:tooltip (ds/meta ds/up-arrow)
|
||||
:command (ds/c-mod "up")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :up))}
|
||||
|
||||
:bring-backward {:tooltip (ds/meta ds/down-arrow)
|
||||
:command (ds/c-mod "down")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :down))}
|
||||
:bring-backward {:tooltip (ds/meta ds/down-arrow)
|
||||
:command (ds/c-mod "down")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :down))}
|
||||
|
||||
:bring-front {:tooltip (ds/meta-shift ds/up-arrow)
|
||||
:command (ds/c-mod "shift+up")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :top))}
|
||||
:bring-front {:tooltip (ds/meta-shift ds/up-arrow)
|
||||
:command (ds/c-mod "shift+up")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :top))}
|
||||
|
||||
:bring-back {:tooltip (ds/meta-shift ds/down-arrow)
|
||||
:command (ds/c-mod "shift+down")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :bottom))}
|
||||
:bring-back {:tooltip (ds/meta-shift ds/down-arrow)
|
||||
:command (ds/c-mod "shift+down")
|
||||
:fn #(st/emit! (dw/vertical-order-selected :bottom))}
|
||||
|
||||
:move-fast-up {:tooltip (ds/shift ds/up-arrow)
|
||||
:command "shift+up"
|
||||
:fn #(st/emit! (dwt/move-selected :up true))}
|
||||
:move-fast-up {:tooltip (ds/shift ds/up-arrow)
|
||||
:command "shift+up"
|
||||
:fn #(st/emit! (dwt/move-selected :up true))}
|
||||
|
||||
:move-fast-down {:tooltip (ds/shift ds/down-arrow)
|
||||
:command "shift+down"
|
||||
:fn #(st/emit! (dwt/move-selected :down true))}
|
||||
:move-fast-down {:tooltip (ds/shift ds/down-arrow)
|
||||
:command "shift+down"
|
||||
:fn #(st/emit! (dwt/move-selected :down true))}
|
||||
|
||||
:move-fast-right {:tooltip (ds/shift ds/right-arrow)
|
||||
:command "shift+right"
|
||||
:fn #(st/emit! (dwt/move-selected :right true))}
|
||||
:move-fast-right {:tooltip (ds/shift ds/right-arrow)
|
||||
:command "shift+right"
|
||||
:fn #(st/emit! (dwt/move-selected :right true))}
|
||||
|
||||
:move-fast-left {:tooltip (ds/shift ds/left-arrow)
|
||||
:command "shift+left"
|
||||
:fn #(st/emit! (dwt/move-selected :left true))}
|
||||
:move-fast-left {:tooltip (ds/shift ds/left-arrow)
|
||||
:command "shift+left"
|
||||
:fn #(st/emit! (dwt/move-selected :left true))}
|
||||
|
||||
:move-unit-up {:tooltip ds/up-arrow
|
||||
:command "up"
|
||||
:fn #(st/emit! (dwt/move-selected :up false))}
|
||||
:move-unit-up {:tooltip ds/up-arrow
|
||||
:command "up"
|
||||
:fn #(st/emit! (dwt/move-selected :up false))}
|
||||
|
||||
:move-unit-down {:tooltip ds/down-arrow
|
||||
:command "down"
|
||||
:fn #(st/emit! (dwt/move-selected :down false))}
|
||||
:move-unit-down {:tooltip ds/down-arrow
|
||||
:command "down"
|
||||
:fn #(st/emit! (dwt/move-selected :down false))}
|
||||
|
||||
:move-unit-left {:tooltip ds/right-arrow
|
||||
:command "right"
|
||||
:fn #(st/emit! (dwt/move-selected :right false))}
|
||||
:move-unit-left {:tooltip ds/right-arrow
|
||||
:command "right"
|
||||
:fn #(st/emit! (dwt/move-selected :right false))}
|
||||
|
||||
:move-unit-right {:tooltip ds/left-arrow
|
||||
:command "left"
|
||||
:fn #(st/emit! (dwt/move-selected :left false))}
|
||||
:move-unit-right {:tooltip ds/left-arrow
|
||||
:command "left"
|
||||
:fn #(st/emit! (dwt/move-selected :left false))}
|
||||
|
||||
:open-color-picker {:tooltip "I"
|
||||
:command "i"
|
||||
:fn #(st/emit! (mdc/picker-for-selected-shape))}
|
||||
:open-color-picker {:tooltip "I"
|
||||
:command "i"
|
||||
:fn #(st/emit! (mdc/picker-for-selected-shape))}
|
||||
|
||||
:open-viewer {:tooltip "G V"
|
||||
:command "g v"
|
||||
:fn #(st/emit! (dw/go-to-viewer))}
|
||||
:open-viewer {:tooltip "G V"
|
||||
:command "g v"
|
||||
:fn #(st/emit! (dw/go-to-viewer))}
|
||||
|
||||
:open-handoff {:tooltip "G H"
|
||||
:command "g h"
|
||||
:fn #(st/emit! (dw/go-to-viewer {:section :handoff}))}
|
||||
:open-handoff {:tooltip "G H"
|
||||
:command "g h"
|
||||
:fn #(st/emit! (dw/go-to-viewer {:section :handoff}))}
|
||||
|
||||
:open-comments {:tooltip "G C"
|
||||
:command "g c"
|
||||
:fn #(st/emit! (dw/go-to-viewer {:section :comments}))}
|
||||
:open-comments {:tooltip "G C"
|
||||
:command "g c"
|
||||
:fn #(st/emit! (dw/go-to-viewer {:section :comments}))}
|
||||
|
||||
:open-dashboard {:tooltip "G D"
|
||||
:command "g d"
|
||||
:fn #(st/emit! (dw/go-to-dashboard))}
|
||||
:open-dashboard {:tooltip "G D"
|
||||
:command "g d"
|
||||
:fn #(st/emit! (dw/go-to-dashboard))}
|
||||
|
||||
:escape {:tooltip (ds/esc)
|
||||
:command "escape"
|
||||
:fn #(st/emit! :interrupt (dw/deselect-all true))}
|
||||
:escape {:tooltip (ds/esc)
|
||||
:command "escape"
|
||||
:fn #(st/emit! :interrupt (dw/deselect-all true))}
|
||||
|
||||
:start-editing {:tooltip (ds/enter)
|
||||
:command "enter"
|
||||
:fn #(st/emit! (dw/start-editing-selected))}
|
||||
:start-editing {:tooltip (ds/enter)
|
||||
:command "enter"
|
||||
:fn #(st/emit! (dw/start-editing-selected))}
|
||||
|
||||
:start-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type "keydown"
|
||||
:fn #(st/emit! (dw/toggle-distances-display true))}
|
||||
:start-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type "keydown"
|
||||
:fn #(st/emit! (dw/toggle-distances-display true))}
|
||||
|
||||
:stop-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type "keyup"
|
||||
:fn #(st/emit! (dw/toggle-distances-display false))}
|
||||
:stop-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type "keyup"
|
||||
:fn #(st/emit! (dw/toggle-distances-display false))}
|
||||
|
||||
:bool-union {:tooltip (ds/meta (ds/alt "U"))
|
||||
:command (ds/c-mod "alt+u")
|
||||
:fn #(st/emit! (dw/create-bool :union))}
|
||||
:bool-union {:tooltip (ds/meta (ds/alt "U"))
|
||||
:command (ds/c-mod "alt+u")
|
||||
:fn #(st/emit! (dw/create-bool :union))}
|
||||
|
||||
:bool-difference {:tooltip (ds/meta (ds/alt "D"))
|
||||
:command (ds/c-mod "alt+d")
|
||||
:fn #(st/emit! (dw/create-bool :difference))}
|
||||
:bool-difference {:tooltip (ds/meta (ds/alt "D"))
|
||||
:command (ds/c-mod "alt+d")
|
||||
:fn #(st/emit! (dw/create-bool :difference))}
|
||||
|
||||
:bool-intersection {:tooltip (ds/meta (ds/alt "I"))
|
||||
:command (ds/c-mod "alt+i")
|
||||
@ -329,7 +329,7 @@
|
||||
:align-vcenter {:tooltip (ds/alt "V")
|
||||
:command "alt+v"
|
||||
:fn #(st/emit! (dw/align-objects :vcenter))}
|
||||
|
||||
|
||||
:align-bottom {:tooltip (ds/alt "S")
|
||||
:command "alt+s"
|
||||
:fn #(st/emit! (dw/align-objects :vbottom))}
|
||||
@ -354,9 +354,9 @@
|
||||
:command (ds/c-mod "alt+l")
|
||||
:fn #(st/emit! (dw/toggle-proportion-lock))}
|
||||
|
||||
:create-artboard-from-selection {:tooltip (ds/meta (ds/alt "G"))
|
||||
:command (ds/c-mod "alt+g")
|
||||
:fn #(st/emit! (dw/create-artboard-from-selection))}
|
||||
:artboard-selection {:tooltip (ds/meta (ds/alt "G"))
|
||||
:command (ds/c-mod "alt+g")
|
||||
:fn #(st/emit! (dw/create-artboard-from-selection))}
|
||||
|
||||
:hide-ui {:tooltip "\\"
|
||||
:command "\\"
|
||||
@ -368,8 +368,16 @@
|
||||
|
||||
:thumbnail-set {:tooltip (ds/shift "T")
|
||||
:command "shift+t"
|
||||
:fn #(st/emit! (dw/toggle-file-thumbnail-selected))}})
|
||||
:fn #(st/emit! (dw/toggle-file-thumbnail-selected))}
|
||||
|
||||
:show-pixel-grid {:tooltip (ds/shift ",")
|
||||
:command "shift+,"
|
||||
:fn #(st/emit! (toggle-layout-flag :show-pixel-grid))}
|
||||
|
||||
:snap-pixel-grid {:command ","
|
||||
:tooltip ","
|
||||
:fn #(st/emit! (toggle-layout-flag :snap-pixel-grid))}
|
||||
})
|
||||
|
||||
(def opacity-shortcuts
|
||||
(into {} (->>
|
||||
|
||||
@ -11,7 +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 :as cp]
|
||||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :refer [max-safe-int min-safe-int]]
|
||||
@ -33,9 +33,7 @@
|
||||
(defonce default-image {:x 0 :y 0 :width 1 :height 1 :rx 0 :ry 0})
|
||||
|
||||
(defn- assert-valid-num [attr num]
|
||||
(when (or (nil? num)
|
||||
(mth/nan? num)
|
||||
(not (mth/finite? num))
|
||||
(when (or (not (d/num? num))
|
||||
(>= num max-safe-int )
|
||||
(<= num min-safe-int))
|
||||
(ex/raise (str (d/name attr) " attribute invalid: " num)))
|
||||
@ -168,7 +166,7 @@
|
||||
(assoc :svg-attrs attrs)
|
||||
(assoc :svg-viewbox (-> (select-keys svg-data [:width :height])
|
||||
(assoc :x offset-x :y offset-y)))
|
||||
(gsh/setup-selrect))))
|
||||
(cp/setup-rect-selrect))))
|
||||
|
||||
(defn create-svg-root [frame-id svg-data]
|
||||
(let [{:keys [name x y width height offset-x offset-y]} svg-data]
|
||||
@ -180,7 +178,7 @@
|
||||
:height height
|
||||
:x (+ x offset-x)
|
||||
:y (+ y offset-y)}
|
||||
(gsh/setup-selrect)
|
||||
(cp/setup-rect-selrect)
|
||||
(assoc :svg-attrs (-> (:attrs svg-data)
|
||||
(dissoc :viewBox :xmlns)
|
||||
(d/without-keys usvg/inheritable-props))))))
|
||||
@ -200,7 +198,7 @@
|
||||
(assoc :svg-attrs (d/without-keys attrs usvg/inheritable-props))
|
||||
(assoc :svg-viewbox (-> (select-keys svg-data [:width :height])
|
||||
(assoc :x offset-x :y offset-y)))
|
||||
(gsh/setup-selrect))))
|
||||
(cp/setup-rect-selrect))))
|
||||
|
||||
(defn create-path-shape [name frame-id svg-data {:keys [attrs] :as data}]
|
||||
(when (and (contains? attrs :d) (seq (:d attrs)))
|
||||
@ -230,14 +228,9 @@
|
||||
(let [points (-> (gsh/rect->points rect-data)
|
||||
(gsh/transform-points transform))
|
||||
|
||||
center (gsh/center-points points)
|
||||
|
||||
rect-shape (-> (gsh/make-centered-rect center (:width rect-data) (:height rect-data))
|
||||
(update :width max 1)
|
||||
(update :height max 1))
|
||||
|
||||
selrect (gsh/rect->selrect rect-shape)
|
||||
|
||||
center (gsh/center-points points)
|
||||
rect-shape (gsh/center->rect center (:width rect-data) (:height rect-data))
|
||||
selrect (gsh/rect->selrect rect-shape)
|
||||
rect-points (gsh/rect->points rect-shape)
|
||||
|
||||
[shape-transform shape-transform-inv rotation]
|
||||
|
||||
@ -289,14 +289,6 @@
|
||||
|
||||
;; --- RESIZE UTILS
|
||||
|
||||
(defn update-overflow-text [id value]
|
||||
(ptk/reify ::update-overflow-text
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [page-id (:current-page-id state)]
|
||||
(update-in state [:workspace-data :pages-index page-id :objects id] assoc :overflow-text value)))))
|
||||
|
||||
|
||||
(def start-edit-if-selected
|
||||
(ptk/reify ::start-edit-if-selected
|
||||
ptk/UpdateEvent
|
||||
@ -325,22 +317,13 @@
|
||||
update-fn
|
||||
(fn [shape]
|
||||
(let [[new-width new-height] (get changes-map (:id shape))
|
||||
{:keys [selrect grow-type overflow-text]} (gsh/transform-shape shape)
|
||||
{:keys [selrect grow-type]} (gsh/transform-shape shape)
|
||||
{shape-width :width shape-height :height} selrect
|
||||
|
||||
modifier-width (gsh/resize-modifiers shape :width new-width)
|
||||
modifier-height (gsh/resize-modifiers shape :height new-height)]
|
||||
|
||||
(cond-> shape
|
||||
(and overflow-text (not= :fixed grow-type))
|
||||
(assoc :overflow-text false)
|
||||
|
||||
(and (= :fixed grow-type) (not overflow-text) (> new-height shape-height))
|
||||
(assoc :overflow-text true)
|
||||
|
||||
(and (= :fixed grow-type) overflow-text (<= new-height shape-height))
|
||||
(assoc :overflow-text false)
|
||||
|
||||
(and (not-changed? shape-width new-width) (= grow-type :auto-width))
|
||||
(-> (assoc :modifiers modifier-width)
|
||||
(gsh/transform-shape))
|
||||
|
||||
@ -114,23 +114,32 @@
|
||||
(declare get-ignore-tree)
|
||||
|
||||
(defn- set-modifiers
|
||||
([ids] (set-modifiers ids nil false))
|
||||
([ids modifiers] (set-modifiers ids modifiers false))
|
||||
([ids]
|
||||
(set-modifiers ids nil false))
|
||||
|
||||
([ids modifiers]
|
||||
(set-modifiers ids modifiers false))
|
||||
|
||||
([ids modifiers ignore-constraints]
|
||||
(set-modifiers ids modifiers ignore-constraints false))
|
||||
|
||||
([ids modifiers ignore-constraints ignore-snap-pixel]
|
||||
(us/verify (s/coll-of uuid?) ids)
|
||||
(ptk/reify ::set-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {}))
|
||||
page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
ids (into #{} (remove #(get-in objects [% :blocked] false)) ids)
|
||||
(let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {}))
|
||||
page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
ids (into #{} (remove #(get-in objects [% :blocked] false)) ids)
|
||||
layout (get state :workspace-layout)
|
||||
snap-pixel? (and (not ignore-snap-pixel) (contains? layout :snap-pixel-grid))
|
||||
|
||||
setup-modifiers
|
||||
(fn [state id]
|
||||
(let [shape (get objects id)]
|
||||
(update state :workspace-modifiers
|
||||
#(set-modifiers-recursive % objects shape modifiers ignore-constraints))))]
|
||||
#(set-modifiers-recursive % objects shape modifiers ignore-constraints snap-pixel?))))]
|
||||
|
||||
(reduce setup-modifiers state ids))))))
|
||||
|
||||
@ -181,9 +190,12 @@
|
||||
:ignore-tree ignore-tree
|
||||
;; Attributes that can change in the transform. This way we don't have to check
|
||||
;; all the attributes
|
||||
:attrs [:selrect :points
|
||||
:x :y
|
||||
:width :height
|
||||
:attrs [:selrect
|
||||
:points
|
||||
:x
|
||||
:y
|
||||
:width
|
||||
:height
|
||||
:content
|
||||
:transform
|
||||
:transform-inverse
|
||||
@ -232,9 +244,97 @@
|
||||
|
||||
[root transformed-root ignore-geometry?]))
|
||||
|
||||
(defn set-pixel-precision
|
||||
"Adjust modifiers so they adjust to the pixel grid"
|
||||
[modifiers shape]
|
||||
|
||||
(if (or (some? (:resize-transform modifiers))
|
||||
(some? (:resize-transform-2 modifiers)))
|
||||
;; If we're working with a rotation we don't handle pixel precision because
|
||||
;; the transformation won't have the precision anyway
|
||||
modifiers
|
||||
|
||||
(let [center (gsh/center-shape shape)
|
||||
base-bounds (-> (:points shape) (gsh/points->rect))
|
||||
|
||||
raw-bounds
|
||||
(-> (gsh/transform-bounds (:points shape) center modifiers)
|
||||
(gsh/points->rect))
|
||||
|
||||
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
|
||||
flip-y? (neg? (get-in modifiers [:resize-vector :y]))
|
||||
|
||||
path? (= :path (:type shape))
|
||||
vertical-line? (and path? (<= (:width raw-bounds) 0.01))
|
||||
horizontal-line? (and path? (<= (:height raw-bounds) 0.01))
|
||||
|
||||
target-width (if vertical-line?
|
||||
(:width raw-bounds)
|
||||
(max 1 (mth/round (:width raw-bounds))))
|
||||
|
||||
target-height (if horizontal-line?
|
||||
(:height raw-bounds)
|
||||
(max 1 (mth/round (:height raw-bounds))))
|
||||
|
||||
target-p (cond-> (gpt/round (gpt/point raw-bounds))
|
||||
flip-x?
|
||||
(update :x + target-width)
|
||||
|
||||
flip-y?
|
||||
(update :y + target-height))
|
||||
|
||||
ratio-width (/ target-width (:width raw-bounds))
|
||||
ratio-height (/ target-height (:height raw-bounds))
|
||||
|
||||
modifiers
|
||||
(-> modifiers
|
||||
(d/without-nils)
|
||||
(d/update-in-when
|
||||
[:resize-vector :x] #(* % ratio-width))
|
||||
|
||||
;; If the resize-vector-2 modifier arrives means the resize-vector
|
||||
;; will only resize on the x axis
|
||||
(cond-> (nil? (:resize-vector-2 modifiers))
|
||||
(d/update-in-when
|
||||
[:resize-vector :y] #(* % ratio-height)))
|
||||
|
||||
(d/update-in-when
|
||||
[:resize-vector-2 :y] #(* % ratio-height)))
|
||||
|
||||
origin (get modifiers :resize-origin)
|
||||
origin-2 (get modifiers :resize-origin-2)
|
||||
|
||||
resize-v (get modifiers :resize-vector)
|
||||
resize-v-2 (get modifiers :resize-vector-2)
|
||||
displacement (get modifiers :displacement)
|
||||
|
||||
target-p-inv
|
||||
(-> target-p
|
||||
(gpt/transform
|
||||
(cond-> (gmt/matrix)
|
||||
(some? displacement)
|
||||
(gmt/multiply (gmt/inverse displacement))
|
||||
|
||||
(and (some? resize-v) (some? origin))
|
||||
(gmt/scale (gpt/inverse resize-v) origin)
|
||||
|
||||
(and (some? resize-v-2) (some? origin-2))
|
||||
(gmt/scale (gpt/inverse resize-v-2) origin-2))))
|
||||
|
||||
delta-v (gpt/subtract target-p-inv (gpt/point base-bounds))
|
||||
|
||||
modifiers
|
||||
(-> modifiers
|
||||
(d/update-when :displacement #(gmt/multiply (gmt/translate-matrix delta-v) %))
|
||||
(cond-> (nil? (:displacement modifiers))
|
||||
(assoc :displacement (gmt/translate-matrix delta-v))))]
|
||||
modifiers)))
|
||||
|
||||
(defn- set-modifiers-recursive
|
||||
[modif-tree objects shape modifiers ignore-constraints]
|
||||
[modif-tree objects shape modifiers ignore-constraints snap-pixel?]
|
||||
|
||||
(let [children (map (d/getf objects) (:shapes shape))
|
||||
modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))
|
||||
transformed-rect (gsh/transform-selrect (:selrect shape) modifiers)
|
||||
|
||||
set-child
|
||||
@ -242,7 +342,7 @@
|
||||
(let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)]
|
||||
(cond-> modif-tree
|
||||
(not (gsh/empty-modifiers? child-modifiers))
|
||||
(set-modifiers-recursive objects child child-modifiers ignore-constraints))))
|
||||
(set-modifiers-recursive objects child child-modifiers ignore-constraints snap-pixel?))))
|
||||
|
||||
modif-tree
|
||||
(-> modif-tree
|
||||
@ -280,7 +380,6 @@
|
||||
(dissoc :workspace-modifiers)
|
||||
(update :workspace-local dissoc :current-move-selected)))))
|
||||
|
||||
|
||||
;; -- Resize --------------------------------------------------------
|
||||
|
||||
(defn start-resize
|
||||
@ -291,8 +390,8 @@
|
||||
{:keys [rotation]} shape
|
||||
|
||||
shape-center (gsh/center-shape shape)
|
||||
shape-transform (:transform shape (gmt/matrix))
|
||||
shape-transform-inverse (:transform-inverse shape (gmt/matrix))
|
||||
shape-transform (:transform shape)
|
||||
shape-transform-inverse (:transform-inverse shape)
|
||||
|
||||
rotation (or rotation 0)
|
||||
|
||||
@ -399,7 +498,8 @@
|
||||
(finish-transform))))))))
|
||||
|
||||
(defn update-dimensions
|
||||
"Change size of shapes, from the sideber options form."
|
||||
"Change size of shapes, from the sideber options form.
|
||||
Will ignore pixel snap used in the options side panel"
|
||||
[ids attr value]
|
||||
(us/verify (s/coll-of ::us/uuid) ids)
|
||||
(us/verify #{:width :height} attr)
|
||||
@ -408,15 +508,15 @@
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (get-in state [:workspace-data :pages-index page-id :objects])]
|
||||
objects (get-in state [:workspace-data :pages-index page-id :objects])
|
||||
|
||||
(reduce (fn [state id]
|
||||
(let [shape (get objects id)
|
||||
modifiers (gsh/resize-modifiers shape attr value)]
|
||||
(update state :workspace-modifiers
|
||||
#(set-modifiers-recursive % objects shape modifiers false))))
|
||||
state
|
||||
ids)))
|
||||
update-modifiers
|
||||
(fn [state id]
|
||||
(let [shape (get objects id)
|
||||
modifiers (gsh/resize-modifiers shape attr value)]
|
||||
(update state :workspace-modifiers
|
||||
#(set-modifiers-recursive % objects shape modifiers false false))))]
|
||||
(reduce update-modifiers state ids)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
@ -439,26 +539,29 @@
|
||||
group (gsh/selection-rect shapes)
|
||||
group-center (gsh/center-selrect group)
|
||||
initial-angle (gpt/angle @ms/mouse-position group-center)
|
||||
calculate-angle (fn [pos ctrl? shift?]
|
||||
(let [angle (- (gpt/angle pos group-center) initial-angle)
|
||||
angle (if (neg? angle) (+ 360 angle) angle)
|
||||
angle (if (= angle 360)
|
||||
0
|
||||
angle)
|
||||
angle (if ctrl?
|
||||
(* (mth/floor (/ angle 45)) 45)
|
||||
angle)
|
||||
angle (if shift?
|
||||
(* (mth/floor (/ angle 15)) 15)
|
||||
angle)]
|
||||
angle))]
|
||||
|
||||
calculate-angle
|
||||
(fn [pos ctrl? shift?]
|
||||
(let [angle (- (gpt/angle pos group-center) initial-angle)
|
||||
angle (if (neg? angle) (+ 360 angle) angle)
|
||||
angle (if (= angle 360)
|
||||
0
|
||||
angle)
|
||||
angle (if ctrl?
|
||||
(* (mth/floor (/ angle 45)) 45)
|
||||
angle)
|
||||
angle (if shift?
|
||||
(* (mth/floor (/ angle 15)) 15)
|
||||
angle)]
|
||||
angle))]
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||
(rx/with-latest vector ms/mouse-position-shift)
|
||||
(rx/map (fn [[[pos ctrl?] shift?]]
|
||||
(let [delta-angle (calculate-angle pos ctrl? shift?)]
|
||||
(set-rotation-modifiers delta-angle shapes group-center))))
|
||||
(rx/map
|
||||
(fn [[[pos ctrl?] shift?]]
|
||||
(let [delta-angle (calculate-angle pos ctrl? shift?)]
|
||||
(set-rotation-modifiers delta-angle shapes group-center))))
|
||||
(rx/take-until stoper))
|
||||
(rx/of (apply-modifiers (map :id shapes))
|
||||
(finish-transform)))))))
|
||||
@ -495,12 +598,13 @@
|
||||
(watch [_ state stream]
|
||||
(let [initial (deref ms/mouse-position)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
stopper (rx/filter ms/mouse-up? stream)]
|
||||
stopper (rx/filter ms/mouse-up? stream)
|
||||
zoom (get-in state [:workspace-local :zoom] 1)]
|
||||
(when-not (empty? selected)
|
||||
(->> ms/mouse-position
|
||||
(rx/map #(gpt/to-vec initial %))
|
||||
(rx/map #(gpt/length %))
|
||||
(rx/filter #(> % 1))
|
||||
(rx/filter #(> % (/ 10 zoom)))
|
||||
(rx/take 1)
|
||||
(rx/with-latest vector ms/mouse-position-alt)
|
||||
(rx/mapcat
|
||||
@ -574,7 +678,9 @@
|
||||
(rx/of (finish-transform))
|
||||
(rx/concat
|
||||
(->> position
|
||||
;; We ask for the snap position but we continue even if the result is not available
|
||||
(rx/with-latest vector snap-delta)
|
||||
;; We try to use the previous snap so we don't have to wait for the result of the new
|
||||
(rx/map snap/correct-snap-point)
|
||||
(rx/map #(hash-map :displacement (gmt/translate-matrix %)))
|
||||
(rx/map (partial set-modifiers ids))
|
||||
@ -654,8 +760,10 @@
|
||||
cpos (gpt/point (:x bbox) (:y bbox))
|
||||
pos (gpt/point (or (:x position) (:x bbox))
|
||||
(or (:y position) (:y bbox)))
|
||||
displ (gmt/translate-matrix (gpt/subtract pos cpos))]
|
||||
(rx/of (set-modifiers [id] {:displacement displ})
|
||||
delta (gpt/subtract pos cpos)
|
||||
displ (gmt/translate-matrix delta)]
|
||||
|
||||
(rx/of (set-modifiers [id] {:displacement displ} false true)
|
||||
(apply-modifiers [id]))))))
|
||||
|
||||
(defn- calculate-frame-for-move
|
||||
|
||||
@ -141,6 +141,9 @@
|
||||
(def workspace-layout
|
||||
(l/derived :workspace-layout st/state))
|
||||
|
||||
(def snap-pixel?
|
||||
(l/derived #(contains? % :snap-pixel-grid) workspace-layout))
|
||||
|
||||
(def workspace-file
|
||||
"A ref to a striped vision of file (without data)."
|
||||
(l/derived (fn [state]
|
||||
|
||||
@ -59,15 +59,14 @@
|
||||
(defn- calculate-dimensions
|
||||
[{:keys [objects] :as data} vport]
|
||||
(let [shapes (cph/get-immediate-children objects)
|
||||
to-finite (fn [val fallback] (if (not (mth/finite? val)) fallback val))
|
||||
rect (cond->> (gsh/selection-rect shapes)
|
||||
(some? vport)
|
||||
(gal/adjust-to-viewport vport))]
|
||||
(-> rect
|
||||
(update :x to-finite 0)
|
||||
(update :y to-finite 0)
|
||||
(update :width to-finite 100000)
|
||||
(update :height to-finite 100000))))
|
||||
(update :x mth/finite 0)
|
||||
(update :y mth/finite 0)
|
||||
(update :width mth/finite 100000)
|
||||
(update :height mth/finite 100000))))
|
||||
|
||||
(declare shape-wrapper-factory)
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
(def ^:const snap-accuracy 10)
|
||||
(def ^:const snap-path-accuracy 10)
|
||||
(def ^:const snap-distance-accuracy 10)
|
||||
(def ^:const snap-distance-accuracy 20)
|
||||
|
||||
(defn- remove-from-snap-points
|
||||
[remove-snap?]
|
||||
@ -55,10 +55,6 @@
|
||||
(and (d/not-empty? focus)
|
||||
(not (cp/is-in-focus? objects focus id)))))))
|
||||
|
||||
(defn- flatten-to-points
|
||||
[query-result]
|
||||
(mapcat (fn [[_ data]] (map :pt data)) query-result))
|
||||
|
||||
(defn- calculate-distance [query-result point coord]
|
||||
(->> query-result
|
||||
(map (fn [[value _]] [(mth/abs (- value (coord point))) [(coord point) value]]))))
|
||||
@ -82,16 +78,15 @@
|
||||
;; Otherwise the root frame is the common
|
||||
:else zero)))
|
||||
|
||||
(defn get-snap-points [page-id frame-id remove-snap? point coord]
|
||||
(defn get-snap-points [page-id frame-id remove-snap? zoom point coord]
|
||||
(let [value (get point coord)]
|
||||
(->> (uw/ask! {:cmd :snaps/range-query
|
||||
:page-id page-id
|
||||
:frame-id frame-id
|
||||
:axis coord
|
||||
:ranges [[(- value 0.5) (+ value 0.5)]]})
|
||||
:ranges [[(- value (/ 0.5 zoom)) (+ value (/ 0.5 zoom))]]})
|
||||
(rx/take 1)
|
||||
(rx/map (remove-from-snap-points remove-snap?))
|
||||
(rx/map flatten-to-points))))
|
||||
(rx/map (remove-from-snap-points remove-snap?)))))
|
||||
|
||||
(defn- search-snap
|
||||
[page-id frame-id points coord remove-snap? zoom]
|
||||
@ -195,7 +190,7 @@
|
||||
snap-list (d/concat-vec lt-snap gt-snap between-snap)
|
||||
min-snap (reduce best-snap ##Inf snap-list)]
|
||||
|
||||
(if (mth/finite? min-snap) [0 min-snap] nil)))
|
||||
(if (d/num? min-snap) [0 min-snap] nil)))
|
||||
|
||||
(defn search-snap-distance [selrect coord shapes-lt shapes-gt zoom]
|
||||
(->> (rx/combine-latest shapes-lt shapes-gt)
|
||||
@ -238,25 +233,35 @@
|
||||
(rx/map #(or % (gpt/point 0 0)))
|
||||
(rx/map #(gpt/add point %)))))
|
||||
|
||||
(defn combine-snaps-points
|
||||
([] nil)
|
||||
([p1] p1)
|
||||
([p1 p2]
|
||||
(cond
|
||||
(nil? p2) p1
|
||||
(nil? p1) p2
|
||||
|
||||
:else
|
||||
(gpt/point (mth/max-abs (:x p1) (:x p2))
|
||||
(mth/max-abs (:y p1) (:y p2))))))
|
||||
|
||||
(defn closest-snap-move
|
||||
[page-id shapes objects layout zoom focus movev]
|
||||
(let [frame-id (snap-frame-id shapes)
|
||||
filter-shapes (into #{} (map :id shapes))
|
||||
remove-snap? (make-remove-snap layout filter-shapes objects focus)
|
||||
|
||||
shape (if (> (count shapes) 1)
|
||||
(->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect}))
|
||||
(->> shapes (first)))
|
||||
snap-points
|
||||
(->> shapes
|
||||
(gsh/selection-rect)
|
||||
(sp/selrect-snap-points)
|
||||
;; Move the points in the translation vector
|
||||
(map #(gpt/add % movev)))]
|
||||
|
||||
shapes-points (->> shape
|
||||
(sp/shape-snap-points)
|
||||
;; Move the points in the translation vector
|
||||
(map #(gpt/add % movev)))]
|
||||
|
||||
(->> (rx/merge (closest-snap page-id frame-id shapes-points remove-snap? zoom)
|
||||
(->> (rx/merge (closest-snap page-id frame-id snap-points remove-snap? zoom)
|
||||
(when (contains? layout :dynamic-alignment)
|
||||
(closest-distance-snap page-id shapes objects zoom movev)))
|
||||
(rx/reduce gpt/min)
|
||||
(rx/reduce combine-snaps-points)
|
||||
(rx/map #(or % (gpt/point 0 0))))))
|
||||
|
||||
|
||||
@ -359,9 +364,14 @@
|
||||
0)
|
||||
dy (if (not= 0 (:y snap-delta))
|
||||
(- (+ (:y snap-pos) (:y snap-delta)) (:y position))
|
||||
0)]
|
||||
0)
|
||||
|
||||
;; If the deltas (dx,dy) are bigger than the snap-accuracy means the stored snap
|
||||
;; is not valid, so we change to 0
|
||||
dx (if (> (mth/abs dx) snap-accuracy) 0 dx)
|
||||
dy (if (> (mth/abs dy) snap-accuracy) 0 dy)]
|
||||
(-> position
|
||||
(update :x + dx)
|
||||
(update :y + dy)))
|
||||
|
||||
(cond-> position
|
||||
(<= (mth/abs dx) snap-accuracy) (update :x + dx)
|
||||
(<= (mth/abs dy) snap-accuracy) (update :y + dy)))
|
||||
position))
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
(ns app.main.ui.components.editable-select
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as math]
|
||||
[app.common.math :as mth]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.icons :as i]
|
||||
@ -28,10 +28,6 @@
|
||||
min-val (get params :min)
|
||||
max-val (get params :max)
|
||||
|
||||
num? (fn [val] (and (number? val)
|
||||
(not (math/nan? val))
|
||||
(math/finite? val)))
|
||||
|
||||
emit-blur? (mf/use-ref nil)
|
||||
font-size-wrapper-ref (mf/use-ref)
|
||||
|
||||
@ -58,8 +54,7 @@
|
||||
handle-change-input
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value)
|
||||
value (-> (or (d/parse-double value) value)
|
||||
(math/precision 2))]
|
||||
value (or (d/parse-double value) value)]
|
||||
(set-value value)))
|
||||
|
||||
on-node-load
|
||||
@ -89,8 +84,7 @@
|
||||
(when (or up? down?)
|
||||
(dom/prevent-default event)
|
||||
(let [value (-> event dom/get-target dom/get-value)
|
||||
value (-> (or (d/parse-double value) value)
|
||||
(math/precision 2))
|
||||
value (or (d/parse-double value) value)
|
||||
|
||||
increment (cond
|
||||
(kbd/shift? event)
|
||||
@ -102,12 +96,11 @@
|
||||
:else
|
||||
(if up? 1 -1))
|
||||
|
||||
new-value (-> (+ value increment)
|
||||
(math/precision 2))
|
||||
new-value (+ value increment)
|
||||
|
||||
new-value (cond
|
||||
(and (num? min-val) (< new-value min-val)) min-val
|
||||
(and (num? max-val) (> new-value max-val)) max-val
|
||||
(and (d/num? min-val) (< new-value min-val)) min-val
|
||||
(and (d/num? max-val) (> new-value max-val)) max-val
|
||||
:else new-value)]
|
||||
|
||||
(set-value new-value)))))))
|
||||
@ -135,7 +128,7 @@
|
||||
(let [wrapper-node (mf/ref-val font-size-wrapper-ref)
|
||||
node (dom/get-element-by-class "checked-element is-selected" wrapper-node)
|
||||
nodes (dom/get-elements-by-class "checked-element-value" wrapper-node)
|
||||
closest (fn [a b] (first (sort-by #(math/abs (- % b)) a)))
|
||||
closest (fn [a b] (first (sort-by #(mth/abs (- % b)) a)))
|
||||
closest-value (str (closest options value))]
|
||||
(when (:is-open? @state)
|
||||
(if (some? node)
|
||||
|
||||
@ -7,23 +7,17 @@
|
||||
(ns app.main.ui.components.numeric-input
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as math]
|
||||
[app.common.spec :as us]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :as globals]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.simple-math :as sm]
|
||||
[app.util.strings :as ust]
|
||||
[goog.events :as events]
|
||||
[rumext.alpha :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(defn num? [val]
|
||||
(and (number? val)
|
||||
(not (math/nan? val))
|
||||
(math/finite? val)))
|
||||
|
||||
(mf/defc numeric-input
|
||||
{::mf/wrap-props false
|
||||
::mf/forward-ref true}
|
||||
@ -36,8 +30,8 @@
|
||||
on-change (obj/get props "onChange")
|
||||
on-blur (obj/get props "onBlur")
|
||||
title (obj/get props "title")
|
||||
default-val (obj/get props "default" 0)
|
||||
precision (obj/get props "precision")
|
||||
default-val (obj/get props "default")
|
||||
nillable (obj/get props "nillable")
|
||||
|
||||
;; We need a ref pointing to the input dom element, but the user
|
||||
;; of this component may provide one (that is forwarded here).
|
||||
@ -78,34 +72,34 @@
|
||||
|
||||
parse-value
|
||||
(mf/use-callback
|
||||
(mf/deps ref min-val max-val value)
|
||||
(mf/deps ref min-val max-val value nillable default-val)
|
||||
(fn []
|
||||
(let [input-node (mf/ref-val ref)
|
||||
new-value (-> (dom/get-value input-node)
|
||||
(sm/expr-eval value))]
|
||||
(when (num? new-value)
|
||||
(cond
|
||||
(d/num? new-value)
|
||||
(-> new-value
|
||||
(cond-> (number? precision)
|
||||
(math/precision precision))
|
||||
(cond-> (nil? precision)
|
||||
(math/round))
|
||||
(cljs.core/max us/min-safe-int)
|
||||
(cljs.core/min us/max-safe-int)
|
||||
(cond->
|
||||
(num? min-val)
|
||||
(d/num? min-val)
|
||||
(cljs.core/max min-val)
|
||||
|
||||
(num? max-val)
|
||||
(cljs.core/min max-val)))))))
|
||||
(d/num? max-val)
|
||||
(cljs.core/min max-val)))
|
||||
|
||||
nillable
|
||||
default-val
|
||||
|
||||
:else value))))
|
||||
|
||||
update-input
|
||||
(mf/use-callback
|
||||
(mf/deps ref)
|
||||
(fn [new-value]
|
||||
(let [input-node (mf/ref-val ref)]
|
||||
(dom/set-value! input-node (if (some? precision)
|
||||
(ust/format-precision new-value precision)
|
||||
(str new-value))))))
|
||||
(dom/set-value! input-node (fmt/format-number new-value)))))
|
||||
|
||||
apply-value
|
||||
(mf/use-callback
|
||||
@ -122,24 +116,30 @@
|
||||
(fn [event up? down?]
|
||||
(let [current-value (parse-value)]
|
||||
(when current-value
|
||||
(let [increment (if (kbd/shift? event)
|
||||
(let [increment (cond
|
||||
(kbd/shift? event)
|
||||
(if up? (* step-val 10) (* step-val -10))
|
||||
|
||||
(kbd/alt? event)
|
||||
(if up? (* step-val 0.1) (* step-val -0.1))
|
||||
|
||||
:else
|
||||
(if up? step-val (- step-val)))
|
||||
|
||||
new-value (+ current-value increment)
|
||||
new-value (cond
|
||||
(and wrap-value? (num? max-val) (num? min-val)
|
||||
(and wrap-value? (d/num? max-val min-val)
|
||||
(> new-value max-val) up?)
|
||||
(-> new-value (- max-val) (+ min-val) (- step-val))
|
||||
|
||||
(and wrap-value? (num? min-val) (num? max-val)
|
||||
(and wrap-value? (d/num? max-val min-val)
|
||||
(< new-value min-val) down?)
|
||||
(-> new-value (- min-val) (+ max-val) (+ step-val))
|
||||
|
||||
(and (num? min-val) (< new-value min-val))
|
||||
(and (d/num? min-val) (< new-value min-val))
|
||||
min-val
|
||||
|
||||
(and (num? max-val) (> new-value max-val))
|
||||
(and (d/num? max-val) (> new-value max-val))
|
||||
max-val
|
||||
|
||||
:else new-value)]
|
||||
@ -167,14 +167,19 @@
|
||||
(mf/use-callback
|
||||
(mf/deps set-delta)
|
||||
(fn [event]
|
||||
(set-delta event (< (.-deltaY event) 0) (> (.-deltaY event) 0))))
|
||||
(let [input-node (mf/ref-val ref)]
|
||||
(when (dom/active? input-node)
|
||||
(let [event (.getBrowserEvent ^js event)]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(set-delta event (< (.-deltaY event) 0) (> (.-deltaY event) 0)))))))
|
||||
|
||||
handle-blur
|
||||
(mf/use-callback
|
||||
(mf/deps parse-value apply-value update-input on-blur)
|
||||
(fn [_]
|
||||
(let [new-value (or (parse-value) default-val)]
|
||||
(if new-value
|
||||
(if (or nillable new-value)
|
||||
(apply-value new-value)
|
||||
(update-input new-value)))
|
||||
(when on-blur (on-blur))))
|
||||
@ -189,21 +194,20 @@
|
||||
(dom/blur! current)))))))
|
||||
|
||||
props (-> props
|
||||
(obj/without ["value" "onChange"])
|
||||
(obj/without ["value" "onChange" "nillable"])
|
||||
(obj/set! "className" "input-text")
|
||||
(obj/set! "type" "text")
|
||||
(obj/set! "ref" ref)
|
||||
(obj/set! "defaultValue" value-str)
|
||||
(obj/set! "defaultValue" (fmt/format-number value))
|
||||
(obj/set! "title" title)
|
||||
(obj/set! "onWheel" handle-mouse-wheel)
|
||||
(obj/set! "onKeyDown" handle-key-down)
|
||||
(obj/set! "onBlur" handle-blur))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps value-str)
|
||||
(mf/deps value)
|
||||
(fn []
|
||||
(when-let [input-node (mf/ref-val ref)]
|
||||
(dom/set-value! input-node value-str))))
|
||||
(dom/set-value! input-node (fmt/format-number value)))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps handle-blur)
|
||||
@ -216,6 +220,13 @@
|
||||
(let [handle-blur (:fn (mf/ref-val handle-blur-ref))]
|
||||
(handle-blur)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps handle-mouse-wheel)
|
||||
(fn []
|
||||
(let [keys [(events/listen (mf/ref-val ref) EventType.WHEEL handle-mouse-wheel #js {:pasive false})]]
|
||||
#(doseq [key keys]
|
||||
(events/unlistenByKey key)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(fn []
|
||||
(let [keys [(events/listen globals/window EventType.POINTERDOWN on-click)
|
||||
@ -225,4 +236,3 @@
|
||||
(events/unlistenByKey key)))))
|
||||
|
||||
[:> :input props]))
|
||||
|
||||
|
||||
43
frontend/src/app/main/ui/formats.cljs
Normal file
43
frontend/src/app/main/ui/formats.cljs
Normal file
@ -0,0 +1,43 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.formats
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.math :as mth]))
|
||||
|
||||
(defn format-percent
|
||||
([value]
|
||||
(format-percent value nil))
|
||||
|
||||
([value {:keys [precision] :or {precision 2}}]
|
||||
(when (d/num? value)
|
||||
(let [percent-val (mth/precision (* value 100) precision)]
|
||||
(dm/str percent-val "%")))))
|
||||
|
||||
(defn format-number
|
||||
([value]
|
||||
(format-number value nil))
|
||||
([value {:keys [precision] :or {precision 2}}]
|
||||
(when (d/num? value)
|
||||
(let [value (mth/precision value precision)]
|
||||
(dm/str value)))))
|
||||
|
||||
(defn format-pixels
|
||||
([value]
|
||||
(format-pixels value nil))
|
||||
|
||||
([value {:keys [precision] :or {precision 2}}]
|
||||
(when (d/num? value)
|
||||
(let [value (mth/precision value precision)]
|
||||
(dm/str value "px")))))
|
||||
|
||||
(defn format-int
|
||||
[value]
|
||||
(when (d/num? value)
|
||||
(let [value (mth/precision value 0)]
|
||||
(dm/str value))))
|
||||
@ -12,6 +12,7 @@
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
;; ------------------------------------------------
|
||||
@ -97,7 +98,7 @@
|
||||
|
||||
(mf/defc size-display [{:keys [selrect zoom]}]
|
||||
(let [{:keys [x y width height]} selrect
|
||||
size-label (dm/str (mth/round width) " x " (mth/round height))
|
||||
size-label (dm/str (fmt/format-number width) " x " (fmt/format-number height))
|
||||
|
||||
rect-height (/ size-display-height zoom)
|
||||
rect-width (/ (if (<= (count size-label) 9)
|
||||
@ -164,7 +165,7 @@
|
||||
:height distance-pill-height
|
||||
:style {:fill distance-text-color
|
||||
:font-size font-size}}
|
||||
distance]]))
|
||||
(fmt/format-pixels distance)]]))
|
||||
|
||||
(mf/defc selection-rect [{:keys [selrect zoom]}]
|
||||
(let [{:keys [x y width height]} selrect
|
||||
@ -214,7 +215,7 @@
|
||||
{:x center-x
|
||||
:y center-y
|
||||
:zoom zoom
|
||||
:distance (dm/str (mth/round distance) "px")
|
||||
:distance distance
|
||||
:bounds bounds}]])))))
|
||||
|
||||
(mf/defc selection-guides [{:keys [bounds selrect zoom]}]
|
||||
|
||||
@ -71,8 +71,8 @@
|
||||
font-variant-id (:font-variant-id data)
|
||||
|
||||
font-size (:font-size data)
|
||||
fill-color (:fill-color data)
|
||||
fill-opacity (:fill-opacity data)
|
||||
fill-color (or (-> data :fills first :fill-color) (:fill-color data))
|
||||
fill-opacity (or (-> data :fills first :fill-opacity) (:fill-opacity data))
|
||||
|
||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||
text-color (when (and (some? fill-color) (some? fill-opacity))
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]]
|
||||
@ -24,6 +25,7 @@
|
||||
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
{:keys [x y width height position-data] :as shape} (obj/get props "shape")
|
||||
|
||||
transform (str (gsh/transform-matrix shape))
|
||||
|
||||
;; These position attributes are not really necesary but they are convenient for for the export
|
||||
@ -48,8 +50,8 @@
|
||||
|
||||
[:> :g group-props
|
||||
(for [[index data] (d/enumerate position-data)]
|
||||
(let [props (-> #js {:x (:x data)
|
||||
:y (:y data)
|
||||
(let [props (-> #js {:x (mth/round (:x data))
|
||||
:y (mth/round (:y data))
|
||||
:dominantBaseline "ideographic"
|
||||
:style (-> #js {:fontFamily (:font-family data)
|
||||
:fontSize (:font-size data)
|
||||
@ -61,7 +63,6 @@
|
||||
:whiteSpace "pre"}
|
||||
(obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))})
|
||||
shape (assoc shape :fills (:fills data))]
|
||||
|
||||
[:& shape-custom-strokes {:shape shape}
|
||||
[:> :text props (:text data)]]))]]))
|
||||
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.common
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
@ -50,9 +49,9 @@
|
||||
(if (:gradient color)
|
||||
[:& color-name {:color color}]
|
||||
(case format
|
||||
:rgba (let [[r g b a] (->> (uc/hex->rgba (:color color) (:opacity color)) (map #(mth/precision % 2)))]
|
||||
:rgba (let [[r g b a] (uc/hex->rgba (:color color) (:opacity color))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
:hsla (let [[h s l a] (->> (uc/hex->hsla (:color color) (:opacity color)) (map #(mth/precision % 2)))]
|
||||
:hsla (let [[h s l a] (uc/hex->hsla (:color color) (:opacity color))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" h s l a)])
|
||||
[:*
|
||||
[:& color-name {:color color}]
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.layout
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.common.spec.radius :as ctr]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.util.code-gen :as cg]
|
||||
@ -39,46 +38,46 @@
|
||||
[:*
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.width")]
|
||||
[:div.attributes-value (mth/precision width 2) "px"]
|
||||
[:div.attributes-value width "px"]
|
||||
[:& copy-button {:data (copy-data selrect :width)}]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.height")]
|
||||
[:div.attributes-value (mth/precision height 2) "px"]
|
||||
[:div.attributes-value height "px"]
|
||||
[:& copy-button {:data (copy-data selrect :height)}]]
|
||||
|
||||
(when (not= (:x shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.left")]
|
||||
[:div.attributes-value (mth/precision x 2) "px"]
|
||||
[:div.attributes-value x "px"]
|
||||
[:& copy-button {:data (copy-data selrect :x)}]])
|
||||
|
||||
(when (not= (:y shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.top")]
|
||||
[:div.attributes-value (mth/precision y 2) "px"]
|
||||
[:div.attributes-value y "px"]
|
||||
[:& copy-button {:data (copy-data selrect :y)}]])
|
||||
|
||||
(when (ctr/radius-1? shape)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.radius")]
|
||||
[:div.attributes-value (mth/precision (:rx shape 0) 2) "px"]
|
||||
[:div.attributes-value (:rx shape 0) "px"]
|
||||
[:& copy-button {:data (copy-data shape :rx)}]])
|
||||
|
||||
(when (ctr/radius-4? shape)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.radius")]
|
||||
[:div.attributes-value
|
||||
(mth/precision (:r1 shape) 2) ", "
|
||||
(mth/precision (:r2 shape) 2) ", "
|
||||
(mth/precision (:r3 shape) 2) ", "
|
||||
(mth/precision (:r4 shape) 2) "px"]
|
||||
(:r1 shape) ", "
|
||||
(:r2 shape) ", "
|
||||
(:r3 shape) ", "
|
||||
(:r4 shape) "px"]
|
||||
[:& copy-button {:data (copy-data shape :r1)}]])
|
||||
|
||||
(when (not= (:rotation shape 0) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.rotation")]
|
||||
[:div.attributes-value (mth/precision (:rotation shape) 2) "deg"]
|
||||
[:div.attributes-value (:rotation shape) "deg"]
|
||||
[:& copy-button {:data (copy-data shape :rotation)}]])]))
|
||||
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.viewer.handoff.attributes.stroke
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [color-row]]
|
||||
[app.util.code-gen :as cg]
|
||||
@ -67,7 +66,7 @@
|
||||
stroke-alignment (or stroke-alignment :center)]
|
||||
[:div.attributes-stroke-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.stroke.width")]
|
||||
[:div.attributes-value (mth/precision (:stroke-width shape) 2) "px"]
|
||||
[:div.attributes-value (:stroke-width shape) "px"]
|
||||
[:div.attributes-value (->> stroke-style d/name (str "handoff.attributes.stroke.style.") (t locale))]
|
||||
[:div.attributes-label (->> stroke-alignment d/name (str "handoff.attributes.stroke.alignment.") (t locale))]
|
||||
[:& copy-button {:data (copy-stroke-data shape)}]])]))
|
||||
|
||||
@ -6,16 +6,12 @@
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.svg
|
||||
(:require
|
||||
#_[app.common.math :as mth]
|
||||
#_[app.main.ui.icons :as i]
|
||||
#_[app.util.code-gen :as cg]
|
||||
[app.common.data :as d]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
||||
(defn map->css [attr]
|
||||
(->> attr
|
||||
(map (fn [[attr-key attr-value]] (str (d/name attr-key) ":" attr-value)))
|
||||
|
||||
@ -6,13 +6,13 @@
|
||||
|
||||
(ns app.main.ui.viewer.header
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.viewer :as dv]
|
||||
[app.main.data.viewer.shortcuts :as sc]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.comments :refer [comments-menu]]
|
||||
[app.main.ui.viewer.interactions :refer [flows-menu interactions-menu]]
|
||||
@ -32,7 +32,7 @@
|
||||
:as props}]
|
||||
(let [show-dropdown? (mf/use-state false)]
|
||||
[:div.zoom-widget {:on-click #(reset! show-dropdown? true)}
|
||||
[:span.label {} (str (mth/round (* 100 zoom)) "%")]
|
||||
[:span.label (fmt/format-percent zoom)]
|
||||
[:span.icon i/arrow-down]
|
||||
[:& dropdown {:show @show-dropdown?
|
||||
:on-close #(reset! show-dropdown? false)}
|
||||
@ -43,7 +43,7 @@
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(on-decrease))} "-"]
|
||||
[:p.zoom-size {} (str (mth/round (* 100 zoom)) "%")]
|
||||
[:p.zoom-size (fmt/format-percent zoom)]
|
||||
[:button {:on-click (fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.main.ui.workspace.colorpalette
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@ -57,7 +56,7 @@
|
||||
(let [state (mf/use-state {:show-menu false})
|
||||
|
||||
width (:width @state 0)
|
||||
visible (mth/round (/ width 66))
|
||||
visible (/ width 66)
|
||||
|
||||
offset (:offset @state 0)
|
||||
max-offset (- (count current-colors)
|
||||
@ -75,7 +74,7 @@
|
||||
(swap! state update :offset
|
||||
(fn [offset]
|
||||
(if (pos? offset)
|
||||
(max (- offset (mth/round (/ visible 2))) 0)
|
||||
(max (- offset (/ visible 2)) 0)
|
||||
offset)))))
|
||||
|
||||
on-right-arrow-click
|
||||
@ -85,7 +84,7 @@
|
||||
(swap! state update :offset
|
||||
(fn [offset]
|
||||
(if (< offset max-offset)
|
||||
(min max-offset (+ offset (mth/round (/ visible 2))))
|
||||
(min max-offset (+ offset (/ visible 2)))
|
||||
offset)))))
|
||||
|
||||
on-scroll
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
(ns app.main.ui.workspace.colorpicker.color-inputs
|
||||
(:require
|
||||
[app.common.math :as math]
|
||||
[app.common.math :as mth]
|
||||
[app.util.color :as uc]
|
||||
[app.util.dom :as dom]
|
||||
[rumext.alpha :as mf]))
|
||||
@ -52,7 +52,7 @@
|
||||
on-change-property
|
||||
(fn [property max-value]
|
||||
(fn [e]
|
||||
(let [val (-> e dom/get-target-val (math/clamp 0 max-value))
|
||||
(let [val (-> e dom/get-target-val (mth/clamp 0 max-value))
|
||||
val (if (#{:s} property) (/ val 100) val)]
|
||||
(when (not (nil? val))
|
||||
(if (#{:r :g :b} property)
|
||||
@ -72,7 +72,7 @@
|
||||
|
||||
on-change-opacity
|
||||
(fn [e]
|
||||
(when-let [new-alpha (-> e dom/get-target-val (math/clamp 0 100) (/ 100))]
|
||||
(when-let [new-alpha (-> e dom/get-target-val (mth/clamp 0 100) (/ 100))]
|
||||
(on-change {:alpha new-alpha})))]
|
||||
|
||||
|
||||
@ -86,9 +86,9 @@
|
||||
(when (and property-val property-ref)
|
||||
(when-let [node (mf/ref-val property-ref)]
|
||||
(case ref-key
|
||||
(:s :alpha) (dom/set-value! node (math/round (* property-val 100)))
|
||||
(:s :alpha) (dom/set-value! node (* property-val 100))
|
||||
:hex (dom/set-value! node property-val)
|
||||
(dom/set-value! node (math/round property-val)))))))))
|
||||
(dom/set-value! node property-val))))))))
|
||||
|
||||
[:div.color-values
|
||||
{:class (when disable-opacity "disable-opacity")}
|
||||
@ -156,7 +156,7 @@
|
||||
:min 0
|
||||
:step 1
|
||||
:max 100
|
||||
:default-value (if (= alpha :multiple) "" (math/precision alpha 2))
|
||||
:default-value (if (= alpha :multiple) "" alpha)
|
||||
:on-change on-change-opacity}])
|
||||
|
||||
[:label.hex-label {:for "hex-value"} "HEX"]
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
(ns app.main.ui.workspace.colorpicker.harmony
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as math]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]]
|
||||
[app.util.color :as uc]
|
||||
[app.util.dom :as dom]
|
||||
@ -28,9 +28,9 @@
|
||||
(.clearRect ctx 0 0 width height)
|
||||
|
||||
(doseq [degrees (range 0 360 step)]
|
||||
(let [degrees-rad (math/radians degrees)
|
||||
x (* radius (math/cos (- degrees-rad)))
|
||||
y (* radius (math/sin (- degrees-rad)))]
|
||||
(let [degrees-rad (mth/radians degrees)
|
||||
x (* radius (mth/cos (- degrees-rad)))
|
||||
y (* radius (mth/sin (- degrees-rad)))]
|
||||
(obj/set! ctx "strokeStyle" (str/format "hsl(%s, 100%, 50%)" degrees))
|
||||
(.beginPath ctx)
|
||||
(.moveTo ctx cx cy)
|
||||
@ -43,15 +43,15 @@
|
||||
(obj/set! ctx "fillStyle" grd)
|
||||
|
||||
(.beginPath ctx)
|
||||
(.arc ctx cx cy radius 0 (* 2 math/PI) true)
|
||||
(.arc ctx cx cy radius 0 (* 2 mth/PI) true)
|
||||
(.closePath ctx)
|
||||
(.fill ctx))))
|
||||
|
||||
(defn color->point
|
||||
[canvas-side hue saturation]
|
||||
(let [hue-rad (math/radians (- hue))
|
||||
comp-x (* saturation (math/cos hue-rad))
|
||||
comp-y (* saturation (math/sin hue-rad))
|
||||
(let [hue-rad (mth/radians (- hue))
|
||||
comp-x (* saturation (mth/cos hue-rad))
|
||||
comp-y (* saturation (mth/sin hue-rad))
|
||||
x (+ (/ canvas-side 2) (* comp-x (/ canvas-side 2)))
|
||||
y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))]
|
||||
(gpt/point x y)))
|
||||
@ -68,15 +68,15 @@
|
||||
calculate-pos (fn [ev]
|
||||
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
||||
{:keys [x y]} (-> ev dom/get-client-position)
|
||||
px (math/clamp (/ (- x left) (- right left)) 0 1)
|
||||
py (math/clamp (/ (- y top) (- bottom top)) 0 1)
|
||||
px (mth/clamp (/ (- x left) (- right left)) 0 1)
|
||||
py (mth/clamp (/ (- y top) (- bottom top)) 0 1)
|
||||
|
||||
px (- (* 2 px) 1)
|
||||
py (- (* 2 py) 1)
|
||||
|
||||
angle (math/degrees (math/atan2 px py))
|
||||
new-hue (math/precision (mod (- angle 90 ) 360) 2)
|
||||
new-saturation (math/clamp (math/distance [px py] [0 0]) 0 1)
|
||||
angle (mth/degrees (mth/atan2 px py))
|
||||
new-hue (mod (- angle 90 ) 360)
|
||||
new-saturation (mth/clamp (mth/distance [px py] [0 0]) 0 1)
|
||||
hex (uc/hsv->hex [new-hue new-saturation value])
|
||||
[r g b] (uc/hex->rgb hex)]
|
||||
(on-change {:hex hex
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
(ns app.main.ui.workspace.colorpicker.ramp
|
||||
(:require
|
||||
[app.common.math :as math]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||
[app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]]
|
||||
[app.util.color :as uc]
|
||||
@ -19,8 +19,8 @@
|
||||
(fn [ev]
|
||||
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
||||
{:keys [x y]} (-> ev dom/get-client-position)
|
||||
px (math/clamp (/ (- x left) (- right left)) 0 1)
|
||||
py (* 255 (- 1 (math/clamp (/ (- y top) (- bottom top)) 0 1)))]
|
||||
px (mth/clamp (/ (- x left) (- right left)) 0 1)
|
||||
py (* 255 (- 1 (mth/clamp (/ (- y top) (- bottom top)) 0 1)))]
|
||||
(on-change px py)))
|
||||
|
||||
handle-start-drag
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
(ns app.main.ui.workspace.colorpicker.slider-selector
|
||||
(:require
|
||||
[app.common.math :as math]
|
||||
[app.common.math :as mth]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
@ -41,13 +41,13 @@
|
||||
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
|
||||
{:keys [x y]} (-> ev dom/get-client-position)
|
||||
unit-value (if vertical?
|
||||
(math/clamp (/ (- bottom y) (- bottom top)) 0 1)
|
||||
(math/clamp (/ (- x left) (- right left)) 0 1))
|
||||
(mth/clamp (/ (- bottom y) (- bottom top)) 0 1)
|
||||
(mth/clamp (/ (- x left) (- right left)) 0 1))
|
||||
unit-value (if reverse?
|
||||
(math/abs (- unit-value 1.0))
|
||||
(mth/abs (- unit-value 1.0))
|
||||
unit-value)
|
||||
value (+ min-value (* unit-value (- max-value min-value)))]
|
||||
(on-change (math/precision value 2)))))]
|
||||
(on-change value))))]
|
||||
|
||||
[:div.slider-selector
|
||||
{:class (str (if vertical? "vertical " "") class)
|
||||
@ -60,7 +60,7 @@
|
||||
(- max-value min-value)) 100)
|
||||
|
||||
value-percent (if reverse?
|
||||
(math/abs (- value-percent 100))
|
||||
(mth/abs (- value-percent 100))
|
||||
value-percent)
|
||||
value-percent-str (str value-percent "%")
|
||||
|
||||
|
||||
@ -225,7 +225,7 @@
|
||||
(when (not has-frame?)
|
||||
[:*
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection")
|
||||
:shortcut (sc/get-tooltip :create-artboard-from-selection)
|
||||
:shortcut (sc/get-tooltip :artboard-selection)
|
||||
:on-click do-create-artboard-from-selection}]
|
||||
[:& menu-separator]])]))
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.workspace.header
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.config :as cf]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.messages :as dm]
|
||||
@ -18,6 +17,7 @@
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.hooks.resize :as r]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.presence :refer [active-sessions]]
|
||||
@ -71,7 +71,7 @@
|
||||
:as props}]
|
||||
(let [show-dropdown? (mf/use-state false)]
|
||||
[:div.zoom-widget {:on-click #(reset! show-dropdown? true)}
|
||||
[:span.label {} (str (mth/round (* 100 zoom)) "%")]
|
||||
[:span.label (fmt/format-percent zoom {:precision 0})]
|
||||
[:span.icon i/arrow-down]
|
||||
[:& dropdown {:show @show-dropdown?
|
||||
:on-close #(reset! show-dropdown? false)}
|
||||
@ -82,7 +82,7 @@
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(on-decrease))} "-"]
|
||||
[:p.zoom-size {} (str (mth/round (* 100 zoom)) "%")]
|
||||
[:p.zoom-size {} (fmt/format-percent zoom {:precision 0})]
|
||||
[:button {:on-click (fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
@ -346,6 +346,13 @@
|
||||
(tr "workspace.header.menu.hide-artboard-names")
|
||||
(tr "workspace.header.menu.show-artboard-names"))]]
|
||||
|
||||
[:li {:on-click #(st/emit! (toggle-flag :show-pixel-grid))}
|
||||
[:span
|
||||
(if (contains? layout :show-pixel-grid)
|
||||
(tr "workspace.header.menu.hide-pixel-grid")
|
||||
(tr "workspace.header.menu.show-pixel-grid"))]
|
||||
[:span.shortcut (sc/get-tooltip :show-pixel-grid)]]
|
||||
|
||||
[:li {:on-click #(st/emit! (-> (toggle-flag :hide-ui)
|
||||
(vary-meta assoc ::ev/origin "workspace-menu")))}
|
||||
[:span
|
||||
@ -376,6 +383,13 @@
|
||||
(tr "workspace.header.menu.enable-dynamic-alignment"))]
|
||||
[:span.shortcut (sc/get-tooltip :toggle-alignment)]]
|
||||
|
||||
[:li {:on-click #(st/emit! (toggle-flag :snap-pixel-grid))}
|
||||
[:span
|
||||
(if (contains? layout :snap-pixel-grid)
|
||||
(tr "workspace.header.menu.disable-snap-pixel-grid")
|
||||
(tr "workspace.header.menu.enable-snap-pixel-grid"))]
|
||||
[:span.shortcut (sc/get-tooltip :snap-pixel-grid)]]
|
||||
|
||||
[:li {:on-click #(st/emit! (modal/show {:type :nudge-option}))}
|
||||
[:span (tr "modals.nudge-title")]]]]]))
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.workspace.left-toolbar
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as mth]
|
||||
[app.common.media :as cm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
@ -40,8 +39,8 @@
|
||||
;; We don't want to add a ref because that redraws the component
|
||||
;; for everychange. Better direct access on the callback.
|
||||
(let [vbox (deref refs/vbox)
|
||||
x (mth/round (+ (:x vbox) (/ (:width vbox) 2)))
|
||||
y (mth/round (+ (:y vbox) (/ (:height vbox) 2)))
|
||||
x (+ (:x vbox) (/ (:width vbox) 2))
|
||||
y (+ (:y vbox) (/ (:height vbox) 2))
|
||||
params {:file-id (:id file)
|
||||
:blobs (seq blobs)
|
||||
:position (gpt/point x y)}]
|
||||
|
||||
@ -49,12 +49,12 @@
|
||||
[:div.input-wrapper
|
||||
[:span
|
||||
[:p.nudge-subtitle (tr "modals.small-nudge")]
|
||||
[:> numeric-input {:min 1
|
||||
[:> numeric-input {:min 0.01
|
||||
:value (:small nudge)
|
||||
:on-change update-small}]]]
|
||||
[:div.input-wrapper
|
||||
[:span
|
||||
[:p.nudge-subtitle (tr "modals.big-nudge")]
|
||||
[:> numeric-input {:min 1
|
||||
[:> numeric-input {:min 0.01
|
||||
:value (:big nudge)
|
||||
:on-change update-big}]]]]]]))
|
||||
|
||||
@ -89,6 +89,7 @@
|
||||
:style {:cursor (cond
|
||||
(= edit-mode :draw) cur/pen-node
|
||||
(= edit-mode :move) cur/pointer-node)
|
||||
:stroke-width 0
|
||||
:fill "none"}}]]))
|
||||
|
||||
(mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode snap-angle?]}]
|
||||
@ -147,7 +148,8 @@
|
||||
:on-mouse-enter on-enter
|
||||
:on-mouse-leave on-leave
|
||||
:style {:cursor (when (= edit-mode :move) cur/pointer-move)
|
||||
:fill "none"}}]])))
|
||||
:fill "none"
|
||||
:stroke-width 0}}]])))
|
||||
|
||||
(mf/defc path-preview [{:keys [zoom command from]}]
|
||||
[:g.preview {:style {:pointer-events "none"}}
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.main.ui.workspace.sidebar.options.menus.frame-grid
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.grid :as dw]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@ -71,8 +70,7 @@
|
||||
(let [{:keys [margin gutter item-length]} (:params grid)
|
||||
frame-length (if (= :column (:type grid)) frame-width frame-height)
|
||||
item-length (if (nil? size)
|
||||
(-> (gg/calculate-default-item-length frame-length margin gutter)
|
||||
(mth/precision 2))
|
||||
(gg/calculate-default-item-length frame-length margin gutter)
|
||||
item-length)]
|
||||
(-> grid
|
||||
(update :params assoc :size size :item-length item-length)
|
||||
@ -140,7 +138,7 @@
|
||||
|
||||
(if (= type :square)
|
||||
[:div.input-element.pixels {:title (tr "workspace.options.size")}
|
||||
[:> numeric-input {:min 1
|
||||
[:> numeric-input {:min 0.01
|
||||
:value (or (:size params) "")
|
||||
:no-validate true
|
||||
:on-change (handle-change :params :size)}]]
|
||||
@ -162,7 +160,7 @@
|
||||
(when (= :square type)
|
||||
[:& input-row {:label (tr "workspace.options.grid.params.size")
|
||||
:class "pixels"
|
||||
:min 1
|
||||
:min 0.01
|
||||
:value (:size params)
|
||||
:on-change (handle-change :params :size)}])
|
||||
|
||||
@ -207,7 +205,7 @@
|
||||
[:> numeric-input
|
||||
{:placeholder "Auto"
|
||||
:value (or (:item-length params) "")
|
||||
:default nil
|
||||
:nillable true
|
||||
:on-change handle-change-item-length}]])
|
||||
|
||||
(when (#{:row :column} type)
|
||||
@ -216,11 +214,15 @@
|
||||
:class "pixels"
|
||||
:value (:gutter params)
|
||||
:min 0
|
||||
:nillable true
|
||||
:default 0
|
||||
:placeholder "0"
|
||||
:on-change (handle-change :params :gutter)}]
|
||||
[:& input-row {:label (tr "workspace.options.grid.params.margin")
|
||||
:class "pixels"
|
||||
:min 0
|
||||
:nillable true
|
||||
:default 0
|
||||
:placeholder "0"
|
||||
:value (:margin params)
|
||||
:on-change (handle-change :params :margin)}]])
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.workspace.sidebar.options.menus.layer
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
@ -23,8 +22,7 @@
|
||||
""
|
||||
(str (-> opacity
|
||||
(d/coalesce 1)
|
||||
(* 100)
|
||||
(mth/round)))))
|
||||
(* 100)))))
|
||||
|
||||
(defn select-all [event]
|
||||
(dom/select-text! (dom/get-target event)))
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as math]
|
||||
[app.common.spec.radius :as ctr]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
@ -42,14 +41,6 @@
|
||||
:svg-raw #{:size :position :rotation}
|
||||
:text #{:size :position :rotation}})
|
||||
|
||||
(defn- attr->string [attr values]
|
||||
(let [value (attr values)]
|
||||
(if (= value :multiple)
|
||||
""
|
||||
(str (-> value
|
||||
(d/coalesce 0)
|
||||
(math/precision 2))))))
|
||||
|
||||
(declare +size-presets+)
|
||||
|
||||
;; -- User/drawing coords
|
||||
@ -204,8 +195,8 @@
|
||||
(fn []
|
||||
(when (and (= radius-mode :radius-1)
|
||||
(= @radius-multi? false))
|
||||
;; when going back from radius-multi to normal radius-1,
|
||||
;; restore focus to the newly created numeric-input
|
||||
;; when going back from radius-multi to normal radius-1,
|
||||
;; restore focus to the newly created numeric-input
|
||||
(let [radius-input (mf/ref-val radius-input-ref)]
|
||||
(dom/focus! radius-input)))))
|
||||
|
||||
@ -239,20 +230,20 @@
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (tr "workspace.options.size")]
|
||||
[:div.input-element.width {:title (tr "workspace.options.width")}
|
||||
[:> numeric-input {:min 1
|
||||
[:> numeric-input {:min 0.01
|
||||
:no-validate true
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-width-change
|
||||
:value (attr->string :width values)}]]
|
||||
:value (:width values)}]]
|
||||
|
||||
[:div.input-element.height {:title (tr "workspace.options.height")}
|
||||
[:> numeric-input {:min 1
|
||||
[:> numeric-input {:min 0.01
|
||||
:no-validate true
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-height-change
|
||||
:value (attr->string :height values)}]]
|
||||
:value (:height values)}]]
|
||||
|
||||
[:div.lock-size {:class (dom/classnames
|
||||
:selected (true? proportion-lock)
|
||||
@ -271,15 +262,13 @@
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-pos-x-change
|
||||
:value (attr->string :x values)
|
||||
:precision 2}]]
|
||||
:value (:x values)}]]
|
||||
[:div.input-element.Yaxis {:title (tr "workspace.options.y")}
|
||||
[:> numeric-input {:no-validate true
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-pos-y-change
|
||||
:value (attr->string :y values)
|
||||
:precision 2}]]])
|
||||
:value (:y values)}]]])
|
||||
|
||||
;; ROTATION
|
||||
(when (options :rotation)
|
||||
@ -290,19 +279,12 @@
|
||||
{:no-validate true
|
||||
:min 0
|
||||
:max 359
|
||||
:default 0
|
||||
:data-wrap true
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-rotation-change
|
||||
:value (attr->string :rotation values)}]]
|
||||
#_[:input.slidebar
|
||||
{:type "range"
|
||||
:min "0"
|
||||
:max "359"
|
||||
:step "10"
|
||||
:no-validate true
|
||||
:on-change on-rotation-change
|
||||
:value (attr->string :rotation values)}]])
|
||||
:value (:rotation values)}]]])
|
||||
|
||||
;; RADIUS
|
||||
(when (options :radius)
|
||||
@ -330,13 +312,14 @@
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-1-change
|
||||
:value (attr->string :rx values)}]]
|
||||
:value (:rx values)}]]
|
||||
|
||||
@radius-multi?
|
||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-multi-change
|
||||
:value ""}]]
|
||||
@ -349,7 +332,7 @@
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r1-change
|
||||
:value (attr->string :r1 values)}]]
|
||||
:value (:r1 values)}]]
|
||||
|
||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||
[:> numeric-input
|
||||
@ -357,7 +340,7 @@
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r2-change
|
||||
:value (attr->string :r2 values)}]]
|
||||
:value (:r2 values)}]]
|
||||
|
||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||
[:> numeric-input
|
||||
@ -365,7 +348,7 @@
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r3-change
|
||||
:value (attr->string :r3 values)}]]
|
||||
:value (:r3 values)}]]
|
||||
|
||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||
[:> numeric-input
|
||||
@ -373,7 +356,7 @@
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r4-change
|
||||
:value (attr->string :r4 values)}]]])])]]]))
|
||||
:value (:r4 values)}]]])])]]]))
|
||||
|
||||
(def +size-presets+
|
||||
[{:name "APPLE"}
|
||||
|
||||
@ -386,7 +386,6 @@
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:precision 2
|
||||
:value (attr->string line-height)
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change #(handle-change % :line-height)
|
||||
@ -400,7 +399,6 @@
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:precision 2
|
||||
:value (attr->string letter-spacing)
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change #(handle-change % :letter-spacing)
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.workspace.sidebar.options.rows.color-row
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as math]
|
||||
[app.common.pages :as cp]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.refs :as refs]
|
||||
@ -54,8 +53,7 @@
|
||||
""
|
||||
(str (-> opacity
|
||||
(d/coalesce 1)
|
||||
(* 100)
|
||||
(math/round)))))
|
||||
(* 100)))))
|
||||
|
||||
(defn remove-multiple [v]
|
||||
(if (= v :multiple) nil v))
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(mf/defc input-row [{:keys [label options value class min max on-change type placeholder]}]
|
||||
(mf/defc input-row [{:keys [label options value class min max on-change type placeholder default nillable]}]
|
||||
[:div.row-flex.input-row
|
||||
[:span.element-set-subtitle label]
|
||||
[:div.input-element {:class class}
|
||||
@ -43,6 +43,8 @@
|
||||
{:placeholder placeholder
|
||||
:min min
|
||||
:max max
|
||||
:default default
|
||||
:nillable nillable
|
||||
:on-change on-change
|
||||
:value (or value "")}])]])
|
||||
|
||||
|
||||
@ -97,7 +97,6 @@
|
||||
[:> numeric-input
|
||||
{:min 0
|
||||
:value (-> (:stroke-width stroke) width->string)
|
||||
:precision 2
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change (on-stroke-width-change index)
|
||||
:on-click select-all
|
||||
|
||||
@ -149,7 +149,8 @@
|
||||
show-gradient-handlers? (= (count selected) 1)
|
||||
show-grids? (contains? layout :display-grid)
|
||||
show-outlines? (and (nil? transform) (not edition) (not drawing-obj) (not (#{:comments :path :curve} drawing-tool)))
|
||||
show-pixel-grid? (>= zoom 8)
|
||||
show-pixel-grid? (and (contains? layout :show-pixel-grid)
|
||||
(>= zoom 8))
|
||||
show-presence? page-id
|
||||
show-prototypes? (= options-mode :prototype)
|
||||
show-selection-handlers? (seq selected)
|
||||
@ -345,6 +346,7 @@
|
||||
:zoom zoom
|
||||
:transform transform
|
||||
:selected selected
|
||||
:selected-shapes selected-shapes
|
||||
:page-id page-id}])
|
||||
|
||||
(when show-cursor-tooltip?
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
(ns app.main.ui.workspace.viewport.frame-grid
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.uuid :as uuid]
|
||||
@ -42,27 +43,71 @@
|
||||
:fill (str "url(#" grid-id ")")}]]))
|
||||
|
||||
(mf/defc layout-grid
|
||||
[{:keys [key frame grid]}]
|
||||
[{:keys [key frame grid zoom]}]
|
||||
(let [{color-value :color color-opacity :opacity} (-> grid :params :color)
|
||||
;; Support for old color format
|
||||
color-value (or color-value (:value (get-in grid [:params :color :value])))
|
||||
gutter (-> grid :params :gutter)
|
||||
gutter? (and (not (nil? gutter)) (not= gutter 0))
|
||||
gutter (gg/grid-gutter frame grid)
|
||||
gutter? (and (not (nil? gutter)) (not (mth/almost-zero? gutter)))]
|
||||
|
||||
style (if gutter?
|
||||
#js {:fill color-value
|
||||
:opacity color-opacity}
|
||||
#js {:stroke color-value
|
||||
:strokeOpacity color-opacity
|
||||
:fill "none"})]
|
||||
[:g.grid
|
||||
(for [{:keys [x y width height] :as area} (gg/grid-areas frame grid)]
|
||||
[:rect {:key (str key "-" x "-" y)
|
||||
:x (mth/round x)
|
||||
:y (mth/round y)
|
||||
:width (- (mth/round (+ x width)) (mth/round x))
|
||||
:height (- (mth/round (+ y height)) (mth/round y))
|
||||
:style style}])]))
|
||||
(for [[idx {:keys [x y width height] :as area}] (d/enumerate (gg/grid-areas frame grid))]
|
||||
(cond
|
||||
gutter?
|
||||
[:rect {:key (str key "-" x "-" y)
|
||||
:x x
|
||||
:y y
|
||||
:width (- (+ x width) x)
|
||||
:height (- (+ y height) y)
|
||||
:style {:fill color-value
|
||||
:stroke-width 0
|
||||
:opacity color-opacity}}]
|
||||
|
||||
(and (not gutter?) (= :column (:type grid)))
|
||||
[:*
|
||||
(when (= idx 0)
|
||||
[:line {:key (str key "-" x "-" y "-start")
|
||||
:x1 x
|
||||
:y1 y
|
||||
:x2 x
|
||||
:y2 (+ y height)
|
||||
:style {:stroke color-value
|
||||
:stroke-width (/ 1 zoom)
|
||||
:strokeOpacity color-opacity
|
||||
:fill "none"}}])
|
||||
|
||||
[:line {:key (str key "-" x "-" y "-end")
|
||||
:x1 (+ x width)
|
||||
:y1 y
|
||||
:x2 (+ x width)
|
||||
:y2 (+ y height)
|
||||
:style {:stroke color-value
|
||||
:stroke-width (/ 1 zoom)
|
||||
:strokeOpacity color-opacity
|
||||
:fill "none"}}]]
|
||||
|
||||
(and (not gutter?) (= :row (:type grid)))
|
||||
[:*
|
||||
(when (= idx 0)
|
||||
[:line {:key (str key "-" x "-" y "-start")
|
||||
:x1 x
|
||||
:y1 y
|
||||
:x2 (+ x width)
|
||||
:y2 y
|
||||
:style {:stroke color-value
|
||||
:stroke-width (/ 1 zoom)
|
||||
:strokeOpacity color-opacity
|
||||
:fill "none"}}])
|
||||
|
||||
[:line {:key (str key "-" x "-" y "-end")
|
||||
:x1 x
|
||||
:y1 (+ y height)
|
||||
:x2 (+ x width)
|
||||
:y2 (+ y height)
|
||||
:style {:stroke color-value
|
||||
:stroke-width (/ 1 zoom)
|
||||
:strokeOpacity color-opacity
|
||||
:fill "none"}}]]))]))
|
||||
|
||||
(mf/defc grid-display-frame
|
||||
[{:keys [frame zoom]}]
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
(ns app.main.ui.workspace.viewport.gradients
|
||||
"Gradients handlers and renders"
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
@ -281,9 +282,7 @@
|
||||
(fn [point]
|
||||
(let [point (gpt/transform point transform-inverse)
|
||||
start-x (/ (- (:x point) x) width)
|
||||
start-y (/ (- (:y point) y) height)
|
||||
start-x (mth/precision start-x 2)
|
||||
start-y (mth/precision start-y 2)]
|
||||
start-y (/ (- (:y point) y) height)]
|
||||
(change! {:start-x start-x :start-y start-y}))))
|
||||
|
||||
on-change-finish
|
||||
@ -292,9 +291,7 @@
|
||||
(fn [point]
|
||||
(let [point (gpt/transform point transform-inverse)
|
||||
end-x (/ (- (:x point) x) width)
|
||||
end-y (/ (- (:y point) y) height)
|
||||
end-x (mth/precision end-x 2)
|
||||
end-y (mth/precision end-y 2)]
|
||||
end-y (/ (- (:y point) y) height)]
|
||||
(change! {:end-x end-x :end-y end-y}))))
|
||||
|
||||
on-change-width
|
||||
@ -304,7 +301,7 @@
|
||||
(let [scale-factor-y (/ gradient-length (/ height 2))
|
||||
norm-dist (/ (gpt/distance point from-p)
|
||||
(* (/ width 2) scale-factor-y))]
|
||||
(when (and norm-dist (mth/finite? norm-dist))
|
||||
(when (and norm-dist (d/num? norm-dist))
|
||||
(change! {:width norm-dist})))))]
|
||||
|
||||
(when (and gradient
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.workspace.viewport.rules :as rules]
|
||||
[app.util.dom :as dom]
|
||||
[rumext.alpha :as mf]))
|
||||
@ -49,6 +50,8 @@
|
||||
frame-ref (mf/use-memo (mf/deps frame-id) #(refs/object-by-id frame-id))
|
||||
frame (mf/deref frame-ref)
|
||||
|
||||
snap-pixel? (mf/deref refs/snap-pixel?)
|
||||
|
||||
on-pointer-enter
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
@ -89,7 +92,7 @@
|
||||
|
||||
on-mouse-move
|
||||
(mf/use-callback
|
||||
(mf/deps position zoom)
|
||||
(mf/deps position zoom snap-pixel?)
|
||||
(fn [event]
|
||||
|
||||
(when-let [_ (mf/ref-val dragging-ref)]
|
||||
@ -101,8 +104,10 @@
|
||||
(+ position delta)
|
||||
(+ start-pos delta))
|
||||
|
||||
;; TODO: Change when pixel-grid flag exists
|
||||
new-position (mth/round new-position)
|
||||
new-position (if snap-pixel?
|
||||
(mth/round new-position)
|
||||
new-position)
|
||||
|
||||
new-frame-id (:id (get-hover-frame))]
|
||||
(swap! state assoc
|
||||
:new-position new-position
|
||||
@ -366,7 +371,7 @@
|
||||
:style {:font-size (/ rules/font-size zoom)
|
||||
:font-family rules/font-family
|
||||
:fill colors/black}}
|
||||
(str (mth/round pos))]]))])))
|
||||
(fmt/format-number pos)]]))])))
|
||||
|
||||
(mf/defc new-guide-area
|
||||
[{:keys [vbox zoom axis get-hover-frame disabled-guides?]}]
|
||||
|
||||
@ -146,12 +146,12 @@
|
||||
|
||||
:else
|
||||
(connect-to-point orig-shape
|
||||
{:x (+ (:x2 (:selrect orig-shape)) 100)
|
||||
:y (+ (- (:y1 (:selrect orig-shape)) 50)
|
||||
{:x (+ (:x2 (:selrect orig-shape)) (/ 100 zoom))
|
||||
:y (+ (- (:y1 (:selrect orig-shape)) (/ 50 zoom))
|
||||
(/ (* level 32) zoom))}))
|
||||
|
||||
orig-dx (if (= orig-pos :right) 100 -100)
|
||||
dest-dx (if (= dest-pos :right) 100 -100)
|
||||
orig-dx (/ (if (= orig-pos :right) 100 -100) zoom)
|
||||
dest-dx (/ (if (= dest-pos :right) 100 -100) zoom)
|
||||
|
||||
path ["M" orig-x orig-y "C" (+ orig-x orig-dx) orig-y (+ dest-x dest-dx) dest-y dest-x dest-y]
|
||||
pdata (str/join " " path)
|
||||
@ -182,7 +182,8 @@
|
||||
:d pdata}]
|
||||
|
||||
(when dest-shape
|
||||
[:& outline {:shape dest-shape
|
||||
[:& outline {:zoom zoom
|
||||
:shape dest-shape
|
||||
:color "var(--color-primary)"}])
|
||||
|
||||
[:& interaction-marker {:index index
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
@ -131,9 +132,9 @@
|
||||
|
||||
|
||||
(let [{:keys [start end]} (get-rule-params vbox axis)
|
||||
minv (max (mth/round start) -100000)
|
||||
minv (max start -100000)
|
||||
minv (* (mth/ceil (/ minv step)) step)
|
||||
maxv (min (mth/round end) 100000)
|
||||
maxv (min end 100000)
|
||||
maxv (* (mth/floor (/ maxv step)) step)]
|
||||
|
||||
(for [step-val (range minv (inc maxv) step)]
|
||||
@ -149,7 +150,7 @@
|
||||
:style {:font-size (/ font-size zoom)
|
||||
:font-family font-family
|
||||
:fill colors/gray-30}}
|
||||
(str (mth/round step-val))]
|
||||
(fmt/format-number step-val)]
|
||||
|
||||
[:line {:key (str "line-" (d/name axis) "-" step-val)
|
||||
:x1 line-x1
|
||||
@ -184,7 +185,7 @@
|
||||
:style {:font-size (/ font-size zoom)
|
||||
:font-family font-family
|
||||
:fill selection-area-color}}
|
||||
(str (mth/round (:x1 selection-rect)))]
|
||||
(fmt/format-number (:x1 selection-rect))]
|
||||
|
||||
[:rect {:x (:x2 selection-rect)
|
||||
:y (:y vbox)
|
||||
@ -200,7 +201,7 @@
|
||||
:style {:font-size (/ font-size zoom)
|
||||
:font-family font-family
|
||||
:fill selection-area-color}}
|
||||
(str (mth/round (:x2 selection-rect)))]]
|
||||
(fmt/format-number (:x2 selection-rect))]]
|
||||
|
||||
(let [center-x (+ (:x vbox) (/ rule-area-half-size zoom))
|
||||
center-y (- (+ (:y selection-rect) (/ (:height selection-rect) 2)) (/ rule-area-half-size zoom))]
|
||||
@ -234,7 +235,7 @@
|
||||
:style {:font-size (/ font-size zoom)
|
||||
:font-family font-family
|
||||
:fill selection-area-color}}
|
||||
(str (mth/round (:y2 selection-rect)))]
|
||||
(fmt/format-number (:y2 selection-rect))]
|
||||
|
||||
[:text {:x (+ center-x (/ (:height selection-rect) 2) )
|
||||
:y center-y
|
||||
@ -243,7 +244,7 @@
|
||||
:style {:font-size (/ font-size zoom)
|
||||
:font-family font-family
|
||||
:fill selection-area-color}}
|
||||
(str (mth/round (:y1 selection-rect)))]])])
|
||||
(fmt/format-number (:y1 selection-rect))]])])
|
||||
|
||||
(mf/defc rules
|
||||
{::mf/wrap-props false
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.workspace.viewport.scroll-bars
|
||||
(:require
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.store :as st]
|
||||
@ -131,7 +130,7 @@
|
||||
(let [viewport (mf/ref-val viewport-ref)
|
||||
start-pt (mf/ref-val start-ref)
|
||||
current-pt (dom/get-client-position event)
|
||||
current-pt-viewport (utils/translate-point-to-viewport-raw viewport zoom current-pt)
|
||||
current-pt-viewport (utils/translate-point-to-viewport viewport zoom current-pt)
|
||||
y-delta (/ (* (mf/ref-val height-factor-ref) (- (:y current-pt) (:y start-pt))) zoom)
|
||||
x-delta (/ (* (mf/ref-val width-factor-ref) (- (:x current-pt) (:x start-pt))) zoom)
|
||||
new-v-scrollbar-y (-> current-pt-viewport
|
||||
@ -156,8 +155,9 @@
|
||||
(fn [event axis]
|
||||
(let [viewport (mf/ref-val viewport-ref)
|
||||
start-pt (dom/get-client-position event)
|
||||
new-v-scrollbar-y (-> (utils/translate-point-to-viewport-raw viewport zoom start-pt) :y)
|
||||
new-h-scrollbar-x (-> (utils/translate-point-to-viewport-raw viewport zoom start-pt) :x)
|
||||
viewport-point (utils/translate-point-to-viewport viewport zoom start-pt)
|
||||
new-h-scrollbar-x (:x viewport-point)
|
||||
new-v-scrollbar-y (:y viewport-point)
|
||||
v-scrollbar-y-padding (- v-scrollbar-y new-v-scrollbar-y)
|
||||
h-scrollbar-x-padding (- h-scrollbar-x new-h-scrollbar-x)
|
||||
vbox-rect {:x vbox-x
|
||||
@ -168,7 +168,7 @@
|
||||
:y2 (+ vbox-y (:height vbox))
|
||||
:width (:width vbox)
|
||||
:height (:height vbox)}
|
||||
containing-rect (gpr/join-selrects [base-objects-rect vbox-rect])
|
||||
containing-rect (gsh/join-selrects [base-objects-rect vbox-rect])
|
||||
height-factor (/ (:height containing-rect) vbox-height)
|
||||
width-factor (/ (:width containing-rect) vbox-width)]
|
||||
(mf/set-ref-val! start-ref start-pt)
|
||||
@ -206,7 +206,7 @@
|
||||
:x v-scrollbar-x
|
||||
:y v-scrollbar-y
|
||||
:style {:stroke "white"
|
||||
:stroke-width 0.15}}]])
|
||||
:stroke-width (/ 0.15 zoom)}}]])
|
||||
(when show-h-scroll?
|
||||
[:g.h-scroll
|
||||
[:rect {:on-mouse-move #(on-mouse-move % :x)
|
||||
@ -220,4 +220,4 @@
|
||||
:x h-scrollbar-x
|
||||
:y h-scrollbar-y
|
||||
:style {:stroke "white"
|
||||
:stroke-width 0.15}}]])]))
|
||||
:stroke-width (/ 0.15 zoom)}}]])]))
|
||||
|
||||
@ -7,9 +7,11 @@
|
||||
(ns app.main.ui.workspace.viewport.selection
|
||||
"Selection handlers component."
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages :as cp]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@ -17,7 +19,6 @@
|
||||
[app.main.ui.workspace.shapes.path.editor :refer [path-editor]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]
|
||||
[debug :refer [debug?]]
|
||||
[rumext.alpha :as mf]
|
||||
[rumext.util :refer [map->obj]]))
|
||||
@ -33,7 +34,8 @@
|
||||
(def min-selrect-side 10)
|
||||
(def small-selrect-side 30)
|
||||
|
||||
(mf/defc selection-rect [{:keys [transform rect zoom color on-move-selected on-context-menu]}]
|
||||
(mf/defc selection-rect
|
||||
[{:keys [transform rect zoom color on-move-selected on-context-menu]}]
|
||||
(when rect
|
||||
(let [{:keys [x y width height]} rect]
|
||||
[:rect.main.viewport-selrect
|
||||
@ -41,7 +43,7 @@
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:transform transform
|
||||
:transform (str transform)
|
||||
:on-mouse-down on-move-selected
|
||||
:on-context-menu on-context-menu
|
||||
:style {:stroke color
|
||||
@ -49,75 +51,106 @@
|
||||
:fill "none"}}])))
|
||||
|
||||
(defn- handlers-for-selection [{:keys [x y width height]} {:keys [type]} zoom]
|
||||
(let [zoom-width (* width zoom)
|
||||
zoom-height (* height zoom)
|
||||
(let [threshold-small (/ 25 zoom)
|
||||
threshold-tiny (/ 10 zoom)
|
||||
|
||||
align (when (or (<= zoom-width small-selrect-side)
|
||||
(<= zoom-height small-selrect-side))
|
||||
:outside)
|
||||
show-resize-point? (or (not= type :path)
|
||||
(and
|
||||
(> zoom-width min-selrect-side)
|
||||
(> zoom-height min-selrect-side)))
|
||||
min-side-top? (or (not= type :path) (> zoom-height min-selrect-side))
|
||||
min-side-side? (or (not= type :path) (> zoom-width min-selrect-side))]
|
||||
small-width? (<= width threshold-small)
|
||||
tiny-width? (<= width threshold-tiny)
|
||||
|
||||
small-height? (<= height threshold-small)
|
||||
tiny-height? (<= height threshold-tiny)
|
||||
|
||||
vertical-line? (and (= type :path) tiny-width?)
|
||||
horizontal-line? (and (= type :path) tiny-height?)
|
||||
|
||||
align (if (or small-width? small-height?)
|
||||
:outside
|
||||
:inside)]
|
||||
(->>
|
||||
[ ;; TOP-LEFT
|
||||
{:type :rotation
|
||||
:position :top-left
|
||||
:props {:cx x :cy y}}
|
||||
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :top-left
|
||||
:props {:cx x :cy y :align align}})
|
||||
|
||||
{:type :rotation
|
||||
:position :top-right
|
||||
:props {:cx (+ x width) :cy y}}
|
||||
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :top-right
|
||||
:props {:cx (+ x width) :cy y :align align}})
|
||||
|
||||
{:type :rotation
|
||||
:position :bottom-right
|
||||
:props {:cx (+ x width) :cy (+ y height)}}
|
||||
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :bottom-right
|
||||
:props {:cx (+ x width) :cy (+ y height) :align align}})
|
||||
|
||||
{:type :rotation
|
||||
:position :bottom-left
|
||||
:props {:cx x :cy (+ y height)}}
|
||||
|
||||
(when show-resize-point?
|
||||
(when-not horizontal-line?
|
||||
(let [x (if small-width? (+ x (/ (- width threshold-small) 2)) x)
|
||||
length (if small-width? threshold-small width)]
|
||||
{:type :resize-side
|
||||
:position :top
|
||||
:props {:x x
|
||||
:y y
|
||||
:length length
|
||||
:angle 0
|
||||
:align align
|
||||
:show-handler? tiny-width?}}))
|
||||
|
||||
(when-not horizontal-line?
|
||||
(let [x (if small-width? (+ x (/ (+ width threshold-small) 2)) (+ x width))
|
||||
length (if small-width? threshold-small width)]
|
||||
{:type :resize-side
|
||||
:position :bottom
|
||||
:props {:x x
|
||||
:y (+ y height)
|
||||
:length length
|
||||
:angle 180
|
||||
:align align
|
||||
:show-handler? tiny-width?}}))
|
||||
|
||||
(when-not vertical-line?
|
||||
(let [y (if small-height? (+ y (/ (- height threshold-small) 2)) y)
|
||||
length (if small-height? threshold-small height)]
|
||||
{:type :resize-side
|
||||
:position :right
|
||||
:props {:x (+ x width)
|
||||
:y y
|
||||
:length length
|
||||
:angle 90
|
||||
:align align
|
||||
:show-handler? tiny-height?}}))
|
||||
|
||||
(when-not vertical-line?
|
||||
(let [y (if small-height? (+ y (/ (+ height threshold-small) 2)) (+ y height))
|
||||
length (if small-height? threshold-small height)]
|
||||
{:type :resize-side
|
||||
:position :left
|
||||
:props {:x x
|
||||
:y y
|
||||
:length length
|
||||
:angle 270
|
||||
:align align
|
||||
:show-handler? tiny-height?}}))
|
||||
|
||||
(when (and (not tiny-width?) (not tiny-height?))
|
||||
{:type :resize-point
|
||||
:position :top-left
|
||||
:props {:cx x :cy y :align align}})
|
||||
|
||||
(when (and (not tiny-width?) (not tiny-height?))
|
||||
{:type :resize-point
|
||||
:position :top-right
|
||||
:props {:cx (+ x width) :cy y :align align}})
|
||||
|
||||
(when (and (not tiny-width?) (not tiny-height?))
|
||||
{:type :resize-point
|
||||
:position :bottom-right
|
||||
:props {:cx (+ x width) :cy (+ y height) :align align}})
|
||||
|
||||
(when (and (not tiny-width?) (not tiny-height?))
|
||||
{:type :resize-point
|
||||
:position :bottom-left
|
||||
:props {:cx x :cy (+ y height) :align align}})
|
||||
|
||||
(when min-side-top?
|
||||
{:type :resize-side
|
||||
:position :top
|
||||
:props {:x x :y y :length width :angle 0 :align align}})
|
||||
|
||||
(when min-side-side?
|
||||
{:type :resize-side
|
||||
:position :right
|
||||
:props {:x (+ x width) :y y :length height :angle 90 :align align}})
|
||||
|
||||
(when min-side-top?
|
||||
{:type :resize-side
|
||||
:position :bottom
|
||||
:props {:x (+ x width) :y (+ y height) :length width :angle 180 :align align}})
|
||||
|
||||
(when min-side-side?
|
||||
{:type :resize-side
|
||||
:position :left
|
||||
:props {:x x :y (+ y height) :length height :angle 270 :align align}})]
|
||||
:props {:cx x :cy (+ y height) :align align}})]
|
||||
|
||||
(filterv (comp not nil?)))))
|
||||
|
||||
@ -135,12 +168,13 @@
|
||||
:y y
|
||||
:width size
|
||||
:height size
|
||||
:fill (if (debug? :rotation-handler) "blue" "none")
|
||||
:transform transform
|
||||
:fill (if (debug? :handlers) "blue" "none")
|
||||
:stroke-width 0
|
||||
:transform (str transform)
|
||||
:on-mouse-down on-rotate}]))
|
||||
|
||||
(mf/defc resize-point-handler
|
||||
[{:keys [cx cy zoom position on-resize transform rotation color overflow-text align]}]
|
||||
[{:keys [cx cy zoom position on-resize transform rotation color align]}]
|
||||
(let [cursor (if (#{:top-left :bottom-right} position)
|
||||
(cur/resize-nesw rotation) (cur/resize-nwse rotation))
|
||||
{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
|
||||
@ -151,7 +185,7 @@
|
||||
:strokeWidth "1px"
|
||||
:vectorEffect "non-scaling-stroke"}
|
||||
:fill "var(--color-white)"
|
||||
:stroke (if (and (= position :bottom-right) overflow-text) "red" color)
|
||||
:stroke color
|
||||
:cx cx'
|
||||
:cy cy'}]
|
||||
|
||||
@ -166,8 +200,9 @@
|
||||
:y cy'
|
||||
:width resize-point-circle-radius
|
||||
:height resize-point-circle-radius
|
||||
:transform (when rotation (str/fmt "rotate(%s, %s, %s)" rotation cx' cy'))
|
||||
:style {:fill (if (debug? :resize-handler) "red" "none")
|
||||
:transform (when rotation (dm/fmt "rotate(%, %, %)" rotation cx' cy'))
|
||||
:style {:fill (if (debug? :handlers) "red" "none")
|
||||
:stroke-width 0
|
||||
:cursor cursor}
|
||||
:on-mouse-down #(on-resize {:x cx' :y cy'} %)}])
|
||||
|
||||
@ -175,36 +210,43 @@
|
||||
:r (/ resize-point-circle-radius zoom)
|
||||
:cx cx'
|
||||
:cy cy'
|
||||
:style {:fill (if (debug? :resize-handler) "red" "none")
|
||||
:style {:fill (if (debug? :handlers) "red" "none")
|
||||
:stroke-width 0
|
||||
:cursor cursor}}])]))
|
||||
|
||||
(mf/defc resize-side-handler
|
||||
"The side handler is always rendered horizontally and then rotated"
|
||||
[{:keys [x y length align angle zoom position rotation transform on-resize]}]
|
||||
[{:keys [x y length align angle zoom position rotation transform on-resize color show-handler?]}]
|
||||
(let [res-point (if (#{:top :bottom} position)
|
||||
{:y y}
|
||||
{:x x})
|
||||
target-length (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
|
||||
|
||||
width (if (< target-length 6) length target-length)
|
||||
height (/ resize-side-height zoom)
|
||||
|
||||
offset-x (/ (- length width) 2)
|
||||
offset-y (if (= align :outside) (- height) (- (/ height 2)))
|
||||
|
||||
target-x (+ x offset-x)
|
||||
target-y (+ y offset-y)]
|
||||
[:rect {:x target-x
|
||||
:y target-y
|
||||
:width width
|
||||
:height height
|
||||
:transform (gmt/multiply transform
|
||||
(gmt/rotate-matrix angle (gpt/point x y)))
|
||||
:on-mouse-down #(on-resize res-point %)
|
||||
:style {:fill (if (debug? :resize-handler) "yellow" "none")
|
||||
:cursor (if (#{:left :right} position)
|
||||
(cur/resize-ew rotation)
|
||||
(cur/resize-ns rotation)) }}]))
|
||||
target-y (+ y offset-y)
|
||||
transform-str (str (gmt/multiply transform (gmt/rotate-matrix angle (gpt/point x y))))]
|
||||
[:g.resize-handler
|
||||
(when show-handler?
|
||||
[:circle {:r (/ resize-point-radius zoom)
|
||||
:style {:fillOpacity 1
|
||||
:stroke color
|
||||
:strokeWidth "1px"
|
||||
:fill "var(--color-white)"
|
||||
:vectorEffect "non-scaling-stroke"}
|
||||
:cx (+ x (/ length 2))
|
||||
:cy y
|
||||
:transform transform-str}])
|
||||
[:rect {:x x
|
||||
:y target-y
|
||||
:width length
|
||||
:height height
|
||||
:transform transform-str
|
||||
:on-mouse-down #(on-resize res-point %)
|
||||
:style {:fill (if (debug? :handlers) "yellow" "none")
|
||||
:stroke-width 0
|
||||
:cursor (if (#{:left :right} position)
|
||||
(cur/resize-ew rotation)
|
||||
(cur/resize-ns rotation)) }}]]))
|
||||
|
||||
(defn minimum-selrect [{:keys [x y width height] :as selrect}]
|
||||
(let [final-width (max width min-selrect-side)
|
||||
@ -229,13 +271,13 @@
|
||||
current-transform (mf/deref refs/current-transform)
|
||||
|
||||
selrect (:selrect shape)
|
||||
transform (geom/transform-matrix shape {:no-flip true})]
|
||||
transform (gsh/transform-matrix shape {:no-flip true})]
|
||||
|
||||
(when (not (#{:move :rotate} current-transform))
|
||||
[:g.controls {:pointer-events (if disable-handlers "none" "visible")}
|
||||
;; Selection rect
|
||||
[:& selection-rect {:rect selrect
|
||||
:transform transform
|
||||
:transform (str transform)
|
||||
:zoom zoom
|
||||
:color color
|
||||
:on-move-selected on-move-selected
|
||||
@ -244,7 +286,7 @@
|
||||
(mf/defc controls-handlers
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [{:keys [overflow-text] :as shape} (obj/get props "shape")
|
||||
(let [shape (obj/get props "shape")
|
||||
zoom (obj/get props "zoom")
|
||||
color (obj/get props "color")
|
||||
on-resize (obj/get props "on-resize")
|
||||
@ -253,7 +295,7 @@
|
||||
current-transform (mf/deref refs/current-transform)
|
||||
|
||||
selrect (:selrect shape)
|
||||
transform (geom/transform-matrix shape {:no-flip true})
|
||||
transform (gsh/transform-matrix shape {:no-flip true})
|
||||
|
||||
rotation (-> (gpt/point 1 0)
|
||||
(gpt/transform (:transform shape))
|
||||
@ -264,15 +306,14 @@
|
||||
[:g.controls {:pointer-events (if disable-handlers "none" "visible")}
|
||||
;; Handlers
|
||||
(for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)]
|
||||
(let [common-props {:key (str (name type) "-" (name position))
|
||||
(let [common-props {:key (dm/str (name type) "-" (name position))
|
||||
:zoom zoom
|
||||
:position position
|
||||
:on-rotate on-rotate
|
||||
:on-resize (partial on-resize position)
|
||||
:transform transform
|
||||
:rotation rotation
|
||||
:color color
|
||||
:overflow-text overflow-text}
|
||||
:color color}
|
||||
props (map->obj (merge common-props props))]
|
||||
(case type
|
||||
:rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
|
||||
@ -286,13 +327,13 @@
|
||||
(let [{:keys [x y width height]} shape]
|
||||
[:g.controls
|
||||
[:rect.main {:x x :y y
|
||||
:transform (geom/transform-matrix shape)
|
||||
:transform (str (gsh/transform-matrix shape))
|
||||
:width width
|
||||
:height height
|
||||
:pointer-events "visible"
|
||||
:style {:stroke color
|
||||
:stroke-width (/ 0.5 zoom)
|
||||
:stroke-opacity "1"
|
||||
:stroke-opacity 1
|
||||
:fill "none"}}]]))
|
||||
|
||||
(mf/defc multiple-handlers
|
||||
@ -300,10 +341,9 @@
|
||||
(let [shape (mf/use-memo
|
||||
(mf/deps shapes)
|
||||
#(->> shapes
|
||||
(map geom/transform-shape)
|
||||
(geom/selection-rect)
|
||||
(geom/setup {:type :rect})))
|
||||
|
||||
(map gsh/transform-shape)
|
||||
(gsh/selection-rect)
|
||||
(cp/setup-shape)))
|
||||
on-resize
|
||||
(fn [current-position _initial-position event]
|
||||
(when (dom/left-mouse? event)
|
||||
@ -329,9 +369,9 @@
|
||||
(let [shape (mf/use-memo
|
||||
(mf/deps shapes)
|
||||
#(->> shapes
|
||||
(map geom/transform-shape)
|
||||
(geom/selection-rect)
|
||||
(geom/setup {:type :rect})))]
|
||||
(map gsh/transform-shape)
|
||||
(gsh/selection-rect)
|
||||
(cp/setup-shape)))]
|
||||
|
||||
[:& controls-selection
|
||||
{:shape shape
|
||||
@ -344,7 +384,7 @@
|
||||
(mf/defc single-handlers
|
||||
[{:keys [shape zoom color disable-handlers] :as props}]
|
||||
(let [shape-id (:id shape)
|
||||
shape (geom/transform-shape shape {:round-coords? false})
|
||||
shape (gsh/transform-shape shape)
|
||||
|
||||
on-resize
|
||||
(fn [current-position _initial-position event]
|
||||
@ -368,8 +408,7 @@
|
||||
|
||||
(mf/defc single-selection
|
||||
[{:keys [shape zoom color disable-handlers on-move-selected on-context-menu] :as props}]
|
||||
(let [shape (geom/transform-shape shape {:round-coords? false})]
|
||||
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
[:& controls-selection
|
||||
{:shape shape
|
||||
:zoom zoom
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.worker :as uw]
|
||||
[beicon.core :as rx]
|
||||
[clojure.set :as set]
|
||||
@ -44,6 +45,7 @@
|
||||
(def pill-text-font-size 12)
|
||||
(def pill-text-height 20)
|
||||
(def pill-text-border-radius 4)
|
||||
(def pill-text-padding 4)
|
||||
|
||||
(mf/defc shape-distance-segment
|
||||
"Displays a segment between two selrects with the distance between them"
|
||||
@ -54,12 +56,13 @@
|
||||
(get sr2 (if (= :x coord) :x1 :y1)))
|
||||
|
||||
distance (- to-c from-c)
|
||||
distance-str (-> distance (mth/precision 0) str)
|
||||
distance-str (fmt/format-number distance)
|
||||
half-point (half-point coord sr1 sr2)
|
||||
width (-> distance-str
|
||||
count
|
||||
(* (/ pill-text-width-letter zoom))
|
||||
(+ (/ pill-text-width-margin zoom)))]
|
||||
(+ (/ pill-text-width-margin zoom))
|
||||
(+ (* (/ pill-text-width-margin zoom) 2)))]
|
||||
|
||||
[:g.distance-segment
|
||||
(let [point [(+ from-c (/ distance 2))
|
||||
@ -81,7 +84,7 @@
|
||||
:font-size (/ pill-text-font-size zoom)
|
||||
:fill "var(--color-white)"
|
||||
:text-anchor "middle"}
|
||||
(mth/precision distance 0)]])
|
||||
(fmt/format-number distance)]])
|
||||
|
||||
(let [p1 [(+ from-c (/ segment-gap zoom)) (+ half-point (/ segment-gap-side zoom))]
|
||||
p2 [(+ from-c (/ segment-gap zoom)) (- half-point (/ segment-gap-side zoom))]
|
||||
@ -110,7 +113,7 @@
|
||||
sr2 (:selrect sh2)
|
||||
c1 (if (= coord :x) :x1 :y1)
|
||||
c2 (if (= coord :x) :x2 :y2)
|
||||
dist (mth/precision (- (c1 sr2) (c2 sr1)) 0)]
|
||||
dist (- (c1 sr2) (c2 sr1))]
|
||||
[dist [sh1 sh2]]))
|
||||
|
||||
(defn overlap? [coord sh1 sh2]
|
||||
@ -134,8 +137,7 @@
|
||||
(-> (if (<= (coord sr) (coord selrect))
|
||||
(gsh/distance-selrect sr selrect)
|
||||
(gsh/distance-selrect selrect sr))
|
||||
coord
|
||||
(mth/precision 0))))
|
||||
coord)))
|
||||
|
||||
get-shapes-match
|
||||
(fn [pred? shapes]
|
||||
@ -149,9 +151,9 @@
|
||||
check-in-set
|
||||
(fn [value number-set]
|
||||
(->> number-set
|
||||
(some #(<= (mth/abs (- value %)) 1))))
|
||||
(some #(<= (mth/abs (- value %)) 0.01))))
|
||||
|
||||
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter
|
||||
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter)
|
||||
|
||||
;; Gets the distance to the current selection
|
||||
distances-xf (comp (map distance-to-selrect) (filter pos?))
|
||||
@ -195,6 +197,7 @@
|
||||
(map #(vector selrect (:selrect %))))
|
||||
|
||||
segments-to-display (d/concat-set other-shapes-segments selection-segments)]
|
||||
|
||||
segments-to-display))
|
||||
|
||||
(mf/defc shape-distance
|
||||
@ -217,8 +220,9 @@
|
||||
container-selrec (or (:selrect frame)
|
||||
(gsh/rect->selrect @refs/vbox))
|
||||
areas (gsh/selrect->areas container-selrec selrect)
|
||||
|
||||
query-side (fn [side]
|
||||
(let [rect (gsh/pad-selrec (areas side))]
|
||||
(let [rect (get areas side)]
|
||||
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
||||
(->> (uw/ask! {:cmd :selection/query
|
||||
:page-id page-id
|
||||
@ -264,15 +268,10 @@
|
||||
(let [page-id (unchecked-get props "page-id")
|
||||
zoom (unchecked-get props "zoom")
|
||||
selected (unchecked-get props "selected")
|
||||
selected-shapes (mf/deref (refs/objects-by-id selected))
|
||||
selected-shapes (unchecked-get props "selected-shapes")
|
||||
frame-id (-> selected-shapes first :frame-id)
|
||||
frame (mf/deref (refs/object-by-id frame-id))
|
||||
local (mf/deref refs/workspace-local)
|
||||
|
||||
update-shape (fn [shape] (-> shape
|
||||
(update :modifiers merge (:modifiers local))
|
||||
gsh/transform-shape))
|
||||
selrect (->> selected-shapes (map update-shape) gsh/selection-rect)]
|
||||
selrect (gsh/selection-rect selected-shapes)]
|
||||
[:g.distance
|
||||
[:& shape-distance
|
||||
{:selrect selrect
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.main.snap :as snap]
|
||||
@ -28,8 +27,6 @@
|
||||
(mf/defc snap-point
|
||||
[{:keys [point zoom]}]
|
||||
(let [{:keys [x y]} point
|
||||
x (mth/round x)
|
||||
y (mth/round y)
|
||||
cross-width (/ 3 zoom)]
|
||||
[:g
|
||||
[:line {:x1 (- x cross-width)
|
||||
@ -45,32 +42,36 @@
|
||||
|
||||
(mf/defc snap-line
|
||||
[{:keys [snap point zoom]}]
|
||||
[:line {:x1 (mth/round (:x snap))
|
||||
:y1 (mth/round (:y snap))
|
||||
:x2 (mth/round (:x point))
|
||||
:y2 (mth/round (:y point))
|
||||
[:line {:x1 (:x snap)
|
||||
:y1 (:y snap)
|
||||
:x2 (:x point)
|
||||
:y2 (:y point)
|
||||
:style {:stroke line-color :stroke-width (str (/ line-width zoom))}
|
||||
:opacity line-opacity}])
|
||||
|
||||
(defn get-snap
|
||||
[coord {:keys [shapes page-id remove-snap? modifiers]}]
|
||||
(let [shape (if (> (count shapes) 1)
|
||||
(->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect}))
|
||||
(->> shapes (first)))
|
||||
|
||||
shape (if modifiers
|
||||
(-> shape (merge (get modifiers (:id shape))) gsh/transform-shape)
|
||||
shape)
|
||||
[coord {:keys [shapes page-id remove-snap? zoom modifiers]}]
|
||||
(let [shapes-sr
|
||||
(->> shapes
|
||||
;; Merge modifiers into shapes
|
||||
(map #(merge % (get modifiers (:id %))))
|
||||
;; Create the bounding rectangle for the shapes
|
||||
(gsh/selection-rect))
|
||||
|
||||
frame-id (snap/snap-frame-id shapes)]
|
||||
|
||||
(->> (rx/of shape)
|
||||
(rx/flat-map (fn [shape]
|
||||
(->> (sp/shape-snap-points shape)
|
||||
(map #(vector frame-id %)))))
|
||||
(rx/flat-map (fn [[frame-id point]]
|
||||
(->> (snap/get-snap-points page-id frame-id remove-snap? point coord)
|
||||
(rx/map #(vector point % coord)))))
|
||||
(->> (rx/of shapes-sr)
|
||||
(rx/flat-map
|
||||
(fn [selrect]
|
||||
(->> (sp/selrect-snap-points selrect)
|
||||
(map #(vector frame-id %)))))
|
||||
|
||||
(rx/flat-map
|
||||
(fn [[frame-id point]]
|
||||
(->> (snap/get-snap-points page-id frame-id remove-snap? zoom point coord)
|
||||
(rx/map #(mapcat second %))
|
||||
(rx/map #(map :pt %))
|
||||
(rx/map #(vector point % coord)))))
|
||||
(rx/reduce conj []))))
|
||||
|
||||
(defn- flip
|
||||
@ -119,12 +120,19 @@
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(let [sub (->> subject
|
||||
(rx/switch-map #(rx/combine-latest (get-snap :x %)
|
||||
(get-snap :y %)))
|
||||
(rx/map (fn [result]
|
||||
(apply d/concat-vec (seq result))))
|
||||
(rx/subs #(let [rs (filter (fn [[_ snaps _]] (> (count snaps) 0)) %)]
|
||||
(reset! state rs))))]
|
||||
(rx/switch-map
|
||||
(fn [props]
|
||||
(->> (get-snap :y props)
|
||||
(rx/combine-latest (get-snap :x props)))))
|
||||
|
||||
(rx/map
|
||||
(fn [result]
|
||||
(apply d/concat-vec (seq result))))
|
||||
|
||||
(rx/subs
|
||||
(fn [data]
|
||||
(let [rs (filter (fn [[_ snaps _]] (> (count snaps) 0)) data)]
|
||||
(reset! state rs)))))]
|
||||
|
||||
;; On unmount callback
|
||||
#(rx/dispose! sub))))
|
||||
@ -151,22 +159,29 @@
|
||||
|
||||
(mf/defc snap-points
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [layout zoom objects selected page-id drawing transform modifiers focus] :as props}]
|
||||
[{:keys [layout zoom objects selected page-id drawing modifiers focus] :as props}]
|
||||
(us/assert set? selected)
|
||||
(let [shapes (into [] (keep (d/getf objects)) selected)
|
||||
|
||||
filter-shapes
|
||||
(into selected (mapcat #(cph/get-children-ids objects %)) selected)
|
||||
|
||||
remove-snap?
|
||||
remove-snap-base?
|
||||
(mf/with-memo [layout filter-shapes objects focus]
|
||||
(snap/make-remove-snap layout filter-shapes objects focus))
|
||||
|
||||
shapes (if drawing [drawing] shapes)]
|
||||
(when (or drawing transform)
|
||||
[:& snap-feedback {:shapes shapes
|
||||
:page-id page-id
|
||||
:remove-snap? remove-snap?
|
||||
:zoom zoom
|
||||
:modifiers modifiers}])))
|
||||
remove-snap?
|
||||
(mf/use-callback
|
||||
(mf/deps remove-snap-base?)
|
||||
(fn [{:keys [type grid] :as snap}]
|
||||
(or (remove-snap-base? snap)
|
||||
(and (= type :layout) (= grid :square))
|
||||
(= type :guide))))
|
||||
|
||||
shapes (if drawing [drawing] shapes)]
|
||||
[:& snap-feedback {:shapes shapes
|
||||
:page-id page-id
|
||||
:remove-snap? remove-snap?
|
||||
:zoom zoom
|
||||
:modifiers modifiers}]))
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
(ns app.main.ui.workspace.viewport.thumbnail-renderer
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.store :as st]
|
||||
[app.util.dom :as dom]
|
||||
@ -21,6 +22,10 @@
|
||||
(let [thumbnail-img (mf/use-ref nil)
|
||||
thumbnail-canvas (mf/use-ref nil)
|
||||
|
||||
{:keys [width height]} shape
|
||||
fixed-width (mth/clamp width 250 2000)
|
||||
fixed-height (/ (* height fixed-width) width)
|
||||
|
||||
on-dom-rendered
|
||||
(mf/use-callback
|
||||
(mf/deps (:id shape))
|
||||
@ -71,23 +76,23 @@
|
||||
_ (.rect canvas-context 0 0 canvas-width canvas-height)
|
||||
_ (set! (.-fillStyle canvas-context) background)
|
||||
_ (.fill canvas-context)
|
||||
_ (.drawImage canvas-context img-node 0 0)
|
||||
_ (.drawImage canvas-context img-node 0 0 canvas-width canvas-height)
|
||||
|
||||
data (.toDataURL canvas-node "image/jpeg" 0.8)]
|
||||
data (.toDataURL canvas-node "image/jpg" 1)]
|
||||
(on-thumbnail-data data))))]
|
||||
|
||||
[:div.frame-renderer {:ref on-dom-rendered
|
||||
:style {:display "none"}}
|
||||
[:img.thumbnail-img
|
||||
{:ref thumbnail-img
|
||||
:width (:width shape)
|
||||
:height (:height shape)
|
||||
:width width
|
||||
:height height
|
||||
:on-load on-image-load}]
|
||||
|
||||
[:canvas.thumbnail-canvas
|
||||
{:ref thumbnail-canvas
|
||||
:width (:width shape)
|
||||
:height (:height shape)}]]))
|
||||
:width fixed-width
|
||||
:height fixed-height}]]))
|
||||
|
||||
(mf/defc frame-renderer
|
||||
"Component in charge of creating thumbnails and storing them"
|
||||
|
||||
@ -163,7 +163,7 @@
|
||||
(:width vbox 0)
|
||||
(:height vbox 0)]))
|
||||
|
||||
(defn translate-point-to-viewport-raw [viewport zoom pt]
|
||||
(defn translate-point-to-viewport [viewport zoom pt]
|
||||
(let [vbox (.. ^js viewport -viewBox -baseVal)
|
||||
brect (dom/get-bounding-rect viewport)
|
||||
brect (gpt/point (d/parse-integer (:left brect))
|
||||
@ -174,10 +174,6 @@
|
||||
(gpt/divide zoom)
|
||||
(gpt/add box))))
|
||||
|
||||
(defn translate-point-to-viewport [viewport zoom pt]
|
||||
(-> (translate-point-to-viewport-raw viewport zoom pt)
|
||||
(gpt/round 0)))
|
||||
|
||||
(defn get-cursor [cursor]
|
||||
(case cursor
|
||||
:hand cur/hand
|
||||
|
||||
@ -32,8 +32,8 @@
|
||||
:pattern-units "userSpaceOnUse"}
|
||||
[:path {:d "M 1 0 L 0 0 0 1"
|
||||
:style {:fill "none"
|
||||
:stroke "var(--color-info)"
|
||||
:stroke-opacity "0.2"
|
||||
:stroke "var(--color-gray-20)"
|
||||
:stroke-opacity "1"
|
||||
:stroke-width (str (/ 1 zoom))}}]]]
|
||||
[:rect {:x (:x vbox)
|
||||
:y (:y vbox)
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.util.code-gen
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.common.text :as txt]
|
||||
[app.util.color :as uc]
|
||||
[cuerdas.core :as str]))
|
||||
@ -109,7 +108,7 @@
|
||||
(every? #(or (nil? %) (= % 0)) value)
|
||||
(or (nil? value) (= value 0))))
|
||||
|
||||
default-format (fn [value] (str (mth/precision value 2) "px"))
|
||||
default-format (fn [value] (str value "px"))
|
||||
format-property (fn [prop]
|
||||
(let [css-prop (or (prop to-prop) (name prop))
|
||||
format-fn (or (prop format) default-format)
|
||||
|
||||
@ -167,4 +167,3 @@
|
||||
(let [st (str/trim (str/lower search-term))
|
||||
nm (str/trim (str/lower name))]
|
||||
(str/includes? nm st))))
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
(ns app.util.geom.grid
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as mth]))
|
||||
|
||||
@ -39,13 +40,13 @@
|
||||
|
||||
gutter (if (= :stretch type)
|
||||
(let [gutter (/ (- width (* width' size) (* margin 2)) (dec size))]
|
||||
(if (mth/finite? gutter) gutter 0))
|
||||
(if (d/num? gutter) gutter 0))
|
||||
gutter)
|
||||
|
||||
next-v (fn [cur-val]
|
||||
(+ offset v (* (+ width' gutter) cur-val)))]
|
||||
|
||||
[size width' next-v]))
|
||||
[size width' next-v gutter]))
|
||||
|
||||
(defn- calculate-column-grid
|
||||
[{:keys [width height x y] :as frame} params]
|
||||
@ -69,6 +70,20 @@
|
||||
|
||||
[(* col-size row-size) size size next-x next-y]))
|
||||
|
||||
(defn grid-gutter
|
||||
[{:keys [x y width height]} {:keys [type params] :as grid}]
|
||||
|
||||
(case type
|
||||
:column
|
||||
(let [[_ _ _ gutter] (calculate-generic-grid x width params)]
|
||||
gutter)
|
||||
|
||||
:row
|
||||
(let [[_ _ _ gutter] (calculate-generic-grid y height params)]
|
||||
gutter)
|
||||
|
||||
nil))
|
||||
|
||||
(defn grid-areas
|
||||
"Given a frame and the grid parameters returns the areas defined on the grid"
|
||||
[frame grid]
|
||||
@ -92,28 +107,25 @@
|
||||
|
||||
(defn grid-snap-points
|
||||
"Returns the snap points for a given grid"
|
||||
([shape coord]
|
||||
(mapcat #(grid-snap-points shape % coord) (:grids shape)))
|
||||
[shape {:keys [type params] :as grid} coord]
|
||||
(when (:display grid)
|
||||
(case type
|
||||
:square
|
||||
(let [{:keys [x y width height]} shape
|
||||
size (-> params :size)]
|
||||
(when (> size 0)
|
||||
(if (= coord :x)
|
||||
(mapcat #(vector (gpt/point (+ x %) y)
|
||||
(gpt/point (+ x %) (+ y height))) (range size width size))
|
||||
(mapcat #(vector (gpt/point x (+ y %))
|
||||
(gpt/point (+ x width) (+ y %))) (range size height size)))))
|
||||
|
||||
([shape {:keys [type params] :as grid} coord]
|
||||
(when (:display grid)
|
||||
(case type
|
||||
:square
|
||||
(let [{:keys [x y width height]} shape
|
||||
size (-> params :size)]
|
||||
(when (> size 0)
|
||||
(if (= coord :x)
|
||||
(mapcat #(vector (gpt/point (+ x %) y)
|
||||
(gpt/point (+ x %) (+ y height))) (range size width size))
|
||||
(mapcat #(vector (gpt/point x (+ y %))
|
||||
(gpt/point (+ x width) (+ y %))) (range size height size)))))
|
||||
:column
|
||||
(when (= coord :x)
|
||||
(->> (grid-areas shape grid)
|
||||
(mapcat grid-area-points)))
|
||||
|
||||
:column
|
||||
(when (= coord :x)
|
||||
(->> (grid-areas shape grid)
|
||||
(mapcat grid-area-points)))
|
||||
|
||||
:row
|
||||
(when (= coord :y)
|
||||
(->> (grid-areas shape grid)
|
||||
(mapcat grid-area-points)))))))
|
||||
:row
|
||||
(when (= coord :y)
|
||||
(->> (grid-areas shape grid)
|
||||
(mapcat grid-area-points))))))
|
||||
|
||||
@ -9,25 +9,28 @@
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]))
|
||||
|
||||
(defn- selrect-snap-points [{:keys [x y width height]}]
|
||||
(defn selrect-snap-points [{:keys [x y width height] :as selrect}]
|
||||
#{(gpt/point x y)
|
||||
(gpt/point (+ x width) y)
|
||||
(gpt/point (+ x width) (+ y height))
|
||||
(gpt/point x (+ y height))})
|
||||
(gpt/point x (+ y height))
|
||||
(gsh/center-selrect selrect)})
|
||||
|
||||
(defn- frame-snap-points [{:keys [x y width height] :as selrect}]
|
||||
(into (selrect-snap-points selrect)
|
||||
#{(gpt/point (+ x (/ width 2)) y)
|
||||
(gpt/point (+ x width) (+ y (/ height 2)))
|
||||
(gpt/point (+ x (/ width 2)) (+ y height))
|
||||
(gpt/point x (+ y (/ height 2)))}))
|
||||
(defn frame-snap-points [{:keys [x y width height blocked hidden] :as selrect}]
|
||||
(when (and (not blocked) (not hidden))
|
||||
(into (selrect-snap-points selrect)
|
||||
#{(gpt/point (+ x (/ width 2)) y)
|
||||
(gpt/point (+ x width) (+ y (/ height 2)))
|
||||
(gpt/point (+ x (/ width 2)) (+ y height))
|
||||
(gpt/point x (+ y (/ height 2)))})))
|
||||
|
||||
(defn shape-snap-points
|
||||
[shape]
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
(case (:type shape)
|
||||
:frame (-> shape :selrect frame-snap-points)
|
||||
(into #{(gsh/center-shape shape)} (:points shape)))))
|
||||
[{:keys [hidden blocked] :as shape}]
|
||||
(when (and (not blocked) (not hidden))
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
(case (:type shape)
|
||||
:frame (-> shape :selrect frame-snap-points)
|
||||
(into #{(gsh/center-shape shape)} (:points shape))))))
|
||||
|
||||
(defn guide-snap-points
|
||||
[guide]
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.math :as mth]
|
||||
[app.common.path.commands :as upc]
|
||||
[clojure.set :as set]))
|
||||
|
||||
@ -402,9 +401,7 @@
|
||||
(rest segments))))))
|
||||
|
||||
(defn calculate-merge-points [group-segments points]
|
||||
(let [index-merge-point (fn [group] (vector group (-> (gpt/center-points group)
|
||||
(update :x mth/round)
|
||||
(update :y mth/round))))
|
||||
(let [index-merge-point (fn [group] (vector group (gpt/center-points group)))
|
||||
index-group (fn [point] (vector point (d/seek #(contains? % point) group-segments)))
|
||||
|
||||
group->merge-point (into {} (map index-merge-point) group-segments)
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"Performance profiling for react components."
|
||||
(:require-macros [app.util.perf])
|
||||
(:require
|
||||
[app.common.math :as math]
|
||||
[app.common.math :as mth]
|
||||
[rumext.alpha :as mf]
|
||||
[goog.functions :as f]
|
||||
["react" :as react]
|
||||
|
||||
@ -53,6 +53,19 @@
|
||||
(assoc-in [frame-id :x] (rt/make-tree))
|
||||
(assoc-in [frame-id :y] (rt/make-tree)))))
|
||||
|
||||
(defn get-grids-snap-points
|
||||
[frame coord]
|
||||
(let [grid->snap (fn [[grid-type position]]
|
||||
{:type :layout
|
||||
:id (:id frame)
|
||||
:grid grid-type
|
||||
:pt position})]
|
||||
(->> (:grids frame)
|
||||
(mapcat (fn [grid]
|
||||
(->> (gg/grid-snap-points frame grid coord)
|
||||
(mapv #(vector (:type grid) %)))))
|
||||
(mapv grid->snap))))
|
||||
|
||||
(defn- add-frame
|
||||
[page-data frame]
|
||||
(let [frame-id (:id frame)
|
||||
@ -61,17 +74,8 @@
|
||||
(mapv #(array-map :type :shape
|
||||
:id frame-id
|
||||
:pt %)))
|
||||
|
||||
grid-x-data (->> (gg/grid-snap-points frame :x)
|
||||
(mapv #(array-map :type :layout
|
||||
:id frame-id
|
||||
:pt %)))
|
||||
|
||||
grid-y-data (->> (gg/grid-snap-points frame :y)
|
||||
(mapv #(array-map :type :layout
|
||||
:id frame-id
|
||||
:pt %)))]
|
||||
|
||||
grid-x-data (get-grids-snap-points frame :x)
|
||||
grid-y-data (get-grids-snap-points frame :y)]
|
||||
(-> page-data
|
||||
;; Update root frame information
|
||||
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
|
||||
@ -107,6 +111,7 @@
|
||||
(mapv #(array-map
|
||||
:type :guide
|
||||
:id (:id guide)
|
||||
:axis (:axis guide)
|
||||
:frame-id (:frame-id guide)
|
||||
:pt %)))]
|
||||
(if-let [frame-id (:frame-id guide)]
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns debug
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.transit :as t]
|
||||
[app.common.uuid :as uuid]
|
||||
@ -32,11 +31,8 @@
|
||||
;; Displays in the console log the events through the application
|
||||
:events
|
||||
|
||||
;; Display the boxes that represent the rotation handlers
|
||||
:rotation-handler
|
||||
|
||||
;; Display the boxes that represent the resize handlers
|
||||
:resize-handler
|
||||
;; Display the boxes that represent the rotation and resize handlers
|
||||
:handlers
|
||||
|
||||
;; Displays the center of a selection
|
||||
:selection-center
|
||||
@ -130,7 +126,7 @@
|
||||
ts (/ 1000 (* (- cur @last)))
|
||||
val (+ @avg (* (- ts @avg) 0.1))]
|
||||
|
||||
(obj/set! node "innerText" (mth/precision val 0))
|
||||
(obj/set! node "innerText" val)
|
||||
(vreset! last cur)
|
||||
(vreset! avg val)
|
||||
(do-thing)))))]
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
(let [page (current-page state)
|
||||
frame (cph/get-frame (:objects page))
|
||||
shape (-> (cp/make-minimal-shape type)
|
||||
(gsh/setup {:x 0 :y 0 :width 1 :height 1})
|
||||
(cp/setup-shape {:x 0 :y 0 :width 1 :height 1})
|
||||
(merge props))]
|
||||
(swap! idmap assoc label (:id shape))
|
||||
(update state :workspace-data
|
||||
|
||||
@ -2230,6 +2230,18 @@ msgstr "Show rules"
|
||||
msgid "workspace.header.menu.show-textpalette"
|
||||
msgstr "Show fonts palette"
|
||||
|
||||
msgid "workspace.header.menu.hide-pixel-grid"
|
||||
msgstr "Hide pixel grid"
|
||||
|
||||
msgid "workspace.header.menu.show-pixel-grid"
|
||||
msgstr "Show pixel grid"
|
||||
|
||||
msgid "workspace.header.menu.disable-snap-pixel-grid"
|
||||
msgstr "Disable snap to pixel"
|
||||
|
||||
msgid "workspace.header.menu.enable-snap-pixel-grid"
|
||||
msgstr "Enable snap to pixel"
|
||||
|
||||
#: src/app/main/ui/workspace/header.cljs
|
||||
msgid "workspace.header.reset-zoom"
|
||||
msgstr "Reset"
|
||||
|
||||
@ -2246,6 +2246,18 @@ msgstr "Mostrar reglas"
|
||||
msgid "workspace.header.menu.show-textpalette"
|
||||
msgstr "Mostrar paleta de textos"
|
||||
|
||||
msgid "workspace.header.menu.hide-pixel-grid"
|
||||
msgstr "Ocultar rejilla de pixeles"
|
||||
|
||||
msgid "workspace.header.menu.show-pixel-grid"
|
||||
msgstr "Mostrar rejilla de pixeles"
|
||||
|
||||
msgid "workspace.header.menu.disable-snap-pixel-grid"
|
||||
msgstr "Desactivar ajuste al pixel"
|
||||
|
||||
msgid "workspace.header.menu.enable-snap-pixel-grid"
|
||||
msgstr "Activar ajuste al pixel"
|
||||
|
||||
#: src/app/main/ui/workspace/header.cljs
|
||||
msgid "workspace.header.reset-zoom"
|
||||
msgstr "Restablecer"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user