diff --git a/CHANGES.md b/CHANGES.md index 37cd31218..c257bb64e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -45,6 +45,7 @@ ### :bug: Bugs fixed +- Avoid numeric inputs to change its value without focusing them [Taiga #3140](https://tree.taiga.io/project/penpot/issue/3140) - Fix comments modal when changing pages [Taiga #2597](https://tree.taiga.io/project/penpot/issue/2508) - Copy paste inside a text layer leaves pasted text transparent [Taiga #3096](https://tree.taiga.io/project/penpot/issue/3096) - On dashboard enter on empty search refresh the page [Taiga #2597](https://tree.taiga.io/project/penpot/issue/2597) @@ -68,6 +69,8 @@ - Fix paste ordering for frames not being respected [Taiga #3097](https://tree.taiga.io/project/penpot/issue/3097) - Improved command support for MacOS [Taiga #2789](https://tree.taiga.io/project/penpot/issue/2789) - Fix shift+2 shortcut in MacOS with non-english keyboards [Taiga #3038](https://tree.taiga.io/project/penpot/issue/3038) +- Some fixes to SVG imports [Taiga #3122](https://tree.taiga.io/project/penpot/issue/3122) [#1720](https://github.com/penpot/penpot/issues/1720) [Taiga #2884](https://tree.taiga.io/project/penpot/issue/2884) +- Fix drag guides to delete target area [#1679](https://github.com/penpot/penpot/issues/1679) ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index d60ffdc23..afc1bb6bb 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -105,6 +105,7 @@ (dm/export gco/transform-points) (dm/export gpr/make-rect) +(dm/export gpr/make-selrect) (dm/export gpr/rect->selrect) (dm/export gpr/rect->points) (dm/export gpr/points->selrect) diff --git a/common/src/app/common/path/shapes_to_path.cljc b/common/src/app/common/path/shapes_to_path.cljc index bb4e439c3..37bc286a2 100644 --- a/common/src/app/common/path/shapes_to_path.cljc +++ b/common/src/app/common/path/shapes_to_path.cljc @@ -22,7 +22,7 @@ (def dissoc-attrs [:x :y :width :height :rx :ry :r1 :r2 :r3 :r4 - :metadata :shapes]) + :metadata]) (def allowed-transform-types #{:rect @@ -199,7 +199,6 @@ (map #(convert-to-path % objects))) bool-type (:bool-type shape) content (pb/content-bool bool-type (mapv :content children))] - (-> shape (assoc :type :path) (assoc :content content) diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index 94c5a1e88..2291c7ca0 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -1209,8 +1209,7 @@ } .modal-container { - background-image: url("../images/deco-left.png"), - url("../images/deco-right.png"); + background-image: url("../images/deco-left.png"), url("../images/deco-right.png"); background-repeat: no-repeat; background-position: 10% 50px, 90% 50px; background-size: 65px; @@ -1237,18 +1236,8 @@ --checkbox-border-radius: 3px; --dropdown-option-background-color: rgba(0, 195, 139, 1); --dropdown-option-active-background-color: rgba(0, 138, 98, 1); - --invalid-field-background-color: rgba( - 238.51780000000002, - 205.7178, - 204.11780000000002, - 1 - ); - --message-fail-background-color: rgba( - 238.51780000000002, - 205.7178, - 204.11780000000002, - 1 - ); + --invalid-field-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1); + --message-fail-background-color: rgba(238.51780000000002, 205.7178, 204.11780000000002, 1); --message-success-background-color: rgba(171, 232, 197, 1); } } @@ -1487,7 +1476,7 @@ } &.scale { - width: 25%; + width: 6.25rem; } &.scale, diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 67fe9cb10..0336f2bab 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -104,8 +104,9 @@ (update-in state path cp/process-changes redo-changes false) - (catch :default e - (vreset! error e) + (catch :default err + (log/error :js/error err) + (vreset! error err) state)))) ptk/WatchEvent diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index e5e5f491b..dad3d08d9 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -259,10 +259,7 @@ (assoc :stroke-color-gradient (:gradient attrs)) (contains? attrs :opacity) - (assoc :stroke-opacity (:opacity attrs)) - - :always - (d/without-nils)) + (assoc :stroke-opacity (:opacity attrs))) attrs (merge attrs color-attrs)] @@ -276,7 +273,10 @@ (assoc :stroke-style :solid) (not (contains? new-attrs :stroke-alignment)) - (assoc :stroke-alignment :center))] + (assoc :stroke-alignment :center) + + :always + (d/without-nils))] (assoc-in shape [:strokes index] new-attrs))))))))) (defn add-stroke diff --git a/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs b/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs index d0d7b6ce8..6e9fb0b26 100644 --- a/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs +++ b/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs @@ -30,7 +30,7 @@ changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) - (pcb/remove-objects children-ids) - (pcb/update-shapes selected #(upsp/convert-to-path % objects)))] + (pcb/update-shapes selected #(upsp/convert-to-path % objects)) + (pcb/remove-objects children-ids))] (rx/of (dch/commit-changes changes)))))) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 6998bce8e..549148bb9 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -70,32 +70,30 @@ :else (str tag)))) (defn setup-fill [shape] - (if (some? (:fills shape)) - shape - (cond-> shape - ;; Color present as attribute - (uc/color? (str/trim (get-in shape [:svg-attrs :fill]))) - (-> (update :svg-attrs dissoc :fill) - (assoc-in [:fills 0 :fill-color] (-> (get-in shape [:svg-attrs :fill]) - (str/trim) - (uc/parse-color)))) + (cond-> shape + ;; Color present as attribute + (uc/color? (str/trim (get-in shape [:svg-attrs :fill]))) + (-> (update :svg-attrs dissoc :fill) + (assoc-in [:fills 0 :fill-color] (-> (get-in shape [:svg-attrs :fill]) + (str/trim) + (uc/parse-color)))) - ;; Color present as style - (uc/color? (str/trim (get-in shape [:svg-attrs :style :fill]))) - (-> (update-in [:svg-attrs :style] dissoc :fill) - (assoc-in [:fills 0 :fill-color] (-> (get-in shape [:svg-attrs :style :fill]) - (str/trim) - (uc/parse-color)))) + ;; Color present as style + (uc/color? (str/trim (get-in shape [:svg-attrs :style :fill]))) + (-> (update-in [:svg-attrs :style] dissoc :fill) + (assoc-in [:fills 0 :fill-color] (-> (get-in shape [:svg-attrs :style :fill]) + (str/trim) + (uc/parse-color)))) - (get-in shape [:svg-attrs :fill-opacity]) - (-> (update :svg-attrs dissoc :fill-opacity) - (assoc-in [:fills 0 :fill-opacity] (-> (get-in shape [:svg-attrs :fill-opacity]) - (d/parse-double)))) + (get-in shape [:svg-attrs :fill-opacity]) + (-> (update :svg-attrs dissoc :fill-opacity) + (assoc-in [:fills 0 :fill-opacity] (-> (get-in shape [:svg-attrs :fill-opacity]) + (d/parse-double)))) - (get-in shape [:svg-attrs :style :fill-opacity]) - (-> (update-in [:svg-attrs :style] dissoc :fill-opacity) - (assoc-in [:fills 0 :fill-opacity] (-> (get-in shape [:svg-attrs :style :fill-opacity]) - (d/parse-double))))))) + (get-in shape [:svg-attrs :style :fill-opacity]) + (-> (update-in [:svg-attrs :style] dissoc :fill-opacity) + (assoc-in [:fills 0 :fill-opacity] (-> (get-in shape [:svg-attrs :style :fill-opacity]) + (d/parse-double)))))) (defn setup-stroke [shape] (let [stroke-linecap (-> (or (get-in shape [:svg-attrs :stroke-linecap]) diff --git a/frontend/src/app/main/ui/components/numeric_input.cljs b/frontend/src/app/main/ui/components/numeric_input.cljs index dd41cc7b6..90dcf9628 100644 --- a/frontend/src/app/main/ui/components/numeric_input.cljs +++ b/frontend/src/app/main/ui/components/numeric_input.cljs @@ -245,4 +245,11 @@ (events/unlistenByKey key))))) + (mf/use-layout-effect + (mf/deps handle-mouse-wheel) + (fn [] + (let [keys [(events/listen (mf/ref-val ref) EventType.WHEEL handle-mouse-wheel #js {:pasive false})]] + #(doseq [key keys] + (events/unlistenByKey key))))) + [:> :input props])) diff --git a/frontend/src/app/main/ui/shapes/svg_defs.cljs b/frontend/src/app/main/ui/shapes/svg_defs.cljs index ac04f57f7..0b875bcfb 100644 --- a/frontend/src/app/main/ui/shapes/svg_defs.cljs +++ b/frontend/src/app/main/ui/shapes/svg_defs.cljs @@ -6,9 +6,9 @@ (ns app.main.ui.shapes.svg-defs (:require - [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.geom.matrix :as gmt] - [app.common.geom.point :as gpt] + [app.main.ui.shapes.filters :as f] [app.util.object :as obj] [app.util.svg :as usvg] [rumext.alpha :as mf])) @@ -21,24 +21,7 @@ (str transform-matrix " " val) (str transform-matrix))))) -(defn transform-region [attrs transform] - (let [{x-str :x y-str :y width-str :width height-str :height} attrs - data (map d/parse-double [x-str y-str width-str height-str])] - (if (every? (comp not nil?) data) - (let [[x y width height] data - p1 (-> (gpt/point x y) - (gpt/transform transform)) - p2 (-> (gpt/point (+ x width) (+ y height)) - (gpt/transform transform))] - - (assoc attrs - :x (:x p1) - :y (:y p1) - :width (- (:x p2) (:x p1)) - :height (- (:y p2) (:y p1)))) - attrs))) - -(mf/defc svg-node [{:keys [node prefix-id transform]}] +(mf/defc svg-node [{:keys [type node prefix-id transform bounds]}] (cond (string? node) node @@ -63,23 +46,34 @@ attrs (-> attrs (usvg/update-attr-ids prefix-id) (usvg/clean-attrs) - + ;; This clasname will be used to change the transform on the viewport + ;; only necessary for groups because shapes have their own transform + (cond-> (and (or transform-gradient? + transform-pattern? + transform-clippath? + transform-filter? + transform-mask?) + (= :group type)) + (update :className #(if % (dm/str % " svg-def") "svg-def"))) (cond-> transform-gradient? (add-matrix :gradientTransform transform) transform-pattern? (add-matrix :patternTransform transform) transform-clippath? (add-matrix :transform transform) (or transform-filter? - transform-mask?) (transform-region transform))) + transform-mask?) (merge attrs bounds))) [wrapper wrapper-props] (if (= tag :mask) - ["g" #js {:transform (str transform)}] + ["g" #js {:className "svg-mask-wrapper" + :transform (str transform)}] [mf/Fragment (obj/new)])] [:> (name tag) (clj->js attrs) [:> wrapper wrapper-props - (for [node content] [:& svg-node {:node node + (for [node content] [:& svg-node {:type type + :node node :prefix-id prefix-id - :transform transform}])]]))) + :transform transform + :bounds bounds}])]]))) (mf/defc svg-defs [{:keys [shape render-id]}] (let [svg-defs (:svg-defs shape) @@ -91,8 +85,8 @@ (usvg/svg-transform-matrix shape))) ;; Paths doesn't have transform so we have to transform its gradients - transform (if (contains? shape :svg-transform) - (gmt/multiply transform (or (:svg-transform shape) (gmt/matrix))) + transform (if (some? (:svg-transform shape)) + (gmt/multiply transform (:svg-transform shape)) transform) prefix-id @@ -103,7 +97,9 @@ ;; TODO: no key? (when (seq svg-defs) (for [svg-def (vals svg-defs)] - [:& svg-node {:node svg-def + [:& svg-node {:type (:type shape) + :node svg-def :prefix-id prefix-id - :transform transform}])))) + :transform transform + :bounds (f/get-filters-bounds shape)}])))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs index 6cac84489..4156c2c56 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs @@ -117,8 +117,8 @@ ;; Note that the "indeterminate" attribute only may be set by code, not as a static attribute. ;; See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#attr-indeterminate (if (= hide-fill-on-export? :multiple) - (dom/set-attribute checkbox "indeterminate" true) - (dom/remove-attribute checkbox "indeterminate"))))) + (dom/set-attribute! checkbox "indeterminate" true) + (dom/remove-attribute! checkbox "indeterminate"))))) [:div.element-set [:div.element-set-title diff --git a/frontend/src/app/main/ui/workspace/viewport/guides.cljs b/frontend/src/app/main/ui/workspace/viewport/guides.cljs index c12d31850..434458d31 100644 --- a/frontend/src/app/main/ui/workspace/viewport/guides.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/guides.cljs @@ -215,18 +215,19 @@ :text-y pos}))) (defn guide-inside-vbox? - ([vbox] - (partial guide-inside-vbox? vbox)) + ([zoom vbox] + (partial guide-inside-vbox? zoom vbox)) - ([{:keys [x y width height]} {:keys [axis position]}] - (let [x1 x + ([zoom {:keys [x y width height]} {:keys [axis position]}] + (let [rule-area-size (/ rules/rule-area-size zoom) + x1 x x2 (+ x width) y1 y y2 (+ y height)] (if (= axis :x) - (and (>= position x1) + (and (>= position (+ x1 rule-area-size)) (<= position x2)) - (and (>= position y1) + (and (>= position (+ y1 rule-area-size)) (<= position y2)))))) (defn guide-creation-area @@ -383,7 +384,7 @@ (let [guide (-> guide (assoc :id (uuid/next) :axis axis))] - (when (guide-inside-vbox? vbox guide) + (when (guide-inside-vbox? zoom vbox guide) (st/emit! (dw/update-guides guide)))))) {:keys [on-pointer-enter @@ -431,7 +432,7 @@ (mf/deps page vbox) #(->> (get-in page [:options :guides] {}) (vals) - (filter (guide-inside-vbox? vbox)))) + (filter (guide-inside-vbox? zoom vbox)))) focus (mf/deref refs/workspace-focus-selected) @@ -448,7 +449,7 @@ (mf/use-callback (mf/deps vbox) (fn [guide] - (if (guide-inside-vbox? vbox guide) + (if (guide-inside-vbox? zoom vbox guide) (st/emit! (dw/update-guides guide)) (st/emit! (dw/remove-guide guide)))))] diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 897d31d50..11e4bc006 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -201,25 +201,43 @@ (mf/use-memo (mf/deps modifiers) (fn [] - (d/mapm (fn [id {modifiers :modifiers}] - (let [center (gsh/center-shape (get objects id))] - (gsh/modifiers->transform center modifiers))) - modifiers))) + (when (some? modifiers) + (d/mapm (fn [id {modifiers :modifiers}] + (let [center (gsh/center-shape (get objects id))] + (gsh/modifiers->transform center modifiers))) + modifiers)))) shapes (mf/use-memo (mf/deps transforms) (fn [] (->> (keys transforms) - (mapv (d/getf objects)))))] + (mapv (d/getf objects))))) + + prev-shapes (mf/use-var nil) + prev-modifiers (mf/use-var nil) + prev-transforms (mf/use-var nil)] ;; Layout effect is important so the code is executed before the modifiers ;; are applied to the shape (mf/use-layout-effect (mf/deps transforms) (fn [] - (utils/update-transform shapes transforms modifiers) - #(utils/remove-transform shapes))))) + (when (and (nil? @prev-transforms) + (some? transforms)) + (utils/start-transform! shapes)) + + (when (some? modifiers) + (utils/update-transform! shapes transforms modifiers)) + + + (when (and (some? @prev-modifiers) + (not (some? modifiers))) + (utils/remove-transform! @prev-shapes)) + + (reset! prev-modifiers modifiers) + (reset! prev-transforms transforms) + (reset! prev-shapes shapes))))) (defn inside-vbox [vbox objects frame-id] (let [frame (get objects frame-id)] diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 416077562..f4151f11f 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -10,6 +10,7 @@ [app.common.data.macros :as dm] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] + [app.common.geom.shapes :as gsh] [app.main.ui.cursors :as cur] [app.util.dom :as dom])) @@ -100,7 +101,10 @@ (dom/query shape-node ".mask-shape")] group? - [] + (let [shape-defs (dom/query shape-node "defs")] + (d/concat-vec + (dom/query-all shape-defs ".svg-def") + (dom/query-all shape-defs ".svg-mask-wrapper"))) text? [shape-node @@ -112,7 +116,59 @@ :else [shape-node]))) -(defn update-transform [shapes transforms modifiers] +(defn transform-region! + [node modifiers] + + (let [{:keys [x y width height]} + (-> (gsh/make-selrect + (-> (dom/get-attribute node "data-old-x") d/parse-double) + (-> (dom/get-attribute node "data-old-y") d/parse-double) + (-> (dom/get-attribute node "data-old-width") d/parse-double) + (-> (dom/get-attribute node "data-old-height") d/parse-double)) + (gsh/transform-selrect modifiers))] + (dom/set-attribute! node "x" x) + (dom/set-attribute! node "y" y) + (dom/set-attribute! node "width" width) + (dom/set-attribute! node "height" height))) + +(defn start-transform! + [shapes] + (doseq [shape shapes] + (when-let [nodes (get-nodes shape)] + (doseq [node nodes] + (let [old-transform (dom/get-attribute node "transform")] + (when (some? old-transform) + (dom/set-attribute! node "data-old-transform" old-transform)) + + (when (or (= (dom/get-tag-name node) "linearGradient") + (= (dom/get-tag-name node) "radialGradient")) + (let [gradient-transform (dom/get-attribute node "gradientTransform")] + (when (some? gradient-transform) + (dom/set-attribute! node "data-old-gradientTransform" gradient-transform)))) + + (when (= (dom/get-tag-name node) "pattern") + (let [pattern-transform (dom/get-attribute node "patternTransform")] + (when (some? pattern-transform) + (dom/set-attribute! node "data-old-patternTransform" pattern-transform)))) + + (when (or (= (dom/get-tag-name node) "mask") + (= (dom/get-tag-name node) "filter")) + (dom/set-attribute! node "data-old-x" (dom/get-attribute node "x")) + (dom/set-attribute! node "data-old-y" (dom/get-attribute node "y")) + (dom/set-attribute! node "data-old-width" (dom/get-attribute node "width")) + (dom/set-attribute! node "data-old-height" (dom/get-attribute node "height")))))))) + +(defn set-transform-att! + [node att value] + + (let [old-att (dom/get-attribute node (dm/str "data-old-" att)) + new-value (if (some? old-att) + (dm/str value " " old-att) + (str value))] + (dom/set-attribute! node att (str new-value)))) + +(defn update-transform! + [shapes transforms modifiers] (doseq [{:keys [id type] :as shape} shapes] (when-let [nodes (get-nodes shape)] (let [transform (get transforms id) @@ -127,24 +183,38 @@ (doseq [node nodes] (cond - (or (dom/class? node "text-shape") (dom/class? node "text-svg")) + ;; Text shapes need special treatment because their resize only change + ;; the text area, not the change size/position + (or (dom/class? node "text-shape") + (dom/class? node "text-svg")) (when (some? text-transform) - (dom/set-attribute node "transform" (str text-transform))) + (set-transform-att! node "transform" text-transform)) (or (= (dom/get-tag-name node) "foreignObject") (dom/class? node "text-clip")) (let [cur-width (dom/get-attribute node "width") cur-height (dom/get-attribute node "height")] (when (and (some? text-width) (not= cur-width text-width)) - (dom/set-attribute node "width" text-width)) - + (dom/set-attribute! node "width" text-width)) (when (and (some? text-height) (not= cur-height text-height)) - (dom/set-attribute node "height" text-height))) + (dom/set-attribute! node "height" text-height))) + + (or (= (dom/get-tag-name node) "mask") + (= (dom/get-tag-name node) "filter")) + (transform-region! node modifiers) + + (or (= (dom/get-tag-name node) "linearGradient") + (= (dom/get-tag-name node) "radialGradient")) + (set-transform-att! node "gradientTransform" transform) + + (= (dom/get-tag-name node) "pattern") + (set-transform-att! node "patternTransform" transform) (and (some? transform) (some? node)) - (dom/set-attribute node "transform" (str transform)))))))) + (set-transform-att! node "transform" transform))))))) -(defn remove-transform [shapes] +(defn remove-transform! + [shapes] (doseq [shape shapes] (when-let [nodes (get-nodes shape)] (doseq [node nodes] @@ -155,7 +225,10 @@ nil :else - (dom/remove-attribute node "transform"))))))) + (let [old-transform (dom/get-attribute node "data-old-transform")] + (when-not (some? old-transform) + (dom/remove-attribute! node "data-old-transform") + (dom/remove-attribute! node "transform"))))))))) (defn format-viewbox [vbox] (dm/str (:x vbox 0) " " diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 499881ecf..0aba75592 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -415,11 +415,11 @@ "application/pdf" "pdf" nil)) -(defn set-attribute [^js node ^string attr value] +(defn set-attribute! [^js node ^string attr value] (when (some? node) (.setAttribute node attr value))) -(defn remove-attribute [^js node ^string attr] +(defn remove-attribute! [^js node ^string attr] (when (some? node) (.removeAttribute node attr))) diff --git a/frontend/src/app/util/svg.cljs b/frontend/src/app/util/svg.cljs index f7a7296a3..229557257 100644 --- a/frontend/src/app/util/svg.cljs +++ b/frontend/src/app/util/svg.cljs @@ -715,7 +715,8 @@ (gmt/matrix) ;; Paths doesn't have transform so we have to transform its gradients - (if (= :path (:type shape)) + (if (or (= :path (:type shape)) + (= :group (:type shape))) (gsh/transform-matrix shape) (gmt/matrix))