0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-08 21:11:22 -05:00

Performance improvements.

This commit is contained in:
Andrey Antukh 2020-01-16 16:22:56 +01:00
parent 3ab5e11d5f
commit 76e19a4b41
9 changed files with 134 additions and 349 deletions

View file

@ -434,10 +434,10 @@
(defn shapes->rect-shape
[shapes]
(let [shapes (mapv shape->rect-shape shapes)
minx (apply js/Math.min (mapv :x1 shapes))
miny (apply js/Math.min (mapv :y1 shapes))
maxx (apply js/Math.max (mapv :x2 shapes))
maxy (apply js/Math.max (mapv :y2 shapes))]
minx (transduce (map :x1) min shapes)
miny (transduce (map :y1) min shapes)
maxx (transduce (map :x2) max shapes)
maxy (transduce (map :y2) max shapes)]
{:x1 minx
:y1 miny
:x2 maxx
@ -509,10 +509,10 @@
(defn- transform-rect
[{:keys [x y width height] :as shape} mx]
(let [tl (gpt/transform [x y] mx)
tr (gpt/transform [(+ x width) y] mx)
bl (gpt/transform [x (+ y height)] mx)
br (gpt/transform [(+ x width) (+ y height)] mx)
(let [tl (gpt/transform (gpt/point x y) mx)
tr (gpt/transform (gpt/point (+ x width) y) mx)
bl (gpt/transform (gpt/point x (+ y height)) mx)
br (gpt/transform (gpt/point (+ x width) (+ y height)) mx)
;; TODO: replace apply with transduce (performance)
minx (apply min (map :x [tl tr bl br]))
maxx (apply max (map :x [tl tr bl br]))
@ -527,10 +527,10 @@
(defn- transform-circle
[{:keys [cx cy rx ry] :as shape} xfmt]
(let [{:keys [x1 y1 x2 y2]} (shape->rect-shape shape)
tl (gpt/transform [x1 y1] xfmt)
tr (gpt/transform [x2 y1] xfmt)
bl (gpt/transform [x1 y2] xfmt)
br (gpt/transform [x2 y2] xfmt)
tl (gpt/transform (gpt/point x1 y1) xfmt)
tr (gpt/transform (gpt/point x2 y1) xfmt)
bl (gpt/transform (gpt/point x1 y2) xfmt)
br (gpt/transform (gpt/point x2 y2) xfmt)
;; TODO: replace apply with transduce (performance)
x (apply min (map :x [tl tr bl br]))

View file

@ -78,7 +78,7 @@
[current]
(->> (rx/concat (rx/of current)
(rx/sample 10 mouse-position))
(rx/map #(gpt/divide % @refs/selected-zoom))
(rx/map #(gpt/divide % (gpt/point @refs/selected-zoom)))
(rx/mapcat (fn [point]
(if @refs/selected-alignment
(uwrk/align-point point)

View file

@ -135,7 +135,7 @@
mouse (->> ms/mouse-position
(rx/mapcat #(conditional-align % align?))
(rx/map #(gpt/divide % zoom)))]
(rx/map #(gpt/divide % (gpt/point zoom))))]
(rx/concat
(->> mouse
(rx/take 1)
@ -179,14 +179,14 @@
(let [{:keys [zoom flags]} (:workspace-local state)
align? (refs/alignment-activated? flags)
last-point (volatile! (gpt/divide @ms/mouse-position zoom))
last-point (volatile! (gpt/divide @ms/mouse-position (gpt/point zoom)))
stoper (->> (rx/filter stoper-event? stream)
(rx/share))
mouse (->> (rx/sample 10 ms/mouse-position)
(rx/mapcat #(conditional-align % align?))
(rx/map #(gpt/divide % zoom)))
(rx/map #(gpt/divide % (gpt/point zoom))))
points (->> stream
(rx/filter ms/mouse-click?)
@ -255,7 +255,7 @@
stoper (rx/filter stoper-event? stream)
mouse (->> (rx/sample 10 ms/mouse-position)
(rx/mapcat #(conditional-align % align?))
(rx/map #(gpt/divide % zoom)))]
(rx/map #(gpt/divide % (gpt/point zoom))))]
(rx/concat
(rx/of initialize-drawing)
(->> mouse

View file

@ -25,7 +25,7 @@
(defn- apply-zoom
[point]
(gpt/divide point @refs/selected-zoom))
(gpt/divide point (gpt/point @refs/selected-zoom)))
;; --- Resize & Rotate
@ -115,8 +115,8 @@
:style {:fillOpacity "1"
:strokeWidth "2px"
:vectorEffect "non-scaling-stroke"}
:fill "rgba(49,239,184,.7)"
:stroke "#31EFB8"
:fill "rgba(49,239,184,.7)"
:stroke "#31EFB8"
:cx cx
:cy cy}])
@ -232,7 +232,6 @@
;; TODO: add specs for clarity
(mf/defc text-edition-selection-handlers
[{:keys [shape zoom] :as props}]
(let [{:keys [x y width height] :as shape} shape]
@ -257,7 +256,6 @@
(mf/defc single-selection-handlers
[{:keys [shape zoom] :as props}]
(prn "single-selection-handlers" shape)
(let [on-resize #(do (dom/stop-propagation %2)
(st/emit! (start-resize %1 #{(:id shape)} shape)))
on-rotate #(do (dom/stop-propagation %)

View file

@ -38,7 +38,7 @@
(mf/defc coordinates
[{:keys [zoom] :as props}]
(let [coords (some-> (use-rxsub ms/mouse-position)
(gpt/divide zoom)
(gpt/divide (gpt/point zoom zoom))
(gpt/round 0))]
[:ul.coordinates
[:span {:alt "x"}

View file

@ -2,8 +2,10 @@
;; 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) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.matrix
(:require [cuerdas.core :as str]
@ -13,50 +15,21 @@
;; --- Matrix Impl
(defrecord Matrix [a b c d e f])
(defprotocol ICoerce
"Matrix coersion protocol."
(-matrix [v] "Return a matrix instance."))
(extend-type Matrix
cljs.core/IDeref
(-deref [v]
(mapv #(get v %) [:a :b :c :d :e :f]))
(defrecord Matrix [a b c d e f]
Object
(toString [v]
(->> (str/join "," @v)
(str/format "matrix(%s)"))))
(extend-protocol ICoerce
nil
(-matrix [_]
(Matrix. 1 0 0 1 0 0))
Matrix
(-matrix [v] v)
cljs.core/PersistentVector
(-matrix [v]
(let [[a b c d e f] v]
(Matrix. a b c d e f)))
cljs.core/IndexedSeq
(-matrix [v]
(let [[a b c d e f] v]
(Matrix. a b c d e f))))
(toString [_]
(str "matrix(" a "," b "," c "," d "," e "," f ")")))
(defn multiply
([m1 m2]
([{m1a :a m1b :b m1c :c m1d :d m1e :e m1f :f :as m1}
{m2a :a m2b :b m2c :c m2d :d m2e :e m2f :f :as m2}]
(Matrix.
(+ (* (:a m1) (:a m2)) (* (:c m1) (:b m2)))
(+ (* (:b m1) (:a m2)) (* (:d m1) (:b m2)))
(+ (* (:a m1) (:c m2)) (* (:c m1) (:d m2)))
(+ (* (:b m1) (:c m2)) (* (:d m1) (:d m2)))
(+ (* (:a m1) (:e m2)) (* (:c m1) (:f m2)) (:e m1))
(+ (* (:b m1) (:e m2)) (* (:d m1) (:f m2)) (:f m1))))
(+ (* m1a m2a) (* m1c m2b))
(+ (* m1b m2a) (* m1d m2b))
(+ (* m1a m2c) (* m1c m2d))
(+ (* m1b m2c) (* m1d m2d))
(+ (* m1a m2e) (* m1c m2f) m1e)
(+ (* m1b m2e) (* m1d m2f) m1f)))
([m1 m2 & others]
(reduce multiply (multiply m1 m2) others)))
@ -69,100 +42,28 @@
"Create a new matrix instance."
([]
(Matrix. 1 0 0 1 0 0))
([v]
(-matrix v))
([a b c d e f]
(Matrix. a b c d e f)))
(defn translate-matrix
[pt]
(let [pt (gpt/point pt)]
(Matrix. 1 0 0 1 (:x pt) (:y pt))))
[{x :x y :y :as pt}]
(assert (gpt/point? pt))
(Matrix. 1 0 0 1 x y))
(defn scale-matrix
[s]
(let [pt (gpt/point s)]
(Matrix. (:x pt) 0 0 (:y pt) 0 0)))
[{x :x y :y :as pt}]
(assert (gpt/point? pt))
(Matrix. x 0 0 y 0 0))
(defn rotate-matrix
[a]
(let [a (mth/radians a)]
(Matrix.
(mth/cos a)
(mth/sin a)
(- (mth/sin a))
(mth/cos a)
0
0)))
;; OLD
;; (defn rotate
;; "Apply rotation transformation to the matrix."
;; ([m angle]
;; (multiply m (rotate-matrix angle)))
;; ([m angle center]
;; (multiply m
;; (translate-matrix center)
;; (rotate-matrix angle)
;; (translate-matrix (gpt/negate center)))))
;; -- ROTATE
;; r = radians(r)
;; const cos = Math.cos(r)
;; const sin = Math.sin(r)
;;
;; const { a, b, c, d, e, f } = this
;;
;; this.a = a * cos - b * sin
;; this.b = b * cos + a * sin
;; this.c = c * cos - d * sin
;; this.d = d * cos + c * sin
;; this.e = e * cos - f * sin + cy * sin - cx * cos + cx
;; this.f = f * cos + e * sin - cx * sin - cy * cos + cy
;; (defn rotate
;; ([m angle] (rotate m angle (gpt/point 0 0)))
;; ([m angle center]
;; (let [{:keys [a b c d e f]} m
;; {cx :x cy :y} center
;; r (mth/radians angle)
;; cos (mth/cos r)
;; sin (mth/sin r)
;; a' (- (* a cos) (* b sin))
;; b' (+ (* b cos) (* a sin))
;; c' (- (* c cos) (* d sin))
;; d' (+ (* d cos) (* c sin))
;; e' (+ (- (* e cos) (* f sin))
;; (- (* cy sin) (* cx cos))
;; cx)
;; f' (+ (- (+ (* f cos) (* e sin))
;; (* cx sin)
;; (* cy cos))
;; cy)]
;; (Matrix. a' b' c' d' e' f'))))
;; export function rotate (angle, cx, cy) {
;; const cosAngle = cos(angle)
;; const sinAngle = sin(angle)
;; const rotationMatrix = {
;; a: cosAngle,
;; c: -sinAngle,
;; e: 0,
;; b: sinAngle,
;; d: cosAngle,
;; f: 0
;; }
;; if (isUndefined(cx) || isUndefined(cy)) {
;; return rotationMatrix
;; }
;; return transform([
;; translate(cx, cy),
;; rotationMatrix,
;; translate(-cx, -cy)
;; ])
;; }
(Matrix. (mth/cos a)
(mth/sin a)
(- (mth/sin a))
(mth/cos a)
0
0)))
(defn rotate
"Apply rotation transformation to the matrix."
@ -177,85 +78,19 @@
;; TODO: temporal backward compatibility
(def rotate* rotate)
;; ([m v] (scale m v v))
;; ([m x y]
;; (assoc m
;; :a (* (:a m) x)
;; :c (* (:c m) x)
;; :b (* (:b m) y)
;; :d (* (:d m) y))))
;; scaleO (x, y = x, cx = 0, cy = 0) {
;; // Support uniform scaling
;; if (arguments.length === 3) {
;; cy = cx
;; cx = y
;; y = x
;; }
;; const { a, b, c, d, e, f } = this
;; this.a = a * x
;; this.b = b * y
;; this.c = c * x
;; this.d = d * y
;; this.e = e * x - cx * x + cx
;; this.f = f * y - cy * y + cy
;; return this
;; }
;; (defn scale
;; "Apply scale transformation to the matrix."
;; ([m x] (scale m x x))
;; ([m x y]
;; (let [{:keys [a b c d e f]} m
;; cx 0
;; cy 0
;; a' (* a x)
;; b' (* b y)
;; c' (* c x)
;; d' (* d y)
;; e' (+ cx (- (* e x)
;; (* cx x)))
;; f' (+ cy (- (* f y)
;; (* cy y)))]
;; (Matrix. a' b' c' d' e f))))
(defn scale
"Apply scale transformation to the matrix."
([m s] (multiply m (scale-matrix s)))
([m s c]
([m scale] (multiply m (scale-matrix scale)))
([m scale center]
(multiply m
(translate-matrix c)
(scale-matrix s)
(translate-matrix (gpt/negate c)))))
(translate-matrix center)
(scale-matrix scale)
(translate-matrix (gpt/negate center)))))
(defn translate
"Apply translate transformation to the matrix."
[m pt]
(let [pt (gpt/point pt)]
(multiply m (translate-matrix pt))))
(defn ^boolean invertible?
[{:keys [a b c d e f] :as m}]
(let [det (- (* a d) (* c b))]
(and (not (mth/nan? det))
(mth/finite? e)
(mth/finite? f))))
(defn invert
[{:keys [a b c d e f] :as m}]
(when (invertible? m)
(let [det (- (* a d) (* c b))]
(Matrix. (/ d det)
(/ (- b) det)
(/ (- c) det)
(/ a det)
(/ (- (* c f) (* d e)) det)
(/ (- (* b e) (* a f)) det)))))
(multiply m (translate-matrix pt)))
;; --- Transit Adapter

View file

@ -2,6 +2,9 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.path

View file

@ -2,8 +2,10 @@
;; 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) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.point
(:refer-clojure :exclude [divide])
@ -27,13 +29,6 @@
(point? v)
v
(or (vector? v)
(seq? v))
(Point. (first v) (second v))
(map? v)
(Point. (:x v) (:y v))
(number? v)
(Point. v v)
@ -41,105 +36,87 @@
(throw (ex-info "Invalid arguments" {:v v}))))
([x y] (Point. x y)))
(defn rotate
"Apply rotation transformation to the point."
[p angle]
{:pre [(point? p)]}
(let [angle (mth/radians angle)
sin (mth/sin angle)
cos (mth/cos angle)]
(Point.
(-> (- (* (:x p) cos) (* (:y p) sin))
(mth/precision 6))
(-> (+ (* (:x p) sin) (* (:y p) cos))
(mth/precision 6)))))
(defn add
"Returns the addition of the supplied value to both
coordinates of the point as a new point."
[p other]
{:pre [(point? p)]}
(let [other (point other)]
(Point. (+ (:x p) (:x other))
(+ (:y p) (:y other)))))
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (+ x ox) (+ y oy)))
(defn subtract
"Returns the subtraction of the supplied value to both
coordinates of the point as a new point."
[p other]
{:pre [(point? p)]}
(let [other (point other)]
(Point. (- (:x p) (:x other))
(- (:y p) (:y other)))))
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (- x ox) (- y oy)))
(defn multiply
"Returns the subtraction of the supplied value to both
coordinates of the point as a new point."
[p other]
{:pre [(point? p)]}
(let [other (point other)]
(Point. (* (:x p) (:x other))
(* (:y p) (:y other)))))
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (* x ox) (* y oy)))
(defn divide
[p other]
{:pre [(point? p)]}
(let [other (point other)]
(Point. (/ (:x p) (:x other))
(/ (:y p) (:y other)))))
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (/ x ox) (/ y oy)))
(defn negate
[p]
{:pre [(point? p)]}
(let [{:keys [x y]} (point p)]
(Point. (- x) (- y))))
[{x :x y :y :as p}]
(assert (point? p))
(Point. (- x) (- y)))
(defn distance
"Calculate the distance between two points."
[p other]
(let [other (point other)
dx (- (:x p) (:x other))
dy (- (:y p) (:y other))]
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(let [dx (- x ox)
dy (- y oy)]
(-> (mth/sqrt (+ (mth/pow dx 2)
(mth/pow dy 2)))
(mth/precision 6))))
(defn length
[p]
{:pre [(point? p)]}
(mth/sqrt (+ (mth/pow (:x p) 2)
(mth/pow (:y p) 2))))
[{x :x y :y :as p}]
(assert (point? p))
(mth/sqrt (+ (mth/pow x 2)
(mth/pow y 2))))
(defn angle
"Returns the smaller angle between two vectors.
If the second vector is not provided, the angle
will be measured from x-axis."
([p]
(-> (mth/atan2 (:y p) (:x p))
([{x :x y :y :as p}]
(-> (mth/atan2 y x)
(mth/degrees)))
([p center]
(let [center (point center)]
(angle (subtract p center)))))
(angle (subtract p center))))
(defn angle-with-other
"Consider point as vector and calculate
the angle between two vectors."
[p other]
{:pre [(point? p)]}
(let [other (point other)
a (/ (+ (* (:x p) (:x other))
(* (:y p) (:y other)))
(* (length p) (length other)))
a (mth/acos (if (< a -1)
-1
(if (> a 1) 1 a)))]
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(let [a (/ (+ (* x ox)
(* y oy))
(* (length p)
(length other)))
a (mth/acos (if (< a -1) -1 (if (> a 1) 1 a)))]
(-> (mth/degrees a)
(mth/precision 6))))
(defn update-angle
"Update the angle of the point."
[p angle]
(assert (point? p))
(assert (number? angle))
(let [len (length p)
angle (mth/radians angle)]
(Point. (* (mth/cos angle) len)
@ -148,24 +125,25 @@
(defn quadrant
"Return the quadrant of the angle of the point."
[{:keys [x y] :as p}]
{:pre [(point? p)]}
(assert (point? p))
(if (>= x 0)
(if (>= y 0) 1 4)
(if (>= y 0) 2 3)))
(defn round
"Change the precision of the point coordinates."
[{:keys [x y]} decimanls]
[{:keys [x y] :as p} decimanls]
(assert (point? p))
(assert (number? decimanls))
(Point. (mth/precision x decimanls)
(mth/precision y decimanls)))
(defn transform
"Transform a point applying a matrix transfomation."
[pt {:keys [a b c d e f] :as m}]
(let [{:keys [x y]} (point pt)]
(Point. (+ (* x a) (* y c) e)
(+ (* x b) (* y d) f))))
[{:keys [x y] :as p} {:keys [a b c d e f] :as m}]
(assert (point? p))
(Point. (+ (* x a) (* y c) e)
(+ (* x b) (* y d) f)))
;; --- Transit Adapter

View file

@ -1,14 +1,15 @@
(ns uxbox.tests.test-util-geom
(:require [cljs.test :as t :include-macros true]
[cljs.pprint :refer [pprint]]
;; [uxbox.util.geom.point-impl :as gpt2]
;; [uxbox.util.geom.matrix-impl :as gmt2]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt]))
(t/deftest point-constructors-test
(let [p (gpt/point 1 2)]
(t/is (= (:x p) 1))
(t/is (= (:y p) 2))
(t/is (gpt/point? p)))
(t/is (= (:y p) 2)))
(let [p (gpt/point 1)]
(t/is (= (:x p) 1))
@ -16,34 +17,27 @@
(let [p (gpt/point)]
(t/is (= (:x p) 0))
(t/is (= (:y p) 0)))
(t/is (= (:y p) 0))))
(let [p (gpt/point [1 2])]
(t/is (= (:x p) 1))
(t/is (= (:y p) 2))))
(t/deftest point-rotate-test
(let [p1 (gpt/point 10 0)
p2 (gpt/rotate p1 90)]
(t/is (= (:x p2) 0))
(t/is (= (:y p2) 10))
(t/is (gpt/point? p2))))
;; (t/deftest point-rotate-test
;; (let [p1 (gpt/point 10 0)
;; p2 (gpt/rotate p1 90)]
;; (t/is (= (:x p2) 0))
;; (t/is (= (:y p2) 10))))
(t/deftest point-add-test
(let [p1 (gpt/point 1 1)
p2 (gpt/point 2 2)
p3 (gpt/add p1 p2)]
(t/is (= (:x p3) 3))
(t/is (= (:y p3) 3))
(t/is (gpt/point? p3))))
(t/is (= (:y p3) 3))))
(t/deftest point-subtract-test
(let [p1 (gpt/point 3 3)
p2 (gpt/point 2 2)
p3 (gpt/subtract p1 p2)]
(t/is (= (:x p3) 1))
(t/is (= (:y p3) 1))
(t/is (gpt/point? p3))))
(t/is (= (:y p3) 1))))
(t/deftest point-distance-test
(let [p1 (gpt/point 0 0)
@ -69,46 +63,23 @@
(t/is (number? angle))
(t/is (= angle 45))))
(t/deftest point-quadrant-test
(let [p1 (gpt/point 10 10)
p2 (gpt/point -10 10)
p3 (gpt/point -10 -10)
p4 (gpt/point 10 -10)]
(t/is (= 1 (gpt/quadrant p1)))
(t/is (= 2 (gpt/quadrant p2)))
(t/is (= 3 (gpt/quadrant p3)))
(t/is (= 4 (gpt/quadrant p4)))))
(t/deftest matrix-constructors-test
(let [m (gmt/matrix)]
(t/is (= @m [1 0 0 1 0 0]))
(t/is (gmt/matrix? m)))
(t/is (= (str m) "matrix(1,0,0,1,0,0)")))
(let [m (gmt/matrix 1 1 1 2 2 2)]
(t/is (= @m [1 1 1 2 2 2]))
(t/is (gmt/matrix? m)))
(let [m (gmt/matrix [1 1 1 2 2 2])]
(t/is (= @m [1 1 1 2 2 2]))
(t/is (gmt/matrix? m))))
(t/is (= (str m) "matrix(1,1,1,2,2,2)"))))
(t/deftest matrix-translate-test
(let [m (-> (gmt/matrix)
(gmt/translate (gpt/point 2 10)))]
(t/is (str m) "matrix(1,0,0,1,2,10)")))
(t/deftest matrix-scale-test
(let [m (-> (gmt/matrix)
(gmt/scale (gpt/point 2)))]
(t/is (str m) "matrix(2,0,0,2,0,0)")))
(t/deftest matrix-rotate-test
(let [m (-> (gmt/matrix)
(gmt/rotate 10))]
(t/is (= @m [0.984807753012208
0.17364817766693033
-0.17364817766693033
0.984807753012208
0 0]))))
(t/deftest matrix-scale-test
(let [m (-> (gmt/matrix)
(gmt/scale 2))]
(t/is (= @m [2 0 0 2 0 0]))))
(t/deftest matrix-translate-test
(let [m (-> (gmt/matrix)
(gmt/translate 2 10))]
(t/is (= @m [1 0 0 1 2 10]))))
(t/is (str m) "matrix(0.984807753012208,0.17364817766693033,-0.17364817766693033,0.984807753012208,0,0)")))