mirror of
https://github.com/penpot/penpot.git
synced 2025-02-08 08:09:14 -05:00
♻️ Move wasm shape proxy impl to frontend
This commit is contained in:
parent
884414d5cf
commit
99905d2286
10 changed files with 398 additions and 405 deletions
|
@ -26,7 +26,6 @@
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape-tree :as ctst]
|
||||||
[app.common.types.shape.impl :as shape.impl]
|
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.types.token-theme :as ctot]
|
[app.common.types.token-theme :as ctot]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
@ -541,7 +540,8 @@
|
||||||
(when verify?
|
(when verify?
|
||||||
(check-changes! items))
|
(check-changes! items))
|
||||||
|
|
||||||
(binding [*touched-changes* (volatile! #{}) shape.impl/*wasm-sync* true]
|
(binding [*touched-changes* (volatile! #{})
|
||||||
|
cts/*wasm-sync* true]
|
||||||
(let [result (reduce #(or (process-change %1 %2) %1) data items)
|
(let [result (reduce #(or (process-change %1 %2) %1) data items)
|
||||||
result (reduce process-touched-change result @*touched-changes*)]
|
result (reduce process-touched-change result @*touched-changes*)]
|
||||||
;; Validate result shapes (only on the backend)
|
;; Validate result shapes (only on the backend)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.common.types.shape
|
(ns app.common.types.shape
|
||||||
(:require
|
(:require
|
||||||
|
#?(:clj [app.common.fressian :as fres])
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
|
@ -13,15 +14,16 @@
|
||||||
[app.common.geom.proportions :as gpr]
|
[app.common.geom.proportions :as gpr]
|
||||||
[app.common.geom.rect :as grc]
|
[app.common.geom.rect :as grc]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.record :as cr]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.schema.generators :as sg]
|
[app.common.schema.generators :as sg]
|
||||||
|
[app.common.transit :as t]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
[app.common.types.grid :as ctg]
|
[app.common.types.grid :as ctg]
|
||||||
[app.common.types.plugins :as ctpg]
|
[app.common.types.plugins :as ctpg]
|
||||||
[app.common.types.shape.attrs :refer [default-color]]
|
[app.common.types.shape.attrs :refer [default-color]]
|
||||||
[app.common.types.shape.blur :as ctsb]
|
[app.common.types.shape.blur :as ctsb]
|
||||||
[app.common.types.shape.export :as ctse]
|
[app.common.types.shape.export :as ctse]
|
||||||
[app.common.types.shape.impl :as impl]
|
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
[app.common.types.shape.layout :as ctsl]
|
[app.common.types.shape.layout :as ctsl]
|
||||||
[app.common.types.shape.path :as ctsp]
|
[app.common.types.shape.path :as ctsp]
|
||||||
|
@ -31,9 +33,31 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[clojure.set :as set]))
|
[clojure.set :as set]))
|
||||||
|
|
||||||
|
(defonce ^:dynamic *wasm-sync* false)
|
||||||
|
|
||||||
|
(defonce wasm-enabled? false)
|
||||||
|
(defonce wasm-create-shape (constantly nil))
|
||||||
|
|
||||||
|
;; Marker protocol
|
||||||
|
(defprotocol IShape)
|
||||||
|
|
||||||
|
(cr/defrecord Shape [id name type x y width height rotation selrect points
|
||||||
|
transform transform-inverse parent-id frame-id flip-x flip-y]
|
||||||
|
IShape)
|
||||||
|
|
||||||
(defn shape?
|
(defn shape?
|
||||||
[o]
|
[o]
|
||||||
(impl/shape? o))
|
#?(:cljs (implements? IShape o)
|
||||||
|
:clj (instance? Shape o)))
|
||||||
|
|
||||||
|
(defn create-shape
|
||||||
|
"A low level function that creates a Shape data structure
|
||||||
|
from a attrs map without performing other transformations"
|
||||||
|
[attrs]
|
||||||
|
#?(:cljs (if ^boolean wasm-enabled?
|
||||||
|
(^function wasm-create-shape attrs)
|
||||||
|
(map->Shape attrs))
|
||||||
|
:clj (map->Shape attrs)))
|
||||||
|
|
||||||
(def stroke-caps-line #{:round :square})
|
(def stroke-caps-line #{:round :square})
|
||||||
(def stroke-caps-marker #{:line-arrow :triangle-arrow :square-marker :circle-marker :diamond-marker})
|
(def stroke-caps-marker #{:line-arrow :triangle-arrow :square-marker :circle-marker :diamond-marker})
|
||||||
|
@ -242,7 +266,7 @@
|
||||||
(defn- decode-shape
|
(defn- decode-shape
|
||||||
[o]
|
[o]
|
||||||
(if (map? o)
|
(if (map? o)
|
||||||
(impl/map->Shape o)
|
(create-shape o)
|
||||||
o))
|
o))
|
||||||
|
|
||||||
(defn- shape-generator
|
(defn- shape-generator
|
||||||
|
@ -266,7 +290,7 @@
|
||||||
(= type :bool))
|
(= type :bool))
|
||||||
(merge attrs1 shape attrs3)
|
(merge attrs1 shape attrs3)
|
||||||
(merge attrs1 shape attrs2 attrs3)))))
|
(merge attrs1 shape attrs2 attrs3)))))
|
||||||
(sg/fmap impl/map->Shape)))
|
(sg/fmap create-shape)))
|
||||||
|
|
||||||
(def schema:shape
|
(def schema:shape
|
||||||
[:and {:title "Shape"
|
[:and {:title "Shape"
|
||||||
|
@ -453,12 +477,6 @@
|
||||||
;; NOTE: used for create ephimeral shapes for multiple selection
|
;; NOTE: used for create ephimeral shapes for multiple selection
|
||||||
:multiple minimal-multiple-attrs))
|
:multiple minimal-multiple-attrs))
|
||||||
|
|
||||||
(defn create-shape
|
|
||||||
"A low level function that creates a Shape data structure
|
|
||||||
from a attrs map without performing other transformations"
|
|
||||||
[attrs]
|
|
||||||
(impl/create-shape attrs))
|
|
||||||
|
|
||||||
(defn- make-minimal-shape
|
(defn- make-minimal-shape
|
||||||
[type]
|
[type]
|
||||||
(let [type (if (= type :curve) :path type)
|
(let [type (if (= type :curve) :path type)
|
||||||
|
@ -476,7 +494,7 @@
|
||||||
(assoc :parent-id uuid/zero)
|
(assoc :parent-id uuid/zero)
|
||||||
(assoc :rotation 0))]
|
(assoc :rotation 0))]
|
||||||
|
|
||||||
(impl/create-shape attrs)))
|
(create-shape attrs)))
|
||||||
|
|
||||||
(defn setup-rect
|
(defn setup-rect
|
||||||
"Initializes the selrect and points for a shape."
|
"Initializes the selrect and points for a shape."
|
||||||
|
@ -531,3 +549,17 @@
|
||||||
(assoc :transform-inverse (gmt/matrix)))
|
(assoc :transform-inverse (gmt/matrix)))
|
||||||
(gpr/setup-proportions))))
|
(gpr/setup-proportions))))
|
||||||
|
|
||||||
|
;; --- SHAPE SERIALIZATION
|
||||||
|
|
||||||
|
(t/add-handlers!
|
||||||
|
{:id "shape"
|
||||||
|
:class Shape
|
||||||
|
:wfn #(into {} %)
|
||||||
|
:rfn create-shape})
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(fres/add-handlers!
|
||||||
|
{:name "penpot/shape"
|
||||||
|
:class Shape
|
||||||
|
:wfn fres/write-map-like
|
||||||
|
:rfn (comp map->Shape fres/read-map-like)}))
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
;; 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) KALEIDOS INC
|
|
||||||
|
|
||||||
(ns app.common.types.shape.impl
|
|
||||||
(:require
|
|
||||||
#?(:clj [app.common.fressian :as fres])
|
|
||||||
#?(:cljs [cuerdas.core :as str])
|
|
||||||
[app.common.record :as cr]
|
|
||||||
[app.common.transit :as t]
|
|
||||||
[clojure.core :as c]))
|
|
||||||
|
|
||||||
(defonce ^:dynamic *wasm-sync* false)
|
|
||||||
(defonce enabled-wasm-ready-shape false)
|
|
||||||
(defonce wasm-create-shape (constantly nil))
|
|
||||||
(defonce wasm-use-shape (constantly nil))
|
|
||||||
(defonce wasm-set-shape-selrect (constantly nil))
|
|
||||||
(defonce wasm-set-shape-transform (constantly nil))
|
|
||||||
(defonce wasm-set-shape-rotation (constantly nil))
|
|
||||||
(defonce wasm-set-shape-fills (constantly nil))
|
|
||||||
(defonce wasm-set-shape-blend-mode (constantly nil))
|
|
||||||
(defonce wasm-set-shape-children (constantly nil))
|
|
||||||
|
|
||||||
(cr/defrecord Shape [id name type x y width height rotation selrect points
|
|
||||||
transform transform-inverse parent-id frame-id flip-x flip-y])
|
|
||||||
|
|
||||||
(declare ^:private impl-assoc)
|
|
||||||
(declare ^:private impl-conj)
|
|
||||||
(declare ^:private impl-dissoc)
|
|
||||||
|
|
||||||
;; TODO: implement lazy MapEntry
|
|
||||||
|
|
||||||
#?(:cljs
|
|
||||||
(deftype ShapeProxy [delegate]
|
|
||||||
Object
|
|
||||||
(toString [coll]
|
|
||||||
(str "{" (str/join ", " (for [[k v] coll] (str k " " v))) "}"))
|
|
||||||
|
|
||||||
(equiv [this other]
|
|
||||||
(-equiv this other))
|
|
||||||
|
|
||||||
IWithMeta
|
|
||||||
(-with-meta [_ meta]
|
|
||||||
(ShapeProxy. (with-meta delegate meta)))
|
|
||||||
|
|
||||||
IMeta
|
|
||||||
(-meta [_] (meta delegate))
|
|
||||||
|
|
||||||
ICollection
|
|
||||||
(-conj [coll entry]
|
|
||||||
(impl-conj coll entry))
|
|
||||||
|
|
||||||
IEquiv
|
|
||||||
(-equiv [coll other]
|
|
||||||
(c/equiv-map coll other))
|
|
||||||
|
|
||||||
IHash
|
|
||||||
(-hash [coll] (hash (into {} coll)))
|
|
||||||
|
|
||||||
ISequential
|
|
||||||
|
|
||||||
ISeqable
|
|
||||||
(-seq [_]
|
|
||||||
(c/-seq delegate))
|
|
||||||
|
|
||||||
ICounted
|
|
||||||
(-count [_]
|
|
||||||
(+ 1 (count delegate)))
|
|
||||||
|
|
||||||
ILookup
|
|
||||||
(-lookup [coll k]
|
|
||||||
(-lookup coll k nil))
|
|
||||||
|
|
||||||
(-lookup [_ k not-found]
|
|
||||||
(c/-lookup delegate k not-found))
|
|
||||||
|
|
||||||
IFind
|
|
||||||
(-find [_ k]
|
|
||||||
(c/-find delegate k))
|
|
||||||
|
|
||||||
IAssociative
|
|
||||||
(-assoc [coll k v]
|
|
||||||
(impl-assoc coll k v))
|
|
||||||
|
|
||||||
(-contains-key? [_ k]
|
|
||||||
(contains? delegate k))
|
|
||||||
|
|
||||||
IMap
|
|
||||||
(-dissoc [coll k]
|
|
||||||
(impl-dissoc coll k))
|
|
||||||
|
|
||||||
IFn
|
|
||||||
(-invoke [coll k]
|
|
||||||
(-lookup coll k))
|
|
||||||
|
|
||||||
(-invoke [coll k not-found]
|
|
||||||
(-lookup coll k not-found))
|
|
||||||
|
|
||||||
IPrintWithWriter
|
|
||||||
(-pr-writer [_ writer _]
|
|
||||||
(-write writer (str "#penpot/shape " (:id delegate))))))
|
|
||||||
|
|
||||||
(defn shape?
|
|
||||||
[o]
|
|
||||||
#?(:clj (instance? Shape o)
|
|
||||||
:cljs (or (instance? Shape o)
|
|
||||||
(instance? ShapeProxy o))))
|
|
||||||
|
|
||||||
;; --- SHAPE IMPL
|
|
||||||
|
|
||||||
#?(:cljs
|
|
||||||
(defn- impl-assoc
|
|
||||||
[coll k v]
|
|
||||||
(when *wasm-sync*
|
|
||||||
(wasm-use-shape (:id coll))
|
|
||||||
(case k
|
|
||||||
:selrect (wasm-set-shape-selrect v)
|
|
||||||
:rotation (wasm-set-shape-rotation v)
|
|
||||||
:transform (wasm-set-shape-transform v)
|
|
||||||
:fills (wasm-set-shape-fills v)
|
|
||||||
:blend-mode (wasm-set-shape-blend-mode v)
|
|
||||||
:shapes (wasm-set-shape-children v)
|
|
||||||
nil))
|
|
||||||
(let [delegate (.-delegate ^ShapeProxy coll)
|
|
||||||
delegate' (assoc delegate k v)]
|
|
||||||
(if (identical? delegate' delegate)
|
|
||||||
coll
|
|
||||||
(ShapeProxy. delegate')))))
|
|
||||||
|
|
||||||
#?(:cljs
|
|
||||||
(defn- impl-dissoc
|
|
||||||
[coll k]
|
|
||||||
(let [delegate (.-delegate ^ShapeProxy coll)
|
|
||||||
delegate' (dissoc delegate k)]
|
|
||||||
(if (identical? delegate delegate')
|
|
||||||
coll
|
|
||||||
(ShapeProxy. delegate')))))
|
|
||||||
|
|
||||||
#?(:cljs
|
|
||||||
(defn- impl-conj
|
|
||||||
[coll entry]
|
|
||||||
(if (vector? entry)
|
|
||||||
(-assoc coll (-nth entry 0) (-nth entry 1))
|
|
||||||
(loop [ret coll es (seq entry)]
|
|
||||||
(if (nil? es)
|
|
||||||
ret
|
|
||||||
(let [e (first es)]
|
|
||||||
(if (vector? e)
|
|
||||||
(recur (-assoc ret (-nth e 0) (-nth e 1))
|
|
||||||
(next es))
|
|
||||||
(throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))))
|
|
||||||
|
|
||||||
(defn create-shape
|
|
||||||
"Instanciate a shape from a map"
|
|
||||||
[attrs]
|
|
||||||
#?(:cljs
|
|
||||||
(if enabled-wasm-ready-shape
|
|
||||||
(ShapeProxy. attrs)
|
|
||||||
(map->Shape attrs))
|
|
||||||
|
|
||||||
:clj (map->Shape attrs)))
|
|
||||||
|
|
||||||
;; --- SHAPE SERIALIZATION
|
|
||||||
|
|
||||||
(t/add-handlers!
|
|
||||||
{:id "shape"
|
|
||||||
:class Shape
|
|
||||||
:wfn #(into {} %)
|
|
||||||
:rfn create-shape})
|
|
||||||
|
|
||||||
#?(:cljs
|
|
||||||
(t/add-handlers!
|
|
||||||
{:id "shape"
|
|
||||||
:class ShapeProxy
|
|
||||||
:wfn #(into {} %)
|
|
||||||
:rfn create-shape}))
|
|
||||||
|
|
||||||
#?(:clj
|
|
||||||
(fres/add-handlers!
|
|
||||||
{:name "penpot/shape"
|
|
||||||
:class Shape
|
|
||||||
:wfn fres/write-map-like
|
|
||||||
:rfn (comp create-shape fres/read-map-like)}))
|
|
|
@ -76,7 +76,7 @@
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.main.worker :as uw]
|
[app.main.worker :as uw]
|
||||||
[app.render-wasm :as render.wasm]
|
[app.render-wasm :as wasm]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.globals :as ug]
|
[app.util.globals :as ug]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
|
@ -270,7 +270,7 @@
|
||||||
;; load. We need to wait the promise to be resolved
|
;; load. We need to wait the promise to be resolved
|
||||||
;; before continue with the next workspace loading
|
;; before continue with the next workspace loading
|
||||||
;; steps
|
;; steps
|
||||||
(->> (rx/from render.wasm/module)
|
(->> (rx/from wasm/module)
|
||||||
(rx/ignore))
|
(rx/ignore))
|
||||||
(->> (rp/cmd! :get-team {:id (:team-id project)})
|
(->> (rp/cmd! :get-team {:id (:team-id project)})
|
||||||
(rx/mapcat (fn [team]
|
(rx/mapcat (fn [team]
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.render-wasm :as render.wasm]
|
[app.render-wasm :as wasm]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
|
@ -126,8 +126,8 @@
|
||||||
(effect [_ state _]
|
(effect [_ state _]
|
||||||
(let [features (get-team-enabled-features state)]
|
(let [features (get-team-enabled-features state)]
|
||||||
(if (contains? features "render-wasm/v1")
|
(if (contains? features "render-wasm/v1")
|
||||||
(render.wasm/initialize true)
|
(wasm/initialize true)
|
||||||
(render.wasm/initialize false))
|
(wasm/initialize false))
|
||||||
|
|
||||||
(log/inf :hint "initialized"
|
(log/inf :hint "initialized"
|
||||||
:enabled (str/join "," features)
|
:enabled (str/join "," features)
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.logic.shapes :as cls]
|
[app.common.logic.shapes :as cls]
|
||||||
[app.common.types.shape.impl :as shape.impl]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.common.types.shape.radius :as ctsr]
|
[app.common.types.shape.radius :as ctsr]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
@ -249,7 +249,7 @@
|
||||||
(fn [value attr]
|
(fn [value attr]
|
||||||
(let [token-value (wtc/maybe-resolve-token-value value)
|
(let [token-value (wtc/maybe-resolve-token-value value)
|
||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
(binding [shape.impl/*wasm-sync* true]
|
(binding [cts/*wasm-sync* true]
|
||||||
(if-not design-tokens?
|
(if-not design-tokens?
|
||||||
(st/emit! (udw/trigger-bounding-box-cloaking ids)
|
(st/emit! (udw/trigger-bounding-box-cloaking ids)
|
||||||
(udw/update-dimensions ids attr (or token-value value)))
|
(udw/update-dimensions ids attr (or token-value value)))
|
||||||
|
@ -285,7 +285,7 @@
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
(fn [value attr]
|
(fn [value attr]
|
||||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||||
(binding [shape.impl/*wasm-sync* true]
|
(binding [cts/*wasm-sync* true]
|
||||||
(doall (map #(do-position-change %1 %2 value attr) shapes frames)))))
|
(doall (map #(do-position-change %1 %2 value attr) shapes frames)))))
|
||||||
|
|
||||||
;; ROTATION
|
;; ROTATION
|
||||||
|
@ -294,7 +294,7 @@
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
(fn [value]
|
(fn [value]
|
||||||
(binding [shape.impl/*wasm-sync* true]
|
(binding [cts/*wasm-sync* true]
|
||||||
(st/emit! (udw/trigger-bounding-box-cloaking ids)
|
(st/emit! (udw/trigger-bounding-box-cloaking ids)
|
||||||
(udw/increase-rotation ids value)))))
|
(udw/increase-rotation ids value)))))
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape-tree :as ctt]
|
[app.common.types.shape-tree :as ctt]
|
||||||
[app.common.types.shape.impl :as shape.impl]
|
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
[app.main.ui.workspace.viewport.utils :as utils]
|
[app.main.ui.workspace.viewport.utils :as utils]
|
||||||
[app.main.ui.workspace.viewport.viewport-ref :refer [create-viewport-ref]]
|
[app.main.ui.workspace.viewport.viewport-ref :refer [create-viewport-ref]]
|
||||||
[app.main.ui.workspace.viewport.widgets :as widgets]
|
[app.main.ui.workspace.viewport.widgets :as widgets]
|
||||||
[app.render-wasm :as render.wasm]
|
[app.render-wasm.api :as wasm.api]
|
||||||
[app.util.debug :as dbg]
|
[app.util.debug :as dbg]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
|
@ -113,7 +113,7 @@
|
||||||
text-modifiers (mf/deref refs/workspace-text-modifier)
|
text-modifiers (mf/deref refs/workspace-text-modifier)
|
||||||
|
|
||||||
objects-modified (mf/with-memo [base-objects text-modifiers modifiers]
|
objects-modified (mf/with-memo [base-objects text-modifiers modifiers]
|
||||||
(binding [shape.impl/*wasm-sync* true]
|
(binding [cts/*wasm-sync* true]
|
||||||
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers)))
|
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers)))
|
||||||
|
|
||||||
selected-shapes (keep (d/getf objects-modified) selected)
|
selected-shapes (keep (d/getf objects-modified) selected)
|
||||||
|
@ -275,26 +275,26 @@
|
||||||
|
|
||||||
(mf/with-effect []
|
(mf/with-effect []
|
||||||
(when-let [canvas (mf/ref-val canvas-ref)]
|
(when-let [canvas (mf/ref-val canvas-ref)]
|
||||||
(->> render.wasm/module
|
(->> wasm.api/module
|
||||||
(p/fmap (fn [ready?]
|
(p/fmap (fn [ready?]
|
||||||
(when ready?
|
(when ready?
|
||||||
(reset! canvas-init? true)
|
(reset! canvas-init? true)
|
||||||
(render.wasm/assign-canvas canvas)))))
|
(wasm.api/assign-canvas canvas)))))
|
||||||
(fn []
|
(fn []
|
||||||
(render.wasm/clear-canvas))))
|
(wasm.api/clear-canvas))))
|
||||||
|
|
||||||
(mf/with-effect [base-objects canvas-init?]
|
(mf/with-effect [base-objects canvas-init?]
|
||||||
(when @canvas-init?
|
(when @canvas-init?
|
||||||
(render.wasm/set-objects base-objects)
|
(wasm.api/set-objects base-objects)
|
||||||
(render.wasm/draw-objects zoom vbox)))
|
(wasm.api/draw-objects zoom vbox)))
|
||||||
|
|
||||||
(mf/with-effect [modifiers canvas-init?]
|
(mf/with-effect [modifiers canvas-init?]
|
||||||
(when (and @canvas-init? modifiers)
|
(when (and @canvas-init? modifiers)
|
||||||
(render.wasm/draw-objects zoom vbox)))
|
(wasm.api/draw-objects zoom vbox)))
|
||||||
|
|
||||||
(mf/with-effect [vbox canvas-init?]
|
(mf/with-effect [vbox canvas-init?]
|
||||||
(let [frame-id (when @canvas-init? (render.wasm/draw-objects zoom vbox))]
|
(let [frame-id (when @canvas-init? (wasm.api/draw-objects zoom vbox))]
|
||||||
(partial render.wasm/cancel-draw frame-id)))
|
(partial wasm.api/cancel-draw frame-id)))
|
||||||
|
|
||||||
(hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool drawing-path?)
|
(hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool drawing-path?)
|
||||||
(hooks/setup-viewport-size vport viewport-ref)
|
(hooks/setup-viewport-size vport viewport-ref)
|
||||||
|
|
|
@ -7,193 +7,13 @@
|
||||||
(ns app.render-wasm
|
(ns app.render-wasm
|
||||||
"A WASM based render API"
|
"A WASM based render API"
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.types.shape :as shape]
|
||||||
[app.common.types.shape.impl :as ctsi]
|
[app.render-wasm.api :as api]
|
||||||
[app.common.uuid :as uuid]
|
[app.render-wasm.shape :as wasm.shape]))
|
||||||
[app.config :as cf]
|
|
||||||
[app.render-wasm.helpers :as h]
|
(def module api/module)
|
||||||
[promesa.core :as p]))
|
|
||||||
|
|
||||||
(defn initialize
|
(defn initialize
|
||||||
[enabled?]
|
[enabled?]
|
||||||
(set! app.common.types.shape.impl/enabled-wasm-ready-shape enabled?))
|
(set! app.common.types.shape/wasm-enabled? enabled?)
|
||||||
|
(set! app.common.types.shape/wasm-create-shape wasm.shape/create-shape))
|
||||||
(defonce internal-module #js {})
|
|
||||||
|
|
||||||
(defn create-shape
|
|
||||||
[id]
|
|
||||||
(let [buffer (uuid/get-u32 id)]
|
|
||||||
(h/call internal-module "_create_shape"
|
|
||||||
(aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))
|
|
||||||
|
|
||||||
(defn use-shape
|
|
||||||
[id]
|
|
||||||
(let [buffer (uuid/get-u32 id)]
|
|
||||||
(h/call internal-module "_use_shape"
|
|
||||||
(aget buffer 0)
|
|
||||||
(aget buffer 1)
|
|
||||||
(aget buffer 2)
|
|
||||||
(aget buffer 3))))
|
|
||||||
|
|
||||||
(defn set-shape-selrect
|
|
||||||
[selrect]
|
|
||||||
(h/call internal-module "_set_shape_selrect"
|
|
||||||
(dm/get-prop selrect :x1)
|
|
||||||
(dm/get-prop selrect :y1)
|
|
||||||
(dm/get-prop selrect :x2)
|
|
||||||
(dm/get-prop selrect :y2)))
|
|
||||||
|
|
||||||
(defn set-shape-transform
|
|
||||||
[transform]
|
|
||||||
(h/call internal-module "_set_shape_transform"
|
|
||||||
(dm/get-prop transform :a)
|
|
||||||
(dm/get-prop transform :b)
|
|
||||||
(dm/get-prop transform :c)
|
|
||||||
(dm/get-prop transform :d)
|
|
||||||
(dm/get-prop transform :e)
|
|
||||||
(dm/get-prop transform :f)))
|
|
||||||
|
|
||||||
(defn set-shape-rotation
|
|
||||||
[rotation]
|
|
||||||
(h/call internal-module "_set_shape_rotation" rotation))
|
|
||||||
|
|
||||||
(defn set-shape-children
|
|
||||||
[shape-ids]
|
|
||||||
(h/call internal-module "_clear_shape_children")
|
|
||||||
(run! (fn [id]
|
|
||||||
(let [buffer (uuid/get-u32 id)]
|
|
||||||
(h/call internal-module "_add_shape_child"
|
|
||||||
(aget buffer 0)
|
|
||||||
(aget buffer 1)
|
|
||||||
(aget buffer 2)
|
|
||||||
(aget buffer 3))))
|
|
||||||
shape-ids))
|
|
||||||
|
|
||||||
(defn set-shape-fills
|
|
||||||
[fills]
|
|
||||||
(h/call internal-module "_clear_shape_fills")
|
|
||||||
(run! (fn [fill]
|
|
||||||
(let [opacity (:fill-opacity fill)
|
|
||||||
color (:fill-color fill)]
|
|
||||||
(when ^boolean color
|
|
||||||
(let [rgb (js/parseInt (subs color 1) 16)
|
|
||||||
r (bit-shift-right rgb 16)
|
|
||||||
g (bit-and (bit-shift-right rgb 8) 255)
|
|
||||||
b (bit-and rgb 255)]
|
|
||||||
(h/call internal-module "_add_shape_solid_fill" r g b opacity)))))
|
|
||||||
fills))
|
|
||||||
|
|
||||||
(defn- translate-blend-mode
|
|
||||||
[blend-mode]
|
|
||||||
(case blend-mode
|
|
||||||
:normal 3
|
|
||||||
:darken 16
|
|
||||||
:multiply 24
|
|
||||||
:color-burn 19
|
|
||||||
:lighten 17
|
|
||||||
:screen 14
|
|
||||||
:color-dodge 18
|
|
||||||
:overlay 15
|
|
||||||
:soft-light 21
|
|
||||||
:hard-light 20
|
|
||||||
:difference 22
|
|
||||||
:exclusion 23
|
|
||||||
:hue 25
|
|
||||||
:saturation 26
|
|
||||||
:color 27
|
|
||||||
:luminosity 28
|
|
||||||
3))
|
|
||||||
|
|
||||||
(defn set-shape-blend-mode
|
|
||||||
[blend-mode]
|
|
||||||
;; These values correspond to skia::BlendMode representation
|
|
||||||
;; https://rust-skia.github.io/doc/skia_safe/enum.BlendMode.html
|
|
||||||
(h/call internal-module "_set_shape_blend_mode" (translate-blend-mode blend-mode)))
|
|
||||||
|
|
||||||
(defn set-objects
|
|
||||||
[objects]
|
|
||||||
(let [shapes (into [] (vals objects))
|
|
||||||
|
|
||||||
total-shapes (count shapes)]
|
|
||||||
(loop [index 0]
|
|
||||||
(when (< index total-shapes)
|
|
||||||
(let [shape (nth shapes index)
|
|
||||||
id (dm/get-prop shape :id)
|
|
||||||
selrect (dm/get-prop shape :selrect)
|
|
||||||
rotation (dm/get-prop shape :rotation)
|
|
||||||
transform (dm/get-prop shape :transform)
|
|
||||||
fills (dm/get-prop shape :fills)
|
|
||||||
children (dm/get-prop shape :shapes)
|
|
||||||
blend-mode (dm/get-prop shape :blend-mode)]
|
|
||||||
(use-shape id)
|
|
||||||
(set-shape-selrect selrect)
|
|
||||||
(set-shape-rotation rotation)
|
|
||||||
(set-shape-transform transform)
|
|
||||||
(set-shape-fills fills)
|
|
||||||
(set-shape-blend-mode blend-mode)
|
|
||||||
(set-shape-children children)
|
|
||||||
(recur (inc index)))))))
|
|
||||||
|
|
||||||
(defn draw-objects
|
|
||||||
[zoom vbox]
|
|
||||||
(js/requestAnimationFrame
|
|
||||||
(fn []
|
|
||||||
(let [pan-x (- (dm/get-prop vbox :x))
|
|
||||||
pan-y (- (dm/get-prop vbox :y))]
|
|
||||||
(h/call internal-module "_draw_all_shapes" zoom pan-x pan-y)))))
|
|
||||||
|
|
||||||
(defn cancel-draw
|
|
||||||
[frame-id]
|
|
||||||
(when (some? frame-id)
|
|
||||||
(js/cancelAnimationFrame frame-id)))
|
|
||||||
|
|
||||||
(def ^:private canvas-options
|
|
||||||
#js {:antialias true
|
|
||||||
:depth true
|
|
||||||
:stencil true
|
|
||||||
:alpha true})
|
|
||||||
|
|
||||||
(defn clear-canvas
|
|
||||||
[]
|
|
||||||
;; TODO: perform corresponding cleaning
|
|
||||||
)
|
|
||||||
|
|
||||||
(defn assign-canvas
|
|
||||||
[canvas]
|
|
||||||
(let [gl (unchecked-get internal-module "GL")
|
|
||||||
init-fn (unchecked-get internal-module "_init")
|
|
||||||
|
|
||||||
context (.getContext ^js canvas "webgl2" canvas-options)
|
|
||||||
|
|
||||||
;; Register the context with emscripten
|
|
||||||
handle (.registerContext ^js gl context #js {"majorVersion" 2})]
|
|
||||||
(.makeContextCurrent ^js gl handle)
|
|
||||||
;; Initialize Skia
|
|
||||||
(^function init-fn (.-width ^js canvas)
|
|
||||||
(.-height ^js canvas))
|
|
||||||
(set! (.-width canvas) (.-clientWidth ^js canvas))
|
|
||||||
(set! (.-height canvas) (.-clientHeight ^js canvas))))
|
|
||||||
|
|
||||||
(defonce module
|
|
||||||
(if (exists? js/dynamicImport)
|
|
||||||
(let [uri (cf/resolve-static-asset "js/render_wasm.js")]
|
|
||||||
(->> (js/dynamicImport (str uri))
|
|
||||||
(p/mcat (fn [module]
|
|
||||||
(let [default (unchecked-get module "default")]
|
|
||||||
(default))))
|
|
||||||
(p/fmap (fn [module]
|
|
||||||
(set! internal-module module)
|
|
||||||
true))
|
|
||||||
(p/merr (fn [cause]
|
|
||||||
(js/console.error cause)
|
|
||||||
(p/resolved false)))))
|
|
||||||
(p/resolved false)))
|
|
||||||
|
|
||||||
(set! app.common.types.shape.impl/wasm-create-shape create-shape)
|
|
||||||
(set! app.common.types.shape.impl/wasm-use-shape use-shape)
|
|
||||||
(set! app.common.types.shape.impl/wasm-set-shape-selrect set-shape-selrect)
|
|
||||||
(set! app.common.types.shape.impl/wasm-set-shape-transform set-shape-transform)
|
|
||||||
(set! app.common.types.shape.impl/wasm-set-shape-rotation set-shape-rotation)
|
|
||||||
(set! app.common.types.shape.impl/wasm-set-shape-fills set-shape-fills)
|
|
||||||
(set! app.common.types.shape.impl/wasm-set-shape-blend-mode set-shape-blend-mode)
|
|
||||||
(set! app.common.types.shape.impl/wasm-set-shape-children set-shape-children)
|
|
||||||
|
|
185
frontend/src/app/render_wasm/api.cljs
Normal file
185
frontend/src/app/render_wasm/api.cljs
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
;; 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) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.render-wasm.api
|
||||||
|
"A WASM based render API"
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cf]
|
||||||
|
[app.render-wasm.helpers :as h]
|
||||||
|
[promesa.core :as p]))
|
||||||
|
|
||||||
|
(defonce internal-module #js {})
|
||||||
|
|
||||||
|
(defn create-shape
|
||||||
|
[id]
|
||||||
|
(let [buffer (uuid/get-u32 id)]
|
||||||
|
(h/call internal-module "_create_shape"
|
||||||
|
(aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))
|
||||||
|
|
||||||
|
(defn use-shape
|
||||||
|
[id]
|
||||||
|
(let [buffer (uuid/get-u32 id)]
|
||||||
|
(h/call internal-module "_use_shape"
|
||||||
|
(aget buffer 0)
|
||||||
|
(aget buffer 1)
|
||||||
|
(aget buffer 2)
|
||||||
|
(aget buffer 3))))
|
||||||
|
|
||||||
|
(defn set-shape-selrect
|
||||||
|
[selrect]
|
||||||
|
(h/call internal-module "_set_shape_selrect"
|
||||||
|
(dm/get-prop selrect :x1)
|
||||||
|
(dm/get-prop selrect :y1)
|
||||||
|
(dm/get-prop selrect :x2)
|
||||||
|
(dm/get-prop selrect :y2)))
|
||||||
|
|
||||||
|
(defn set-shape-transform
|
||||||
|
[transform]
|
||||||
|
(h/call internal-module "_set_shape_transform"
|
||||||
|
(dm/get-prop transform :a)
|
||||||
|
(dm/get-prop transform :b)
|
||||||
|
(dm/get-prop transform :c)
|
||||||
|
(dm/get-prop transform :d)
|
||||||
|
(dm/get-prop transform :e)
|
||||||
|
(dm/get-prop transform :f)))
|
||||||
|
|
||||||
|
(defn set-shape-rotation
|
||||||
|
[rotation]
|
||||||
|
(h/call internal-module "_set_shape_rotation" rotation))
|
||||||
|
|
||||||
|
(defn set-shape-children
|
||||||
|
[shape-ids]
|
||||||
|
(h/call internal-module "_clear_shape_children")
|
||||||
|
(run! (fn [id]
|
||||||
|
(let [buffer (uuid/get-u32 id)]
|
||||||
|
(h/call internal-module "_add_shape_child"
|
||||||
|
(aget buffer 0)
|
||||||
|
(aget buffer 1)
|
||||||
|
(aget buffer 2)
|
||||||
|
(aget buffer 3))))
|
||||||
|
shape-ids))
|
||||||
|
|
||||||
|
(defn set-shape-fills
|
||||||
|
[fills]
|
||||||
|
(h/call internal-module "_clear_shape_fills")
|
||||||
|
(run! (fn [fill]
|
||||||
|
(let [opacity (:fill-opacity fill)
|
||||||
|
color (:fill-color fill)]
|
||||||
|
(when ^boolean color
|
||||||
|
(let [rgb (js/parseInt (subs color 1) 16)
|
||||||
|
r (bit-shift-right rgb 16)
|
||||||
|
g (bit-and (bit-shift-right rgb 8) 255)
|
||||||
|
b (bit-and rgb 255)]
|
||||||
|
(h/call internal-module "_add_shape_solid_fill" r g b opacity)))))
|
||||||
|
fills))
|
||||||
|
|
||||||
|
(defn- translate-blend-mode
|
||||||
|
[blend-mode]
|
||||||
|
(case blend-mode
|
||||||
|
:normal 3
|
||||||
|
:darken 16
|
||||||
|
:multiply 24
|
||||||
|
:color-burn 19
|
||||||
|
:lighten 17
|
||||||
|
:screen 14
|
||||||
|
:color-dodge 18
|
||||||
|
:overlay 15
|
||||||
|
:soft-light 21
|
||||||
|
:hard-light 20
|
||||||
|
:difference 22
|
||||||
|
:exclusion 23
|
||||||
|
:hue 25
|
||||||
|
:saturation 26
|
||||||
|
:color 27
|
||||||
|
:luminosity 28
|
||||||
|
3))
|
||||||
|
|
||||||
|
(defn set-shape-blend-mode
|
||||||
|
[blend-mode]
|
||||||
|
;; These values correspond to skia::BlendMode representation
|
||||||
|
;; https://rust-skia.github.io/doc/skia_safe/enum.BlendMode.html
|
||||||
|
(h/call internal-module "_set_shape_blend_mode" (translate-blend-mode blend-mode)))
|
||||||
|
|
||||||
|
(defn set-objects
|
||||||
|
[objects]
|
||||||
|
(let [shapes (into [] (vals objects))
|
||||||
|
|
||||||
|
total-shapes (count shapes)]
|
||||||
|
(loop [index 0]
|
||||||
|
(when (< index total-shapes)
|
||||||
|
(let [shape (nth shapes index)
|
||||||
|
id (dm/get-prop shape :id)
|
||||||
|
selrect (dm/get-prop shape :selrect)
|
||||||
|
rotation (dm/get-prop shape :rotation)
|
||||||
|
transform (dm/get-prop shape :transform)
|
||||||
|
fills (dm/get-prop shape :fills)
|
||||||
|
children (dm/get-prop shape :shapes)
|
||||||
|
blend-mode (dm/get-prop shape :blend-mode)]
|
||||||
|
(use-shape id)
|
||||||
|
(set-shape-selrect selrect)
|
||||||
|
(set-shape-rotation rotation)
|
||||||
|
(set-shape-transform transform)
|
||||||
|
(set-shape-fills fills)
|
||||||
|
(set-shape-blend-mode blend-mode)
|
||||||
|
(set-shape-children children)
|
||||||
|
(recur (inc index)))))))
|
||||||
|
|
||||||
|
(defn draw-objects
|
||||||
|
[zoom vbox]
|
||||||
|
(js/requestAnimationFrame
|
||||||
|
(fn []
|
||||||
|
(let [pan-x (- (dm/get-prop vbox :x))
|
||||||
|
pan-y (- (dm/get-prop vbox :y))]
|
||||||
|
(h/call internal-module "_draw_all_shapes" zoom pan-x pan-y)))))
|
||||||
|
|
||||||
|
(defn cancel-draw
|
||||||
|
[frame-id]
|
||||||
|
(when (some? frame-id)
|
||||||
|
(js/cancelAnimationFrame frame-id)))
|
||||||
|
|
||||||
|
(def ^:private canvas-options
|
||||||
|
#js {:antialias true
|
||||||
|
:depth true
|
||||||
|
:stencil true
|
||||||
|
:alpha true})
|
||||||
|
|
||||||
|
(defn clear-canvas
|
||||||
|
[]
|
||||||
|
;; TODO: perform corresponding cleaning
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn assign-canvas
|
||||||
|
[canvas]
|
||||||
|
(let [gl (unchecked-get internal-module "GL")
|
||||||
|
init-fn (unchecked-get internal-module "_init")
|
||||||
|
|
||||||
|
context (.getContext ^js canvas "webgl2" canvas-options)
|
||||||
|
|
||||||
|
;; Register the context with emscripten
|
||||||
|
handle (.registerContext ^js gl context #js {"majorVersion" 2})]
|
||||||
|
(.makeContextCurrent ^js gl handle)
|
||||||
|
;; Initialize Skia
|
||||||
|
(^function init-fn (.-width ^js canvas)
|
||||||
|
(.-height ^js canvas))
|
||||||
|
(set! (.-width canvas) (.-clientWidth ^js canvas))
|
||||||
|
(set! (.-height canvas) (.-clientHeight ^js canvas))))
|
||||||
|
|
||||||
|
(defonce module
|
||||||
|
(if (exists? js/dynamicImport)
|
||||||
|
(let [uri (cf/resolve-static-asset "js/render_wasm.js")]
|
||||||
|
(->> (js/dynamicImport (str uri))
|
||||||
|
(p/mcat (fn [module]
|
||||||
|
(let [default (unchecked-get module "default")]
|
||||||
|
(default))))
|
||||||
|
(p/fmap (fn [module]
|
||||||
|
(set! internal-module module)
|
||||||
|
true))
|
||||||
|
(p/merr (fn [cause]
|
||||||
|
(js/console.error cause)
|
||||||
|
(p/resolved false)))))
|
||||||
|
(p/resolved false)))
|
141
frontend/src/app/render_wasm/shape.cljs
Normal file
141
frontend/src/app/render_wasm/shape.cljs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
;; 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) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.render-wasm.shape
|
||||||
|
(:require
|
||||||
|
[app.common.transit :as t]
|
||||||
|
[app.common.types.shape :as shape]
|
||||||
|
[app.render-wasm.api :as api]
|
||||||
|
[clojure.core :as c]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
(declare ^:private impl-assoc)
|
||||||
|
(declare ^:private impl-conj)
|
||||||
|
(declare ^:private impl-dissoc)
|
||||||
|
|
||||||
|
(deftype ShapeProxy [delegate]
|
||||||
|
Object
|
||||||
|
(toString [coll]
|
||||||
|
(str "{" (str/join ", " (for [[k v] coll] (str k " " v))) "}"))
|
||||||
|
|
||||||
|
(equiv [this other]
|
||||||
|
(-equiv this other))
|
||||||
|
|
||||||
|
;; Marker protocol
|
||||||
|
shape/IShape
|
||||||
|
|
||||||
|
IWithMeta
|
||||||
|
(-with-meta [_ meta]
|
||||||
|
(ShapeProxy. (with-meta delegate meta)))
|
||||||
|
|
||||||
|
IMeta
|
||||||
|
(-meta [_] (meta delegate))
|
||||||
|
|
||||||
|
ICollection
|
||||||
|
(-conj [coll entry]
|
||||||
|
(impl-conj coll entry))
|
||||||
|
|
||||||
|
IEquiv
|
||||||
|
(-equiv [coll other]
|
||||||
|
(c/equiv-map coll other))
|
||||||
|
|
||||||
|
IHash
|
||||||
|
(-hash [coll] (hash (into {} coll)))
|
||||||
|
|
||||||
|
ISequential
|
||||||
|
|
||||||
|
ISeqable
|
||||||
|
(-seq [_]
|
||||||
|
(c/-seq delegate))
|
||||||
|
|
||||||
|
ICounted
|
||||||
|
(-count [_]
|
||||||
|
(+ 1 (count delegate)))
|
||||||
|
|
||||||
|
ILookup
|
||||||
|
(-lookup [coll k]
|
||||||
|
(-lookup coll k nil))
|
||||||
|
|
||||||
|
(-lookup [_ k not-found]
|
||||||
|
(c/-lookup delegate k not-found))
|
||||||
|
|
||||||
|
IFind
|
||||||
|
(-find [_ k]
|
||||||
|
(c/-find delegate k))
|
||||||
|
|
||||||
|
IAssociative
|
||||||
|
(-assoc [coll k v]
|
||||||
|
(impl-assoc coll k v))
|
||||||
|
|
||||||
|
(-contains-key? [_ k]
|
||||||
|
(contains? delegate k))
|
||||||
|
|
||||||
|
IMap
|
||||||
|
(-dissoc [coll k]
|
||||||
|
(impl-dissoc coll k))
|
||||||
|
|
||||||
|
IFn
|
||||||
|
(-invoke [coll k]
|
||||||
|
(-lookup coll k))
|
||||||
|
|
||||||
|
(-invoke [coll k not-found]
|
||||||
|
(-lookup coll k not-found))
|
||||||
|
|
||||||
|
IPrintWithWriter
|
||||||
|
(-pr-writer [_ writer _]
|
||||||
|
(-write writer (str "#penpot/shape " (:id delegate)))))
|
||||||
|
|
||||||
|
;; --- SHAPE IMPL
|
||||||
|
|
||||||
|
(defn- impl-assoc
|
||||||
|
[self k v]
|
||||||
|
(when ^boolean shape/*wasm-sync*
|
||||||
|
(api/use-shape (:id self))
|
||||||
|
(case k
|
||||||
|
:selrect (api/set-shape-selrect v)
|
||||||
|
:rotation (api/set-shape-rotation v)
|
||||||
|
:transform (api/set-shape-transform v)
|
||||||
|
:fills (api/set-shape-fills v)
|
||||||
|
:blend-mode (api/set-shape-blend-mode v)
|
||||||
|
:shapes (api/set-shape-children v)
|
||||||
|
nil))
|
||||||
|
(let [delegate (.-delegate ^ShapeProxy self)
|
||||||
|
delegate' (assoc delegate k v)]
|
||||||
|
(if (identical? delegate' delegate)
|
||||||
|
self
|
||||||
|
(ShapeProxy. delegate'))))
|
||||||
|
|
||||||
|
(defn- impl-dissoc
|
||||||
|
[self k]
|
||||||
|
(let [delegate (.-delegate ^ShapeProxy self)
|
||||||
|
delegate' (dissoc delegate k)]
|
||||||
|
(if (identical? delegate delegate')
|
||||||
|
self
|
||||||
|
(ShapeProxy. delegate'))))
|
||||||
|
|
||||||
|
(defn- impl-conj
|
||||||
|
[self entry]
|
||||||
|
(if (vector? entry)
|
||||||
|
(-assoc self (-nth entry 0) (-nth entry 1))
|
||||||
|
(loop [ret self es (seq entry)]
|
||||||
|
(if (nil? es)
|
||||||
|
ret
|
||||||
|
(let [e (first es)]
|
||||||
|
(if (vector? e)
|
||||||
|
(recur (-assoc ret (-nth e 0) (-nth e 1))
|
||||||
|
(next es))
|
||||||
|
(throw (js/Error. "conj on a map takes map entries or seqables of map entries"))))))))
|
||||||
|
|
||||||
|
(defn create-shape
|
||||||
|
"Instanciate a shape from a map"
|
||||||
|
[attrs]
|
||||||
|
(ShapeProxy. attrs))
|
||||||
|
|
||||||
|
(t/add-handlers!
|
||||||
|
;; We only add a write handler, read handler uses the dynamic dispatch
|
||||||
|
{:id "shape"
|
||||||
|
:class ShapeProxy
|
||||||
|
:wfn #(into {} %)})
|
Loading…
Add table
Reference in a new issue