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)] height (d/parse-integer height-str)]
[width height])) [width height]))
(defn tag-name [{:keys [tag]}] (defn tag-name [tag]
(cond (string? tag) tag (cond (string? tag) tag
(keyword? tag) (name tag) (keyword? tag) (name tag)
(nil? tag) "node" (nil? tag) "node"
@ -63,7 +63,8 @@
(d/any-key? attrs :stroke :stroke-width :stroke-opacity) (d/any-key? attrs :stroke :stroke-width :stroke-opacity)
(setup-stroke attrs))) (setup-stroke attrs)))
(defn create-raw-svg [name frame-id x y width height data] (defn create-raw-svg [name frame-id svg-data element-data]
(let [{:keys [x y width height]} svg-data]
(-> {:id (uuid/next) (-> {:id (uuid/next)
:type :svg-raw :type :svg-raw
:name name :name name
@ -72,8 +73,25 @@
:height height :height height
:x x :x x
:y y :y y
:content (if (map? data) (update data :attrs usvg/clean-attrs) data)} :root-attrs (select-keys svg-data [:width :height])
(gsh/setup-selrect))) :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}] (defn parse-path [name frame-id {:keys [attrs] :as data}]
(let [content (ugp/path->content (:d attrs)) (let [content (ugp/path->content (:d attrs))
@ -89,38 +107,19 @@
(add-style-attributes data)))) (add-style-attributes data))))
(defn parse-svg-element [root-shape data unames] (defn parse-svg-element [frame-id svg-data element-data unames]
(let [root-id (:id root-shape) (let [{:keys [tag]} element-data
frame-id (:frame-id root-shape) name (dwc/generate-unique-name unames (str "svg-" (tag-name tag)))]
{: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 (case tag
;; :rect (parse-rect data) ;; :rect (parse-rect data)
;; :path (parse-path name frame-id data) ;; :path (parse-path name frame-id data)
(create-raw-svg name frame-id x y width height data))] (create-raw-svg name frame-id svg-data element-data))))
(-> shape (defn add-svg-child-changes [page-id objects selected frame-id parent-id svg-data ids-mappings result [index data]]
(assoc :svg-id root-id)))) (let [[unames [rchs uchs]] result
data (update data :attrs usvg/replace-attrs-ids ids-mappings)
(defn svg-uploaded [data x y] shape (parse-svg-element frame-id svg-data data unames)
(ptk/reify ::svg-uploaded
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
frame-id (cp/frame-id-by-position objects {:x x :y y})
selected (get-in state [:workspace-local :selected])
[width height] (svg-dimensions 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) shape-id (:id shape)
[rch1 uch1] (dwc/add-shape-changes page-id objects selected shape) [rch1 uch1] (dwc/add-shape-changes page-id objects selected shape)
@ -135,19 +134,42 @@
;; Careful! the undo changes are concatenated reversed (we undo in reverse order ;; Careful! the undo changes are concatenated reversed (we undo in reverse order
changes [(d/concat rchs rch1 rch2) (d/concat uch1 uchs)] changes [(d/concat rchs rch1 rch2) (d/concat uch1 uchs)]
unames (conj unames (:name shape))] unames (conj unames (:name shape))
(reduce (partial add-svg-child shape-id root-shape) [unames changes] (d/enumerate content)))) 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]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
frame-id (cp/frame-id-by-position objects {:x x :y y})
selected (get-in state [:workspace-local :selected])
[width height] (svg-dimensions svg-data)
x (- x (/ width 2))
y (- y (/ height 2))
unames (dwc/retrieve-used-names objects) 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)) (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) root-id (:id root-shape)
changes (dwc/add-shape-changes page-id objects selected 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}) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set root-id))))))) (dwc/select-shapes (d/ordered-set root-id)))))))

View file

@ -13,7 +13,6 @@
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.uuid :as uuid]
[app.main.ui.shapes.attrs :as usa] [app.main.ui.shapes.attrs :as usa]
[app.util.data :as ud] [app.util.data :as ud]
[app.util.object :as obj] [app.util.object :as obj]
@ -21,33 +20,12 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.alpha :as mf])) [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 ;; Context to store a re-mapping of the ids
(def svg-ids-ctx (mf/create-context nil)) (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] (defn set-styles [attrs shape]
(let [custom-attrs (usa/extract-style-attrs shape) (let [custom-attrs (usa/extract-style-attrs shape)
attrs (cond-> attrs attrs (cond-> attrs
@ -58,6 +36,20 @@
(obj/merge! custom-attrs) (obj/merge! custom-attrs)
(obj/set! "style" style)))) (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/defc svg-root
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
@ -68,7 +60,7 @@
{:keys [x y width height]} shape {:keys [x y width height]} shape
{:keys [tag attrs] :as content} (:content 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) attrs (-> (set-styles attrs shape)
(obj/set! "x" x) (obj/set! "x" x)
@ -91,11 +83,16 @@
{:keys [attrs tag]} content {:keys [attrs tag]} content
ids-mapping (mf/use-ctx svg-ids-ctx) 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) 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])) [:> (name tag) attrs children]))
(defn svg-raw-shape [shape-wrapper] (defn svg-raw-shape [shape-wrapper]

View file

@ -57,8 +57,7 @@
:shape shape :shape shape
:childs childs}] :childs childs}]
(when (= tag :svg) [:rect.actions
[:rect.group-actions
{:x x {:x x
:y y :y y
:transform transform :transform transform
@ -69,7 +68,7 @@
:on-double-click handle-double-click :on-double-click handle-double-click
:on-context-menu handle-context-menu :on-context-menu handle-context-menu
:on-pointer-over handle-pointer-enter :on-pointer-over handle-pointer-enter
:on-pointer-out handle-pointer-leave}])] :on-pointer-out handle-pointer-leave}]]
;; We cannot wrap inside groups the shapes that go inside the defs tag ;; 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 ;; we use the context so we know when we should not render the container

View file

@ -98,12 +98,10 @@
stroke-values (get-stroke-values shape)] stroke-values (get-stroke-values shape)]
(when (contains? svg-elements tag) (when (contains? svg-elements tag)
[:*
(when (= tag :svg)
[:* [:*
[:& measures-menu {:ids ids [:& measures-menu {:ids ids
:type type :type type
:values measure-values}]]) :values measure-values}]
[:& fill-menu {:ids ids [:& fill-menu {:ids ids
:type type :type type

View file

@ -9,8 +9,12 @@
(ns app.util.svg (ns app.util.svg
(:require (:require
[app.common.uuid :as uuid]
[app.common.data :as cd]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(defonce replace-regex #"[^#]*#([^)\s]+).*")
(defn clean-attrs (defn clean-attrs
"Transforms attributes to their react equivalent" "Transforms attributes to their react equivalent"
[attrs] [attrs]
@ -40,3 +44,33 @@
(->> attrs (->> attrs
(map map-fn) (map map-fn)
(into {})))) (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}, {removeViewBox: false},
{moveElemsAttrsToGroup: false}, {moveElemsAttrsToGroup: false},
{convertStyleToAttrs: false}, {convertStyleToAttrs: false},
{removeUselessDefs: false},
{convertPathData: { {convertPathData: {
lineShorthands: false, lineShorthands: false,
curveSmoothShorthands: false, curveSmoothShorthands: false,