mirror of
https://github.com/penpot/penpot.git
synced 2025-04-04 19:11:20 -05:00
commit
a050a45424
26 changed files with 369 additions and 184 deletions
2
backend/resources/migrations/0007.remove_version.sql
Normal file
2
backend/resources/migrations/0007.remove_version.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE page
|
||||
DROP COLUMN version;
|
|
@ -116,7 +116,6 @@
|
|||
{:id id
|
||||
:file-id file-id
|
||||
:name name
|
||||
:version version
|
||||
:ordering ordering
|
||||
:data data})))
|
||||
|
||||
|
|
|
@ -32,7 +32,10 @@
|
|||
:fn (mg/resource "migrations/0005.libraries.sql")}
|
||||
{:desc "Initial presence tables"
|
||||
:name "0006-presence"
|
||||
:fn (mg/resource "migrations/0006.presence.sql")}]})
|
||||
:fn (mg/resource "migrations/0006.presence.sql")}
|
||||
{:desc "Remove version"
|
||||
:name "0007.remove_version"
|
||||
:fn (mg/resource "migrations/0007.remove_version.sql")}]})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Entry point
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[uxbox.common.data :as d]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.migrations :as mg]
|
||||
[uxbox.common.spec :as us]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.config :as cfg]
|
||||
|
@ -178,15 +179,14 @@
|
|||
(let [sid (:session-id params)
|
||||
changes (->> (:changes params)
|
||||
(mapv #(assoc % :session-id sid)))
|
||||
data (-> (:data page)
|
||||
(blob/decode)
|
||||
(cp/process-changes changes)
|
||||
(blob/encode))
|
||||
|
||||
page (assoc page
|
||||
:data data
|
||||
:revn (inc (:revn page))
|
||||
:changes (blob/encode changes))
|
||||
page (-> page
|
||||
(update :data blob/decode)
|
||||
(update :data mg/migrate-data)
|
||||
(update :data cp/process-changes changes)
|
||||
(update :data blob/encode)
|
||||
(update :revn inc)
|
||||
(assoc :changes (blob/encode changes)))
|
||||
|
||||
chng (insert-page-change! conn page)
|
||||
msg {:type :page-change
|
||||
|
@ -201,7 +201,7 @@
|
|||
|
||||
(db/update! conn :page
|
||||
{:revn (:revn page)
|
||||
:data data}
|
||||
:data (:data page)}
|
||||
{:id (:id page)})
|
||||
|
||||
(retrieve-lagged-changes conn chng params)))
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.common.spec :as us]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.migrations :as mg]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.queries.files :as files]
|
||||
|
@ -38,7 +39,8 @@
|
|||
[{:keys [profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(files/check-edition-permissions! conn profile-id file-id)
|
||||
(retrieve-pages conn params)))
|
||||
(->> (retrieve-pages conn params)
|
||||
(mapv #(update % :data mg/migrate-data)))))
|
||||
|
||||
(def ^:private sql:pages
|
||||
"select p.*
|
||||
|
@ -64,7 +66,8 @@
|
|||
(with-open [conn (db/open)]
|
||||
(let [page (retrieve-page conn id)]
|
||||
(files/check-edition-permissions! conn profile-id (:file-id page))
|
||||
page)))
|
||||
(-> page
|
||||
(update :data mg/migrate-data)))))
|
||||
|
||||
(def ^:private sql:page
|
||||
"select p.* from page as p where id=?")
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
[cuerdas.core :as str]
|
||||
[next.jdbc :as jdbc]))
|
||||
|
||||
|
||||
(s/def ::name string?)
|
||||
(s/def ::step (s/keys :req-un [::name ::desc ::fn]))
|
||||
(s/def ::steps (s/every ::step :kind vector?))
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
[cognitect.transit :as t]
|
||||
[clojure.java.io :as io]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.data :as data])
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.common.geom.point :as gpt]
|
||||
[uxbox.common.geom.matrix :as gmt])
|
||||
(:import
|
||||
java.io.ByteArrayInputStream
|
||||
java.io.ByteArrayOutputStream
|
||||
java.io.File))
|
||||
java.io.File
|
||||
uxbox.common.geom.point.Point
|
||||
uxbox.common.geom.matrix.Matrix))
|
||||
|
||||
;; --- Handlers
|
||||
|
||||
|
@ -22,10 +26,36 @@
|
|||
(constantly "file")
|
||||
(fn [v] (str v))))
|
||||
|
||||
(def +read-handlers+ dt/+read-handlers+)
|
||||
(def point-write-handler
|
||||
(t/write-handler
|
||||
(constantly "point")
|
||||
(fn [v] (into {} v))))
|
||||
|
||||
(def point-read-handler
|
||||
(t/read-handler
|
||||
(fn [value]
|
||||
(gpt/map->Point value))))
|
||||
|
||||
(def matrix-write-handler
|
||||
(t/write-handler
|
||||
(constantly "matrix")
|
||||
(fn [v] (into {} v))))
|
||||
|
||||
(def matrix-read-handler
|
||||
(t/read-handler
|
||||
(fn [value]
|
||||
(gmt/map->Matrix value))))
|
||||
|
||||
(def +read-handlers+
|
||||
(assoc dt/+read-handlers+
|
||||
"matrix" matrix-read-handler
|
||||
"point" point-read-handler))
|
||||
|
||||
(def +write-handlers+
|
||||
(assoc dt/+write-handlers+
|
||||
File file-write-handler))
|
||||
File file-write-handler
|
||||
Matrix matrix-write-handler
|
||||
Point point-write-handler))
|
||||
|
||||
;; --- Low-Level Api
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
(t/is (= (:name data) (:name result)))
|
||||
(t/is (= (:data data) (:data result)))
|
||||
(t/is (nil? (:share-token result)))
|
||||
(t/is (= 0 (:version result)))
|
||||
(t/is (= 0 (:revn result))))))
|
||||
|
||||
(t/testing "generate share token"
|
||||
|
|
|
@ -105,6 +105,11 @@
|
|||
(defn zip [col1 col2]
|
||||
(map vector col1 col2))
|
||||
|
||||
(defn mapm
|
||||
"Map over the values of a map"
|
||||
[mfn coll]
|
||||
(into {} (map (fn [[key val]] [key (mfn key val)]) coll)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Parsing / Conversion
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
(:refer-clojure :exclude [divide min max])
|
||||
(:require
|
||||
#?(:cljs [cljs.core :as c]
|
||||
:clj [clj.core :as c])
|
||||
:clj [clojure.core :as c])
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.common.math :as mth]))
|
||||
|
||||
|
@ -182,3 +182,7 @@
|
|||
(multiply
|
||||
v2-unit
|
||||
(point scalar-projection scalar-projection))))
|
||||
|
||||
(defn center-points [points]
|
||||
(let [k (point (count points))]
|
||||
(reduce #(add %1 (divide %2 k)) (point) points)))
|
||||
|
|
|
@ -17,12 +17,21 @@
|
|||
[uxbox.common.math :as mth]
|
||||
[uxbox.common.data :as d]))
|
||||
|
||||
(defn- nilf
|
||||
"Returns a new function that if you pass nil as any argument will
|
||||
return nil"
|
||||
[f]
|
||||
(fn [& args]
|
||||
(if (some nil? args)
|
||||
nil
|
||||
(apply f args))))
|
||||
|
||||
;; --- Relative Movement
|
||||
|
||||
(declare move-rect)
|
||||
(declare move-path)
|
||||
|
||||
(defn- _chk
|
||||
(defn -chk
|
||||
"Function that checks if a number is nil or nan. Will return 0 when not
|
||||
valid and the number otherwise."
|
||||
[v]
|
||||
|
@ -31,29 +40,23 @@
|
|||
(defn move
|
||||
"Move the shape relativelly to its current
|
||||
position applying the provided delta."
|
||||
[shape dpoint]
|
||||
(case (:type shape)
|
||||
:curve (move-path shape dpoint)
|
||||
:path (move-path shape dpoint)
|
||||
(move-rect shape dpoint)))
|
||||
|
||||
(defn- move-rect
|
||||
"A specialized function for relative movement
|
||||
for rect-like shapes."
|
||||
[shape {dx :x dy :y}]
|
||||
(assoc shape
|
||||
:x (+ (_chk (:x shape)) (_chk dx))
|
||||
:y (+ (_chk (:y shape)) (_chk dy))))
|
||||
|
||||
(defn- move-path
|
||||
"A specialized function for relative movement
|
||||
for path shapes."
|
||||
[shape {dx :x dy :y}]
|
||||
(let [segments (:segments shape)
|
||||
xf (comp
|
||||
(map #(update % :x + dx))
|
||||
(map #(update % :y + dy)))]
|
||||
(assoc shape :segments (into [] xf segments))))
|
||||
(let [inc-x (nilf (fn [x] (+ (-chk x) (-chk dx))))
|
||||
inc-y (nilf (fn [y] (+ (-chk y) (-chk dy))))
|
||||
inc-point (nilf (fn [p] (-> p
|
||||
(update :x inc-x)
|
||||
(update :y inc-y))))]
|
||||
(-> shape
|
||||
(update :x inc-x)
|
||||
(update :y inc-y)
|
||||
(update-in [:selrect :x] inc-x)
|
||||
(update-in [:selrect :x1] inc-x)
|
||||
(update-in [:selrect :x2] inc-x)
|
||||
(update-in [:selrect :y] inc-y)
|
||||
(update-in [:selrect :y1] inc-y)
|
||||
(update-in [:selrect :y2] inc-y)
|
||||
(update :points #(mapv inc-point %))
|
||||
(update :segments #(mapv inc-point %)))))
|
||||
|
||||
(defn recursive-move
|
||||
"Move the shape and all its recursive children."
|
||||
|
@ -70,16 +73,15 @@
|
|||
"Move the shape to the exactly specified position."
|
||||
[shape position]
|
||||
(case (:type shape)
|
||||
:path shape
|
||||
:curve shape
|
||||
(:curve :path) shape
|
||||
(absolute-move-rect shape position)))
|
||||
|
||||
(defn- absolute-move-rect
|
||||
"A specialized function for absolute moviment
|
||||
for rect-like shapes."
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(let [dx (if x (- (_chk x) (_chk (:x shape))) 0)
|
||||
dy (if y (- (_chk y) (_chk (:y shape))) 0)]
|
||||
(let [dx (if x (- (-chk x) (-chk (:x shape))) 0)
|
||||
dy (if y (- (-chk y) (-chk (:y shape))) 0)]
|
||||
(move shape (gpt/point dx dy))))
|
||||
|
||||
;; --- Center
|
||||
|
@ -198,14 +200,19 @@
|
|||
:image (setup-image shape props)
|
||||
(setup-rect shape props)))
|
||||
|
||||
(declare shape->points)
|
||||
(declare points->selrect)
|
||||
|
||||
(defn- setup-rect
|
||||
"A specialized function for setup rect-like shapes."
|
||||
[shape {:keys [x y width height]}]
|
||||
(assoc shape
|
||||
:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height))
|
||||
(as-> shape $
|
||||
(assoc $ :x x
|
||||
:y y
|
||||
:width width
|
||||
:height height)
|
||||
(assoc $ :points (shape->points $))
|
||||
(assoc $ :selrect (points->selrect (:points $)))))
|
||||
|
||||
(defn- setup-image
|
||||
[{:keys [metadata] :as shape} {:keys [x y width height] :as props}]
|
||||
|
@ -228,17 +235,16 @@
|
|||
"Coerce shape to rect like shape."
|
||||
[{:keys [type] :as shape}]
|
||||
(case type
|
||||
:path (path->rect-shape shape)
|
||||
:curve (path->rect-shape shape)
|
||||
(:curve :path) (path->rect-shape shape)
|
||||
(rect->rect-shape shape)))
|
||||
|
||||
(defn shapes->rect-shape
|
||||
[shapes]
|
||||
(let [shapes (mapv shape->rect-shape shapes)
|
||||
minx (transduce (map :x1) min shapes)
|
||||
miny (transduce (map :y1) min shapes)
|
||||
maxx (transduce (map :x2) max shapes)
|
||||
maxy (transduce (map :y2) max shapes)]
|
||||
minx (transduce (map :x1) min ##Inf shapes)
|
||||
miny (transduce (map :y1) min ##Inf shapes)
|
||||
maxx (transduce (map :x2) max ##-Inf shapes)
|
||||
maxy (transduce (map :y2) max ##-Inf shapes)]
|
||||
{:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
|
@ -249,13 +255,43 @@
|
|||
:height (- maxy miny)
|
||||
:type :rect}))
|
||||
|
||||
;; -- Points
|
||||
|
||||
(declare transform-shape-point)
|
||||
(defn shape->points [shape]
|
||||
(let [points
|
||||
(case (:type shape)
|
||||
(:curve :path) (:segments shape)
|
||||
(let [{:keys [x y width height]} shape]
|
||||
[(gpt/point x y)
|
||||
(gpt/point (+ x width) y)
|
||||
(gpt/point (+ x width) (+ y height))
|
||||
(gpt/point x (+ y height))]))]
|
||||
(mapv #(transform-shape-point % shape (:transform shape (gmt/matrix))) points)))
|
||||
|
||||
(defn points->selrect [points]
|
||||
(let [minx (transduce (map :x) min ##Inf points)
|
||||
miny (transduce (map :y) min ##Inf points)
|
||||
maxx (transduce (map :x) max ##-Inf points)
|
||||
maxy (transduce (map :y) max ##-Inf points)]
|
||||
{:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy
|
||||
:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)
|
||||
:type :rect}))
|
||||
|
||||
;; Shape->PATH
|
||||
|
||||
(declare rect->path)
|
||||
|
||||
(defn shape->path
|
||||
[shape]
|
||||
(case (:type shape)
|
||||
:path shape
|
||||
:curve shape
|
||||
(:curve :path) shape
|
||||
(rect->path shape)))
|
||||
|
||||
(defn rect->path
|
||||
|
@ -282,20 +318,9 @@
|
|||
|
||||
(defn- path->rect-shape
|
||||
[{:keys [segments] :as shape}]
|
||||
(let [minx (transduce (map :x) min segments)
|
||||
miny (transduce (map :y) min segments)
|
||||
maxx (transduce (map :x) max segments)
|
||||
maxy (transduce (map :y) max segments)]
|
||||
(assoc shape
|
||||
:type :rect
|
||||
:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy
|
||||
:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny))))
|
||||
(merge shape
|
||||
{:type :rect}
|
||||
(:selrect shape)))
|
||||
|
||||
;; --- Resolve Shape
|
||||
|
||||
|
@ -365,24 +390,16 @@
|
|||
|
||||
;; --- Outer Rect
|
||||
|
||||
(declare transform-apply-modifiers)
|
||||
|
||||
(defn selection-rect-shape
|
||||
[shape]
|
||||
(-> shape
|
||||
(transform-apply-modifiers)
|
||||
(shape->rect-shape)))
|
||||
|
||||
(defn selection-rect
|
||||
"Returns a rect that contains all the shapes and is aware of the
|
||||
rotation of each shape. Mainly used for multiple selection."
|
||||
[shapes]
|
||||
(let [xf-resolve-shape (map selection-rect-shape)
|
||||
(let [xf-resolve-shape (map :selrect)
|
||||
shapes (into [] xf-resolve-shape shapes)
|
||||
minx (transduce (map :x1) min shapes)
|
||||
miny (transduce (map :y1) min shapes)
|
||||
maxx (transduce (map :x2) max shapes)
|
||||
maxy (transduce (map :y2) max shapes)]
|
||||
minx (transduce (map :x1) min ##Inf shapes)
|
||||
miny (transduce (map :y1) min ##Inf shapes)
|
||||
maxx (transduce (map :x2) max ##-Inf shapes)
|
||||
maxy (transduce (map :y2) max ##-Inf shapes)]
|
||||
{:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
|
@ -685,19 +702,27 @@
|
|||
(gmt/skew (- skew-angle) 0)
|
||||
(gmt/rotate (- rotation-angle)))
|
||||
|
||||
new-shape (-> shape
|
||||
(merge rec)
|
||||
(update :x #(mth/precision % 2))
|
||||
(update :y #(mth/precision % 2))
|
||||
(fix-invalid-rect-values)
|
||||
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
|
||||
(update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))]
|
||||
new-shape (as-> shape $
|
||||
(merge $ rec)
|
||||
(update $ :x #(mth/precision % 2))
|
||||
(update $ :y #(mth/precision % 2))
|
||||
(fix-invalid-rect-values $)
|
||||
(update $ :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
|
||||
(update $ :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix))))
|
||||
(assoc $ :points (shape->points $))
|
||||
(assoc $ :selrect (points->selrect (:points $)))
|
||||
(update $ :rotation #(mod (+ % (get-in $ [:modifiers :rotation] 0)) 360))
|
||||
|
||||
)]
|
||||
|
||||
new-shape))
|
||||
|
||||
(declare update-path-selrect)
|
||||
(defn transform-path-shape
|
||||
[shape]
|
||||
(transform-apply-modifiers shape)
|
||||
(-> shape
|
||||
transform-apply-modifiers
|
||||
update-path-selrect)
|
||||
;; TODO: Addapt for paths is not working
|
||||
#_(let [shape-path (transform-apply-modifiers shape)
|
||||
shape-path-center (center shape-path)
|
||||
|
@ -715,14 +740,15 @@
|
|||
"Transform the shape properties given the modifiers"
|
||||
([shape] (transform-shape nil shape))
|
||||
([frame shape]
|
||||
(let [new-shape (case (:type shape)
|
||||
:path (transform-path-shape shape)
|
||||
:curve (transform-path-shape shape)
|
||||
(transform-rect-shape shape))]
|
||||
(let [new-shape
|
||||
(if (:modifiers shape)
|
||||
(as-> (case (:type shape)
|
||||
(:curve :path) (transform-path-shape shape)
|
||||
(transform-rect-shape shape)) $
|
||||
(dissoc $ :modifiers))
|
||||
shape)]
|
||||
(-> new-shape
|
||||
(translate-to-frame frame)
|
||||
(update :rotation #(mod (+ % (get-in shape [:modifiers :rotation] 0)) 360))
|
||||
(dissoc :modifiers)))))
|
||||
(translate-to-frame frame)))))
|
||||
|
||||
|
||||
(defn transform-matrix
|
||||
|
@ -735,6 +761,15 @@
|
|||
(gmt/multiply (:transform shape (gmt/matrix)))
|
||||
(gmt/translate (gpt/negate shape-center))))))
|
||||
|
||||
(defn update-path-selrect [shape]
|
||||
(as-> shape $
|
||||
(assoc $ :points (shape->points $))
|
||||
(assoc $ :selrect (points->selrect (:points $)))
|
||||
(assoc $ :x (get-in $ [:selrect :x]))
|
||||
(assoc $ :y (get-in $ [:selrect :y]))
|
||||
(assoc $ :width (get-in $ [:selrect :width]))
|
||||
(assoc $ :height (get-in $ [:selrect :height]))))
|
||||
|
||||
(defn adjust-to-viewport
|
||||
([viewport srect] (adjust-to-viewport viewport srect nil))
|
||||
([viewport srect {:keys [padding] :or {padding 0}}]
|
||||
|
|
|
@ -12,54 +12,54 @@
|
|||
#?(:cljs
|
||||
(:require [goog.math :as math])))
|
||||
|
||||
(defn ^boolean nan?
|
||||
(defn nan?
|
||||
[v]
|
||||
#?(:cljs (js/isNaN v)
|
||||
:clj (Double/isNaN v)))
|
||||
|
||||
(defn ^boolean finite?
|
||||
(defn finite?
|
||||
[v]
|
||||
#?(:cljs (js/isFinite v)
|
||||
:clj (Double/isFinite v)))
|
||||
|
||||
(defn abs
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.abs v)
|
||||
:clj (Math/abs v)))
|
||||
|
||||
(defn sin
|
||||
"Returns the sine of a number"
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.sin v)
|
||||
:clj (Math/sin)))
|
||||
:clj (Math/sin v)))
|
||||
|
||||
(defn cos
|
||||
"Returns the cosine of a number."
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.cos v)
|
||||
:clj (Math/cos v)))
|
||||
|
||||
(defn acos
|
||||
"Returns the arccosine of a number."
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.acos v)
|
||||
:clj (Math/acos v)))
|
||||
|
||||
(defn tan
|
||||
"Returns the tangent of a number."
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.tan v)
|
||||
:clj (Math/tan v)))
|
||||
|
||||
(defn atan2
|
||||
"Returns the arctangent of the quotient of its arguments."
|
||||
[^number x ^number y]
|
||||
[x y]
|
||||
#?(:cljs (js/Math.atan2 x y)
|
||||
:clj (Math/atan2 x y)))
|
||||
|
||||
(defn neg
|
||||
"Negate the number"
|
||||
[^number v]
|
||||
[v]
|
||||
(- v))
|
||||
|
||||
(defn sqrt
|
||||
|
@ -77,39 +77,39 @@
|
|||
(defn floor
|
||||
"Returns the largest integer less than or
|
||||
equal to a given number."
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.floor v)
|
||||
:clj (Math/floor)))
|
||||
:clj (Math/floor v)))
|
||||
|
||||
(defn round
|
||||
"Returns the value of a number rounded to
|
||||
the nearest integer."
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.round v)
|
||||
:clj (Math/round v)))
|
||||
|
||||
(defn ceil
|
||||
"Returns the smallest integer greater than
|
||||
or equal to a given number."
|
||||
[^number v]
|
||||
[v]
|
||||
#?(:cljs (js/Math.ceil v)
|
||||
:clj (Math/ceil v)))
|
||||
|
||||
(defn precision
|
||||
[^number v ^number n]
|
||||
[v n]
|
||||
(when (and (number? v) (number? n))
|
||||
#?(:cljs (js/parseFloat (.toFixed v n))
|
||||
:clj (.. (BigDecimal/valueOf v) (setScale n java.math.RoundingMode/HALF_UP) (doubleValue)))))
|
||||
|
||||
(defn radians
|
||||
"Converts degrees to radians."
|
||||
[^number degrees]
|
||||
[degrees]
|
||||
#?(:cljs (math/toRadians degrees)
|
||||
:clj (Math/toRadians degrees)))
|
||||
|
||||
(defn degrees
|
||||
"Converts radians to degrees."
|
||||
[^number radians]
|
||||
[radians]
|
||||
#?(:cljs (math/toDegrees radians)
|
||||
:clj (Math/toDegrees radians)))
|
||||
|
||||
|
|
64
common/uxbox/common/migrations.cljc
Normal file
64
common/uxbox/common/migrations.cljc
Normal file
|
@ -0,0 +1,64 @@
|
|||
(ns uxbox.common.migrations
|
||||
(:require
|
||||
[uxbox.common.pages :as p]
|
||||
[uxbox.common.geom.shapes :as gsh]
|
||||
[uxbox.common.geom.point :as gpt]
|
||||
[uxbox.common.geom.matrix :as gmt]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.common.data :as d]))
|
||||
|
||||
(defmulti migrate :version)
|
||||
|
||||
(defn migrate-data
|
||||
([data from-version to-version]
|
||||
(-> data
|
||||
(assoc :version to-version)
|
||||
(migrate)))
|
||||
|
||||
([data]
|
||||
(try
|
||||
(reduce #(migrate-data %1 %2 (inc %2))
|
||||
data
|
||||
(range (:version data 0) p/page-version))
|
||||
|
||||
;; If an error is thrown, we log the error and return the data without migrations
|
||||
#?(:clj (catch Exception e (.printStackTrace e) data)
|
||||
:cljs (catch :default e (.error js/console e) data)))))
|
||||
|
||||
;; Default handler, noop
|
||||
(defmethod migrate :default [data] data)
|
||||
|
||||
;; -- MIGRATIONS --
|
||||
|
||||
(defmethod migrate 4 [data]
|
||||
;; We changed the internal model of the shapes so they have their selection rect
|
||||
;; and the vertices
|
||||
|
||||
(letfn [;; Creates a new property `points` that stores the transformed points inside the shape
|
||||
;; this will be used for the snaps and the selection rect
|
||||
(calculate-shape-points [objects]
|
||||
(->> objects
|
||||
(d/mapm
|
||||
(fn [id shape]
|
||||
(if (= (:id shape) uuid/zero)
|
||||
shape
|
||||
(assoc shape :points (gsh/shape->points shape)))))))
|
||||
|
||||
;; Creates a new property `selrect` that stores the selection rect for the shape
|
||||
(calculate-shape-selrects [objects]
|
||||
(->> objects
|
||||
(d/mapm
|
||||
(fn [id shape]
|
||||
(if (= (:id shape) uuid/zero)
|
||||
shape
|
||||
(assoc shape :selrect (gsh/points->selrect (:points shape))))))))]
|
||||
(-> data
|
||||
|
||||
;; Adds vertices to shapes
|
||||
(update :objects calculate-shape-points)
|
||||
|
||||
;; Creates selection rects for shapes
|
||||
(update :objects calculate-shape-selrects))))
|
||||
|
||||
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.spec :as us]))
|
||||
|
||||
(def page-version 4)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Page Data Structure Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -184,6 +186,22 @@
|
|||
(s/def ::width number?)
|
||||
(s/def ::height number?)
|
||||
(s/def ::index integer?)
|
||||
(s/def ::x1 number?)
|
||||
(s/def ::y1 number?)
|
||||
(s/def ::x2 number?)
|
||||
(s/def ::y2 number?)
|
||||
|
||||
(s/def ::selrect (s/keys :req-un [::x
|
||||
::y
|
||||
::x1
|
||||
::y1
|
||||
::x2
|
||||
::y2
|
||||
::width
|
||||
::height]))
|
||||
|
||||
(s/def ::point (s/keys :req-un [::x ::y]))
|
||||
(s/def ::points (s/coll-of ::point :kind vector?))
|
||||
|
||||
(s/def ::shape-attrs
|
||||
(s/keys :opt-un [::blocked
|
||||
|
@ -211,7 +229,9 @@
|
|||
::stroke-alignment
|
||||
::text-align
|
||||
::width ::height
|
||||
::interactions]))
|
||||
::interactions
|
||||
::selrect
|
||||
::points]))
|
||||
|
||||
(s/def ::minimal-shape
|
||||
(s/keys :req-un [::type ::name]
|
||||
|
@ -228,8 +248,8 @@
|
|||
(s/map-of uuid? ::shape))
|
||||
|
||||
(s/def ::data
|
||||
(s/keys :req-un [::options
|
||||
::version
|
||||
(s/keys :req-un [::version
|
||||
::options
|
||||
::objects]))
|
||||
|
||||
(s/def ::attr keyword?)
|
||||
|
@ -282,7 +302,7 @@
|
|||
|
||||
(def default-page-data
|
||||
"A reference value of the empty page data."
|
||||
{:version 3
|
||||
{:version page-version
|
||||
:options {}
|
||||
:objects
|
||||
{root
|
||||
|
@ -469,5 +489,3 @@
|
|||
[shape op]
|
||||
(ex/raise :type :operation-not-implemented
|
||||
:context {:type (:type op)}))
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
(ns uxbox.main.data.workspace
|
||||
(:require
|
||||
[uxbox.util.debug :refer [logjs]]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
|
|
|
@ -165,9 +165,8 @@
|
|||
|
||||
(defn- calculate-frame-overlap
|
||||
[frames shape]
|
||||
(let [shape (geom/shape->rect-shape shape)
|
||||
xf (comp
|
||||
(filter #(geom/overlaps? % shape))
|
||||
(let [xf (comp
|
||||
(filter #(geom/overlaps? % (:selrect shape)))
|
||||
(take 1))
|
||||
frame (first (into [] xf frames))]
|
||||
(or (:id frame) uuid/zero)))
|
||||
|
|
|
@ -80,6 +80,8 @@
|
|||
(let [{:keys [width height rotation]} shape
|
||||
shapev (-> (gpt/point width height))
|
||||
|
||||
rotation (if (#{:curve :path} (:type shape)) 0 rotation)
|
||||
|
||||
;; Vector modifiers depending on the handler
|
||||
handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y))
|
||||
|
||||
|
@ -129,15 +131,13 @@
|
|||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [shape (gsh/shape->rect-shape shape)
|
||||
initial @ms/mouse-position
|
||||
(let [initial @ms/mouse-position
|
||||
stoper (rx/filter ms/mouse-up? stream)
|
||||
page-id (get state :current-page-id)
|
||||
resizing-shapes (map #(get-in state [:workspace-data page-id :objects %]) ids)
|
||||
layout (get state :workspace-layout)]
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
;; (rx/mapcat apply-grid-alignment)
|
||||
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||
(rx/map normalize-proportion-lock)
|
||||
(rx/switch-map (fn [[point :as current]]
|
||||
|
|
|
@ -44,8 +44,7 @@
|
|||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [{:keys [x y width height]
|
||||
:as shape} (->> (unchecked-get props "shape")
|
||||
(geom/selection-rect-shape))
|
||||
:as shape} (->> (unchecked-get props "shape") :selrect)
|
||||
|
||||
childs (unchecked-get props "childs")
|
||||
frame (unchecked-get props "frame")
|
||||
|
|
|
@ -196,7 +196,8 @@
|
|||
(assoc-in [:workspace-local :drawing ::initialized?] true)))
|
||||
|
||||
(insert-point-segment [state point]
|
||||
(update-in state [:workspace-local :drawing :segments] (fnil conj []) point))
|
||||
(-> state
|
||||
(update-in [:workspace-local :drawing :segments] (fnil conj []) point)))
|
||||
|
||||
(update-point-segment [state index point]
|
||||
(let [segments (count (get-in state [:workspace-local :drawing :segments]))
|
||||
|
@ -204,8 +205,12 @@
|
|||
(cond-> state
|
||||
exists? (assoc-in [:workspace-local :drawing :segments index] point))))
|
||||
|
||||
(remove-dangling-segmnet [state]
|
||||
(update-in state [:workspace-local :drawing :segments] #(vec (butlast %))))]
|
||||
(finish-drawing-path [state]
|
||||
(update-in
|
||||
state [:workspace-local :drawing]
|
||||
(fn [shape] (-> shape
|
||||
(update :segments #(vec (butlast %)))
|
||||
(geom/update-path-selrect)))))]
|
||||
|
||||
(ptk/reify ::handle-drawing-path
|
||||
ptk/WatchEvent
|
||||
|
@ -263,9 +268,11 @@
|
|||
point)]
|
||||
#(update-point-segment % index point))))
|
||||
(rx/take-until stoper))
|
||||
(rx/of remove-dangling-segmnet
|
||||
(rx/of finish-drawing-path
|
||||
handle-finish-drawing))))))))
|
||||
|
||||
(def simplify-tolerance 0.3)
|
||||
|
||||
(def handle-drawing-curve
|
||||
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
||||
(ms/mouse-event? event) (= type :up))
|
||||
|
@ -276,8 +283,13 @@
|
|||
(insert-point-segment [state point]
|
||||
(update-in state [:workspace-local :drawing :segments] (fnil conj []) point))
|
||||
|
||||
(simplify-drawing-path [state tolerance]
|
||||
(update-in state [:workspace-local :drawing :segments] path/simplify tolerance))]
|
||||
(finish-drawing-curve [state]
|
||||
(update-in
|
||||
state [:workspace-local :drawing]
|
||||
(fn [shape]
|
||||
(-> shape
|
||||
(update :segments #(path/simplify % simplify-tolerance))
|
||||
(geom/update-path-selrect)))))]
|
||||
|
||||
(ptk/reify ::handle-drawing-curve
|
||||
ptk/WatchEvent
|
||||
|
@ -290,7 +302,7 @@
|
|||
(->> mouse
|
||||
(rx/map (fn [pt] #(insert-point-segment % pt)))
|
||||
(rx/take-until stoper))
|
||||
(rx/of #(simplify-drawing-path % 0.3)
|
||||
(rx/of finish-drawing-curve
|
||||
handle-finish-drawing)))))))
|
||||
|
||||
(def handle-finish-drawing
|
||||
|
@ -308,10 +320,8 @@
|
|||
:text 16
|
||||
5)
|
||||
shape (-> shape
|
||||
(geom/transform-shape)
|
||||
(update :width #(max shape-min-width %))
|
||||
(update :height #(max shape-min-height %))
|
||||
(dissoc shape ::initialized?))]
|
||||
geom/transform-shape
|
||||
(dissoc ::initialized?)) ]
|
||||
;; Add & select the created shape to the workspace
|
||||
(rx/of dw/deselect-all
|
||||
(dw/add-shape shape)))))))))
|
||||
|
@ -336,7 +346,7 @@
|
|||
|
||||
(mf/defc generic-draw-area
|
||||
[{:keys [shape zoom]}]
|
||||
(let [{:keys [x y width height]} (geom/selection-rect-shape shape)]
|
||||
(let [{:keys [x y width height]} (:selrect shape)]
|
||||
(when (and x y)
|
||||
[:g
|
||||
[:& shapes/shape-wrapper {:shape shape}]
|
||||
|
|
|
@ -187,7 +187,7 @@
|
|||
[:g.controls
|
||||
|
||||
;; Selection rect
|
||||
[:& selection-rect {:rect shape
|
||||
[:& selection-rect {:rect selrect
|
||||
:transform transform
|
||||
:zoom zoom}]
|
||||
|
||||
|
@ -283,7 +283,6 @@
|
|||
(let [shape-id (:id shape)
|
||||
shape (geom/transform-shape shape)
|
||||
shape' (if (debug? :simple-selection) (geom/selection-rect [shape]) shape)
|
||||
|
||||
on-resize
|
||||
#(do (dom/stop-propagation %2)
|
||||
(st/emit! (dw/start-resize %1 #{shape-id} shape')))
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
;; namespace under uxbox.ui.workspace.shapes.* prefix, all the
|
||||
;; others are defined using a generic wrapper implemented in
|
||||
;; common.
|
||||
[uxbox.main.ui.workspace.shapes.bbox :as bbox]
|
||||
[uxbox.main.ui.workspace.shapes.bounding-box :refer [bounding-box]]
|
||||
[uxbox.main.ui.workspace.shapes.common :as common]
|
||||
[uxbox.main.ui.workspace.shapes.frame :as frame]
|
||||
[uxbox.main.ui.workspace.shapes.group :as group]
|
||||
|
@ -71,7 +71,8 @@
|
|||
;; Only used when drawing a new frame.
|
||||
:frame [:> frame-wrapper {:shape shape}]
|
||||
nil)
|
||||
[:& bbox/bounding-box {:shape shape :frame frame}]])))
|
||||
[:& bounding-box {:shape (->> shape (geom/transform-shape frame)) :frame frame}]])))
|
||||
|
||||
(def group-wrapper (group/group-wrapper-factory shape-wrapper))
|
||||
(def frame-wrapper (frame/frame-wrapper-factory shape-wrapper))
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns uxbox.main.ui.workspace.shapes.bbox
|
||||
(ns uxbox.main.ui.workspace.shapes.bounding-box
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]
|
||||
|
@ -13,37 +13,61 @@
|
|||
[uxbox.common.geom.matrix :as gmt]
|
||||
[uxbox.common.geom.point :as gpt]
|
||||
[uxbox.util.debug :refer [debug?]]
|
||||
[uxbox.main.refs :as refs]
|
||||
["randomcolor" :as rdcolor]))
|
||||
|
||||
(defn fixed
|
||||
[num]
|
||||
(when num (.toFixed num 2)))
|
||||
|
||||
(mf/defc cross-point [{:keys [point zoom color]}]
|
||||
(let [width (/ 5 zoom)]
|
||||
[:g.point
|
||||
[:line {:x1 (- (:x point) width) :y1 (- (:y point) width)
|
||||
:x2 (+ (:x point) width) :y2 (+ (:y point) width)
|
||||
:stroke color
|
||||
:stroke-width "1px"
|
||||
:stroke-opacity 0.5}]
|
||||
|
||||
[:line {:x1 (+ (:x point) width) :y1 (- (:y point) width)
|
||||
:x2 (- (:x point) width) :y2 (+ (:y point) width)
|
||||
:stroke color
|
||||
:stroke-width "1px"
|
||||
:stroke-opacity 0.5}]]))
|
||||
|
||||
(mf/defc bounding-box
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(when (debug? :bounding-boxes)
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
selrect (-> shape
|
||||
(geom/selection-rect-shape)
|
||||
(geom/translate-to-frame frame))
|
||||
shape-center (geom/center selrect)
|
||||
line-color (rdcolor #js {:seed (str (:id shape))})]
|
||||
[:g
|
||||
selrect (-> shape :selrect)
|
||||
shape-center (geom/center shape)
|
||||
line-color (rdcolor #js {:seed (str (:id shape))})
|
||||
zoom (mf/deref refs/selected-zoom)]
|
||||
[:g.bounding-box
|
||||
[:text {:x (:x selrect)
|
||||
:y (- (:y selrect) 5)
|
||||
:font-size 10
|
||||
:fill "red"
|
||||
:fill line-color
|
||||
:stroke "white"
|
||||
:stroke-width 0.1}
|
||||
(str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fixed (:x shape)) (fixed (:y shape)))]
|
||||
|
||||
[:& cross-point {:point shape-center
|
||||
:zoom zoom
|
||||
:color line-color}]
|
||||
|
||||
(for [point (:points shape)]
|
||||
[:& cross-point {:point point
|
||||
:zoom zoom
|
||||
:color line-color}])
|
||||
|
||||
[:rect {:x (:x selrect)
|
||||
:y (:y selrect)
|
||||
:width (:width selrect)
|
||||
:height (:height selrect)
|
||||
:style {:stroke "red"
|
||||
:style {:stroke line-color
|
||||
:fill "transparent"
|
||||
:stroke-width "1px"
|
||||
:stroke-opacity 0.5
|
|
@ -40,8 +40,8 @@
|
|||
"Calculate the best position to draw an interaction line
|
||||
between two shapes"
|
||||
[orig-shape dest-shape]
|
||||
(let [orig-rect (geom/selection-rect-shape orig-shape)
|
||||
dest-rect (geom/selection-rect-shape dest-shape)
|
||||
(let [orig-rect (:selrect orig-shape)
|
||||
dest-rect (:selrect dest-shape)
|
||||
|
||||
orig-x-left (:x orig-rect)
|
||||
orig-x-right (+ orig-x-left (:width orig-rect))
|
||||
|
@ -71,7 +71,7 @@
|
|||
"Calculate the best position to draw an interaction line
|
||||
between one shape and one point"
|
||||
[orig-shape dest-point]
|
||||
(let [orig-rect (geom/selection-rect-shape orig-shape)
|
||||
(let [orig-rect (:selrect orig-shape)
|
||||
|
||||
orig-x-left (:x orig-rect)
|
||||
orig-x-right (+ orig-x-left (:width orig-rect))
|
||||
|
@ -159,7 +159,7 @@
|
|||
|
||||
(mf/defc interaction-handle
|
||||
[{:keys [shape selected zoom] :as props}]
|
||||
(let [shape-rect (geom/selection-rect-shape shape)
|
||||
(let [shape-rect (:selrect shape)
|
||||
handle-x (+ (:x shape-rect) (:width shape-rect))
|
||||
handle-y (+ (:y shape-rect) (/ (:height shape-rect) 2))]
|
||||
[:g {:on-mouse-down #(on-mouse-down % shape selected)}
|
||||
|
|
|
@ -25,13 +25,9 @@
|
|||
|
||||
(defn user-coords-vector
|
||||
[shape]
|
||||
(let [{sel-x :x sel-y :y :as selrect}
|
||||
(-> shape
|
||||
gsh/shape->path
|
||||
(gsh/center-transform (:transform shape))
|
||||
gsh/shape->rect-shape)
|
||||
(let [oldselrec (-> shape gsh/shape->path (gsh/center-transform (:transform shape)) gsh/shape->rect-shape)
|
||||
{sel-x :x sel-y :y :as selrec} #_(:selrect shape) oldselrec
|
||||
{rec-x :x rec-y :y} (-> shape gsh/shape->rect-shape)
|
||||
|
||||
dx (- rec-x sel-x)
|
||||
dy (- rec-y sel-y)]
|
||||
(-> (gpt/point dx dy)
|
||||
|
@ -141,13 +137,13 @@
|
|||
:type "number"
|
||||
:no-validate true
|
||||
:on-change on-pos-x-change
|
||||
:value (:x shape)}]]
|
||||
:value (-> shape :x (math/precision 2))}]]
|
||||
[:div.input-element.Yaxis
|
||||
[:input.input-text {:placeholder "y"
|
||||
:type "number"
|
||||
:no-validate true
|
||||
:on-change on-pos-y-change
|
||||
:value (:y shape)}]]])
|
||||
:value (-> shape :y (math/precision 2))}]]])
|
||||
|
||||
(when (options :rotation)
|
||||
[:div.row-flex
|
||||
|
|
|
@ -26,9 +26,8 @@
|
|||
|
||||
(defn shape-snap-points
|
||||
[shape]
|
||||
(let [modified-path (gsh/transform-apply-modifiers shape)
|
||||
shape-center (gsh/center modified-path)]
|
||||
(let [shape (gsh/transform-shape shape)
|
||||
shape-center (gsh/center shape)]
|
||||
(case (:type shape)
|
||||
:frame (-> modified-path gsh/shape->rect-shape frame-snap-points)
|
||||
(:path :curve) (into #{shape-center} (-> modified-path gsh/shape->rect-shape :segments))
|
||||
(into #{shape-center} (-> modified-path :segments)))))
|
||||
:frame (-> shape gsh/shape->rect-shape frame-snap-points)
|
||||
(into #{shape-center} (-> shape :points)))))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[okulary.core :as l]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.worker.impl :as impl]
|
||||
[uxbox.util.range-tree :as rt]
|
||||
[uxbox.util.geom.snap-points :as snap]
|
||||
|
@ -38,11 +39,6 @@
|
|||
(mapcat (process-shape coord))
|
||||
(reduce into-tree (rt/make-tree)))))
|
||||
|
||||
(defn- mapm
|
||||
"Map over the values of a map"
|
||||
[mfn coll]
|
||||
(into {} (map (fn [[key val]] [key (mfn key val)]) coll)))
|
||||
|
||||
(defn- initialize-snap-data
|
||||
"Initialize the snap information with the current workspace information"
|
||||
[objects]
|
||||
|
@ -51,16 +47,16 @@
|
|||
(group-by :frame-id))
|
||||
frame-shapes (->> (cp/select-frames objects)
|
||||
(reduce #(update %1 (:id %2) conj %2) frame-shapes))]
|
||||
(mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x)
|
||||
(d/mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x)
|
||||
:y (create-coord-data frame-id shapes :y)})
|
||||
frame-shapes)))
|
||||
|
||||
(defn- log-state
|
||||
"Helper function to print a friendly version of the snap tree. Debugging purposes"
|
||||
[]
|
||||
(let [process-frame-data #(mapm rt/as-map %)
|
||||
process-page-data #(mapm process-frame-data %)]
|
||||
(js/console.log "STATE" (clj->js (mapm process-page-data @state)))))
|
||||
(let [process-frame-data #(d/mapm rt/as-map %)
|
||||
process-page-data #(d/mapm process-frame-data %)]
|
||||
(js/console.log "STATE" (clj->js (d/mapm process-page-data @state)))))
|
||||
|
||||
(defn- index-page [state page-id objects]
|
||||
(let [snap-data (initialize-snap-data objects)]
|
||||
|
|
Loading…
Add table
Reference in a new issue