Plugins create svg shapes

This commit is contained in:
alonso.torres 2024-04-29 15:43:48 +02:00
parent 21d38a058b
commit 67d48435e7
6 changed files with 128 additions and 103 deletions

View file

@ -22,6 +22,7 @@
[app.common.svg :as csvg] [app.common.svg :as csvg]
[app.common.svg.path :as path] [app.common.svg.path :as path]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(def default-rect (def default-rect
@ -78,67 +79,68 @@
(declare parse-svg-element) (declare parse-svg-element)
(defn create-svg-shapes (defn create-svg-shapes
[svg-data {:keys [x y]} objects frame-id parent-id selected center?] ([svg-data pos objects frame-id parent-id selected center?]
(let [[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data) (create-svg-shapes (uuid/next) svg-data pos objects frame-id parent-id selected center?))
([id svg-data {:keys [x y]} objects frame-id parent-id selected center?]
(let [[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data)
unames (cfh/get-used-names objects)
svg-name (str/replace (:name svg-data) ".svg" "")
unames (cfh/get-used-names objects) svg-data (-> svg-data
svg-name (str/replace (:name svg-data) ".svg" "") (assoc :x (mth/round
(if center?
(- x vb-x (/ vb-width 2))
x)))
(assoc :y (mth/round
(if center?
(- y vb-y (/ vb-height 2))
y)))
(assoc :offset-x vb-x)
(assoc :offset-y vb-y)
(assoc :width vb-width)
(assoc :height vb-height)
(assoc :name svg-name))
svg-data (-> svg-data [def-nodes svg-data]
(assoc :x (mth/round (-> svg-data
(if center? (csvg/fix-default-values)
(- x vb-x (/ vb-width 2)) (csvg/fix-percents)
x))) (csvg/extract-defs))
(assoc :y (mth/round
(if center?
(- y vb-y (/ vb-height 2))
y)))
(assoc :offset-x vb-x)
(assoc :offset-y vb-y)
(assoc :width vb-width)
(assoc :height vb-height)
(assoc :name svg-name))
[def-nodes svg-data] ;; In penpot groups have the size of their children. To
(-> svg-data ;; respect the imported svg size and empty space let's create
(csvg/fix-default-values) ;; a transparent shape as background to respect the imported
(csvg/fix-percents) ;; size
(csvg/extract-defs)) background
{:tag :rect
:attrs {:x (dm/str vb-x)
:y (dm/str vb-y)
:width (dm/str vb-width)
:height (dm/str vb-height)
:fill "none"
:id "base-background"}
:hidden true
:content []}
;; In penpot groups have the size of their children. To svg-data (-> svg-data
;; respect the imported svg size and empty space let's create (assoc :defs def-nodes)
;; a transparent shape as background to respect the imported (assoc :content (into [background] (:content svg-data))))
;; size
background
{:tag :rect
:attrs {:x (dm/str vb-x)
:y (dm/str vb-y)
:width (dm/str vb-width)
:height (dm/str vb-height)
:fill "none"
:id "base-background"}
:hidden true
:content []}
svg-data (-> svg-data root-shape (create-svg-root id frame-id parent-id svg-data)
(assoc :defs def-nodes) root-id (:id root-shape)
(assoc :content (into [background] (:content svg-data))))
root-shape (create-svg-root frame-id parent-id svg-data) ;; Create the root shape
root-id (:id root-shape) root-attrs (-> (:attrs svg-data)
(csvg/format-styles))
;; Create the root shape [_ children]
root-attrs (-> (:attrs svg-data) (reduce (partial create-svg-children objects selected frame-id root-id svg-data)
(csvg/format-styles)) [unames []]
(d/enumerate (->> (:content svg-data)
(mapv #(csvg/inherit-attributes root-attrs %)))))]
[_ children] [root-shape children])))
(reduce (partial create-svg-children objects selected frame-id root-id svg-data)
[unames []]
(d/enumerate (->> (:content svg-data)
(mapv #(csvg/inherit-attributes root-attrs %)))))]
[root-shape children]))
(defn create-raw-svg (defn create-raw-svg
[name frame-id {:keys [x y width height offset-x offset-y]} {:keys [attrs] :as data}] [name frame-id {:keys [x y width height offset-x offset-y]} {:keys [attrs] :as data}]
@ -157,12 +159,13 @@
:svg-viewbox vbox}))) :svg-viewbox vbox})))
(defn create-svg-root (defn create-svg-root
[frame-id parent-id {:keys [name x y width height offset-x offset-y attrs]}] [id frame-id parent-id {:keys [name x y width height offset-x offset-y attrs]}]
(let [props (-> (dissoc attrs :viewBox :view-box :xmlns) (let [props (-> (dissoc attrs :viewBox :view-box :xmlns)
(d/without-keys csvg/inheritable-props) (d/without-keys csvg/inheritable-props)
(csvg/attrs->props))] (csvg/attrs->props))]
(cts/setup-shape (cts/setup-shape
{:type :group {:id id
:type :group
:name name :name name
:frame-id frame-id :frame-id frame-id
:parent-id parent-id :parent-id parent-id

View file

@ -203,7 +203,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [selected (wsh/lookup-selected state)] (let [selected (wsh/lookup-selected state)]
(rx/of group-shapes nil selected))))) (rx/of (group-shapes nil selected))))))
(defn ungroup-shapes (defn ungroup-shapes
[ids & {:keys [change-selection?] :or {change-selection? false}}] [ids & {:keys [change-selection?] :or {change-selection? false}}]

View file

@ -459,3 +459,12 @@
(rx/tap on-success) (rx/tap on-success)
(rx/catch on-error) (rx/catch on-error)
(rx/finalize #(st/emit! (msg/hide-tag :media-loading))))))))) (rx/finalize #(st/emit! (msg/hide-tag :media-loading)))))))))
(defn create-svg-shape
[id name svg-string position]
(ptk/reify ::create-svg-shape
ptk/WatchEvent
(watch [_ _ _]
(->> (svg->clj [name svg-string])
(rx/take 1)
(rx/map #(svg/add-svg-shapes id % position {:change-selection? false}))))))

View file

@ -13,6 +13,7 @@
[app.common.svg :as csvg] [app.common.svg :as csvg]
[app.common.svg.shapes-builder :as csvg.shapes-builder] [app.common.svg.shapes-builder :as csvg.shapes-builder]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
@ -60,52 +61,58 @@
(rx/reduce conj {}))) (rx/reduce conj {})))
(defn add-svg-shapes (defn add-svg-shapes
[svg-data position] ([svg-data position]
(ptk/reify ::add-svg-shapes (add-svg-shapes nil svg-data position nil))
ptk/WatchEvent
(watch [it state _]
(try
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (ctst/top-nested-frame objects position)
selected (wsh/lookup-selected state)
base (cfh/get-base-shape objects selected)
selected-id (first selected) ([id svg-data position {:keys [change-selection?] :or {change-selection? false}}]
selected-frame? (and (= 1 (count selected)) (ptk/reify ::add-svg-shapes
(= :frame (dm/get-in objects [selected-id :type]))) ptk/WatchEvent
(watch [it state _]
(try
(let [id (d/nilv id (uuid/next))
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (ctst/top-nested-frame objects position)
selected (wsh/lookup-selected state)
base (cfh/get-base-shape objects selected)
parent-id (if (or selected-frame? (empty? selected)) selected-id (first selected)
frame-id selected-frame? (and (= 1 (count selected))
(:parent-id base)) (= :frame (dm/get-in objects [selected-id :type])))
[new-shape new-children] parent-id (if (or selected-frame? (empty? selected))
(csvg.shapes-builder/create-svg-shapes svg-data position objects frame-id parent-id selected true) frame-id
(:parent-id base))
changes (-> (pcb/empty-changes it page-id) [new-shape new-children]
(pcb/with-objects objects) (csvg.shapes-builder/create-svg-shapes id svg-data position objects frame-id parent-id selected true)
(pcb/add-object new-shape))
changes (reduce (fn [changes new-child] changes (-> (pcb/empty-changes it page-id)
(pcb/add-object changes new-child)) (pcb/with-objects objects)
changes (pcb/add-object new-shape))
new-children)
changes (pcb/resize-parents changes changes (reduce (fn [changes new-child]
(->> (:redo-changes changes) (pcb/add-object changes new-child))
(filter #(= :add-obj (:type %))) changes
(map :id) new-children)
(reverse)
(vec)))
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id) changes (pcb/resize-parents changes
(dch/commit-changes changes) (->> (:redo-changes changes)
(dws/select-shapes (d/ordered-set (:id new-shape))) (filter #(= :add-obj (:type %)))
(ptk/data-event :layout/update {:ids [(:id new-shape)]}) (map :id)
(dwu/commit-undo-transaction undo-id))) (reverse)
(vec)))
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(when change-selection?
(dws/select-shapes (d/ordered-set (:id new-shape))))
(ptk/data-event :layout/update {:ids [(:id new-shape)]})
(dwu/commit-undo-transaction undo-id)))
(catch :default cause
(js/console.log (.-stack cause))
(rx/throw {:type :svg-parser
:data cause})))))))
(catch :default cause
(js/console.log (.-stack cause))
(rx/throw {:type :svg-parser
:data cause}))))))

View file

@ -9,6 +9,7 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.changes-builder :as cb] [app.common.files.changes-builder :as cb]
[app.common.geom.point :as gpt]
[app.common.record :as cr] [app.common.record :as cr]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
@ -127,7 +128,14 @@
(createRectangle (createRectangle
[_] [_]
(create-shape :rect)) (create-shape :rect))
)
(createShapeFromSvg
[_ svg-string]
(let [id (uuid/next)
page-id (:current-page-id @st/state)]
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
(shape/data->shape-proxy
(dm/get-in @st/state [:workspace-data :pages-index page-id :objects id])))))
(defn create-context (defn create-context
[] []

View file

@ -67,12 +67,12 @@
(appendChild [self child] (appendChild [self child]
(let [parent-id (get-data self :id) (let [parent-id (get-data self :id)
child-id (uuid/uuid (obj/get child "id"))] child-id (uuid/uuid (obj/get child "id"))]
(st/emit! (udw/relocate-shapes #{ child-id } parent-id 0)))) (st/emit! (udw/relocate-shapes #{child-id} parent-id 0))))
(insertChild [self index child] (insertChild [self index child]
(let [parent-id (get-data self :id) (let [parent-id (get-data self :id)
child-id (uuid/uuid (obj/get child "id"))] child-id (uuid/uuid (obj/get child "id"))]
(st/emit! (udw/relocate-shapes #{ child-id } parent-id index))))) (st/emit! (udw/relocate-shapes #{child-id} parent-id index)))))
(crc/define-properties! (crc/define-properties!
ShapeProxy ShapeProxy
@ -124,16 +124,14 @@
:set (fn [self value] :set (fn [self value]
(let [id (get-data self :id) (let [id (get-data self :id)
value (mapv #(utils/from-js %) value)] value (mapv #(utils/from-js %) value)]
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value))))) (st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))}
}
{:name "strokes" {:name "strokes"
:get #(get-state % :strokes make-strokes) :get #(get-state % :strokes make-strokes)
:set (fn [self value] :set (fn [self value]
(let [id (get-data self :id) (let [id (get-data self :id)
value (mapv #(utils/from-js %) value)] value (mapv #(utils/from-js %) value)]
(st/emit! (dwc/update-shapes [id] #(assoc % :strokes value))))) (st/emit! (dwc/update-shapes [id] #(assoc % :strokes value)))))})
})
(cond-> (or (cfh/frame-shape? data) (cfh/group-shape? data) (cfh/svg-raw-shape? data) (cfh/bool-shape? data)) (cond-> (or (cfh/frame-shape? data) (cfh/group-shape? data) (cfh/svg-raw-shape? data) (cfh/bool-shape? data))
(crc/add-properties! (crc/add-properties!