Makes import SVG groups

This commit is contained in:
alonso.torres 2021-02-16 17:19:26 +01:00 committed by Andrey Antukh
parent 507f3c06e7
commit 741d67c30b
8 changed files with 151 additions and 100 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -30,7 +30,7 @@
height (d/parse-integer height-str)]
[width height]))
(defn tag-name [{:keys [tag]}]
(defn tag-name [tag]
(cond (string? tag) tag
(keyword? tag) (name tag)
(nil? tag) "node"
@ -63,17 +63,35 @@
(d/any-key? attrs :stroke :stroke-width :stroke-opacity)
(setup-stroke attrs)))
(defn create-raw-svg [name frame-id x y width height data]
(-> {:id (uuid/next)
:type :svg-raw
:name name
:frame-id frame-id
:width width
:height height
:x x
:y y
:content (if (map? data) (update data :attrs usvg/clean-attrs) data)}
(gsh/setup-selrect)))
(defn create-raw-svg [name frame-id svg-data element-data]
(let [{:keys [x y width height]} svg-data]
(-> {:id (uuid/next)
:type :svg-raw
:name name
:frame-id frame-id
:width width
:height height
:x x
:y y
:root-attrs (select-keys svg-data [:width :height])
:content (cond-> element-data
(map? element-data) (update :attrs usvg/clean-attrs))}
(gsh/setup-selrect))))
(defn create-svg-root [frame-id svg-data]
(let [{:keys [name x y width height]} svg-data]
(-> {:id (uuid/next)
:type :group
:name name
:frame-id frame-id
:width width
:height height
:x x
:y y
:attrs (-> (get svg-data :attrs) usvg/clean-attrs)
;;:content (if (map? data) (update data :attrs usvg/clean-attrs) data)
}
(gsh/setup-selrect))))
(defn parse-path [name frame-id {:keys [attrs] :as data}]
(let [content (ugp/path->content (:d attrs))
@ -89,23 +107,38 @@
(add-style-attributes data))))
(defn parse-svg-element [root-shape data unames]
(let [root-id (:id root-shape)
frame-id (:frame-id root-shape)
{:keys [x y width height]} (:selrect root-shape)
{:keys [tag]} data
name (dwc/generate-unique-name unames (str "svg-" (tag-name data)))
shape
(case tag
;; :rect (parse-rect data)
;; :path (parse-path name frame-id data)
(create-raw-svg name frame-id x y width height data))]
(defn parse-svg-element [frame-id svg-data element-data unames]
(let [{:keys [tag]} element-data
name (dwc/generate-unique-name unames (str "svg-" (tag-name tag)))]
(-> shape
(assoc :svg-id root-id))))
(case tag
;; :rect (parse-rect data)
;; :path (parse-path name frame-id data)
(create-raw-svg name frame-id svg-data element-data))))
(defn svg-uploaded [data x y]
(defn add-svg-child-changes [page-id objects selected frame-id parent-id svg-data ids-mappings result [index data]]
(let [[unames [rchs uchs]] result
data (update data :attrs usvg/replace-attrs-ids ids-mappings)
shape (parse-svg-element frame-id svg-data data unames)
shape-id (:id shape)
[rch1 uch1] (dwc/add-shape-changes page-id objects selected shape)
;; Mov-objects won't have undo because we "delete" the object in the undo of the
;; previous operation
rch2 [{:type :mov-objects
:parent-id parent-id
:frame-id frame-id
:page-id page-id
:index index
:shapes [shape-id]}]
;; Careful! the undo changes are concatenated reversed (we undo in reverse order
changes [(d/concat rchs rch1 rch2) (d/concat uch1 uchs)]
unames (conj unames (:name shape))
reducer-fn (partial add-svg-child-changes page-id objects selected frame-id shape-id svg-data ids-mappings)]
(reduce reducer-fn [unames changes] (d/enumerate (:content data)))))
(defn svg-uploaded [svg-data x y]
(ptk/reify ::svg-uploaded
ptk/WatchEvent
(watch [_ state stream]
@ -114,40 +147,29 @@
frame-id (cp/frame-id-by-position objects {:x x :y y})
selected (get-in state [:workspace-local :selected])
[width height] (svg-dimensions data)
[width height] (svg-dimensions svg-data)
x (- x (/ width 2))
y (- y (/ height 2))
add-svg-child
(fn add-svg-child [parent-id root-shape [unames [rchs uchs]] [index {:keys [content] :as data}]]
(let [shape (parse-svg-element root-shape data unames)
shape-id (:id shape)
[rch1 uch1] (dwc/add-shape-changes page-id objects selected shape)
;; Mov-objects won't have undo because we "delete" the object in the undo of the
;; previous operation
rch2 [{:type :mov-objects
:parent-id parent-id
:frame-id frame-id
:page-id page-id
:index index
:shapes [shape-id]}]
;; Careful! the undo changes are concatenated reversed (we undo in reverse order
changes [(d/concat rchs rch1 rch2) (d/concat uch1 uchs)]
unames (conj unames (:name shape))]
(reduce (partial add-svg-child shape-id root-shape) [unames changes] (d/enumerate content))))
unames (dwc/retrieve-used-names objects)
svg-name (->> (str/replace (:name data) ".svg" "")
svg-name (->> (str/replace (:name svg-data) ".svg" "")
(dwc/generate-unique-name unames))
root-shape (create-raw-svg svg-name frame-id x y width height data)
ids-mappings (usvg/generate-id-mapping svg-data)
svg-data (-> svg-data
(assoc :x x
:y y
:width width
:height height
:name svg-name))
root-shape (create-svg-root frame-id svg-data)
root-id (:id root-shape)
changes (dwc/add-shape-changes page-id objects selected root-shape)
[_ [rchanges uchanges]] (reduce (partial add-svg-child root-id root-shape) [unames changes] (d/enumerate (:content data)))]
reducer-fn (partial add-svg-child-changes page-id objects selected frame-id root-id svg-data ids-mappings)
[_ [rchanges uchanges]] (reduce reducer-fn [unames changes] (d/enumerate (:content svg-data)))]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set root-id)))))))

View file

@ -13,7 +13,6 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.uuid :as uuid]
[app.main.ui.shapes.attrs :as usa]
[app.util.data :as ud]
[app.util.object :as obj]
@ -21,33 +20,12 @@
[cuerdas.core :as str]
[rumext.alpha :as mf]))
;; Graphic tags
(defonce graphic-element? #{ :circle :ellipse :image :line :path :polygon :polyline :rect :text #_"use"})
;; Context to store a re-mapping of the ids
(def svg-ids-ctx (mf/create-context nil))
(defn generate-id-mapping [content]
(letfn [(visit-node [result node]
(let [element-id (get-in node [:attrs :id])
result (cond-> result
element-id (assoc element-id (str (uuid/next))))]
(reduce visit-node result (:content node))))]
(visit-node {} content)))
(defonce replace-regex #"[^#]*#([^)\s]+).*")
(defn replace-attrs-ids
"Replaces the ids inside a property"
[ids-mapping attrs]
(letfn [(replace-ids [key val]
(if (map? val)
(cd/mapm replace-ids val)
(let [[_ from-id] (re-matches replace-regex val)]
(if (and from-id (contains? ids-mapping from-id))
(str/replace val from-id (get ids-mapping from-id))
val))))]
(cd/mapm replace-ids attrs)))
(defn set-styles [attrs shape]
(let [custom-attrs (usa/extract-style-attrs shape)
attrs (cond-> attrs
@ -58,6 +36,20 @@
(obj/merge! custom-attrs)
(obj/set! "style" style))))
(defn translate-shape [attrs shape]
(let [{svg-width :width svg-height :height :as root-shape} (:root-attrs shape)
{:keys [x y width height]} (:selrect shape)
transform (->> (:transform attrs "")
(str (gmt/multiply
(gmt/matrix)
(gsh/transform-matrix shape)
(gmt/translate-matrix (gpt/point x y))
(gmt/scale-matrix (gpt/point (/ width svg-width) (/ height svg-height))))
" "))]
(cond-> attrs
(and root-shape (graphic-element? (-> shape :content :tag)))
(assoc :transform transform))))
(mf/defc svg-root
{::mf/wrap-props false}
[props]
@ -68,7 +60,7 @@
{:keys [x y width height]} shape
{:keys [tag attrs] :as content} (:content shape)
ids-mapping (mf/use-memo #(generate-id-mapping content))
ids-mapping (mf/use-memo #(usvg/generate-id-mapping content))
attrs (-> (set-styles attrs shape)
(obj/set! "x" x)
@ -91,11 +83,16 @@
{:keys [attrs tag]} content
ids-mapping (mf/use-ctx svg-ids-ctx)
attrs (mf/use-memo #(replace-attrs-ids ids-mapping attrs))
element-id (get-in content [:attrs :id])
attrs (mf/use-memo #(usvg/replace-attrs-ids attrs ids-mapping))
attrs (translate-shape attrs shape)
element-id (get-in content [:attrs :id])
attrs (cond-> (set-styles attrs shape)
element-id (obj/set! "id" (get ids-mapping element-id)))]
(and element-id (contains? ids-mapping element-id))
(obj/set! "id" (get ids-mapping element-id)))
{:keys [x y width height]} (:selrect shape)]
[:> (name tag) attrs children]))
(defn svg-raw-shape [shape-wrapper]

View file

@ -57,19 +57,18 @@
:shape shape
:childs childs}]
(when (= tag :svg)
[:rect.group-actions
{:x x
:y y
:transform transform
:width width
:height height
:fill "transparent"
:on-mouse-down handle-mouse-down
:on-double-click handle-double-click
:on-context-menu handle-context-menu
:on-pointer-over handle-pointer-enter
:on-pointer-out handle-pointer-leave}])]
[:rect.actions
{:x x
:y y
:transform transform
:width width
:height height
:fill "transparent"
:on-mouse-down handle-mouse-down
:on-double-click handle-double-click
:on-context-menu handle-context-menu
:on-pointer-over handle-pointer-enter
:on-pointer-out handle-pointer-leave}]]
;; We cannot wrap inside groups the shapes that go inside the defs tag
;; we use the context so we know when we should not render the container

View file

@ -99,11 +99,9 @@
(when (contains? svg-elements tag)
[:*
(when (= tag :svg)
[:*
[:& measures-menu {:ids ids
:type type
:values measure-values}]])
[:& measures-menu {:ids ids
:type type
:values measure-values}]
[:& fill-menu {:ids ids
:type type

View file

@ -9,8 +9,12 @@
(ns app.util.svg
(:require
[app.common.uuid :as uuid]
[app.common.data :as cd]
[cuerdas.core :as str]))
(defonce replace-regex #"[^#]*#([^)\s]+).*")
(defn clean-attrs
"Transforms attributes to their react equivalent"
[attrs]
@ -40,3 +44,33 @@
(->> attrs
(map map-fn)
(into {}))))
(defn replace-attrs-ids
"Replaces the ids inside a property"
[attrs ids-mapping]
(if (and ids-mapping (not (empty? ids-mapping)))
(letfn [(replace-ids [key val]
(cond
(map? val)
(cd/mapm replace-ids val)
(and (= key :id) (contains? ids-mapping val))
(get ids-mapping val)
:else
(let [[_ from-id] (re-matches replace-regex val)]
(if (and from-id (contains? ids-mapping from-id))
(str/replace val from-id (get ids-mapping from-id))
val))))]
(cd/mapm replace-ids attrs))
;; Ids-mapping is null
attrs))
(defn generate-id-mapping [content]
(letfn [(visit-node [result node]
(let [element-id (get-in node [:attrs :id])
result (cond-> result
element-id (assoc element-id (str (uuid/next))))]
(reduce visit-node result (:content node))))]
(visit-node {} content)))

View file

@ -4,6 +4,7 @@ const plugins = [
{removeViewBox: false},
{moveElemsAttrsToGroup: false},
{convertStyleToAttrs: false},
{removeUselessDefs: false},
{convertPathData: {
lineShorthands: false,
curveSmoothShorthands: false,