From ea5b153578174aa5d90240652fe9c81f5507dbca Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 20 Jun 2023 14:03:35 +0200 Subject: [PATCH] :sparkles: Use new defrecord for geom data structures --- common/src/app/common/geom/matrix.cljc | 194 ++++++++++++++----------- common/src/app/common/geom/point.cljc | 157 ++++++++++++-------- common/src/app/common/geom/rect.cljc | 57 ++++++-- 3 files changed, 248 insertions(+), 160 deletions(-) diff --git a/common/src/app/common/geom/matrix.cljc b/common/src/app/common/geom/matrix.cljc index d755580c3b..7af88b81bb 100644 --- a/common/src/app/common/geom/matrix.cljc +++ b/common/src/app/common/geom/matrix.cljc @@ -13,6 +13,7 @@ [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.math :as mth] + [app.common.record :as cr] [app.common.schema :as sm] [app.common.schema.generators :as sg] [app.common.schema.openapi :as-alias oapi] @@ -27,21 +28,21 @@ (def precision 6) ;; --- Matrix Impl -(defrecord Matrix [^double a - ^double b - ^double c - ^double d - ^double e - ^double f] +(cr/defrecord Matrix [^double a + ^double b + ^double c + ^double d + ^double e + ^double f] Object - (toString [_] + (toString [this] (dm/fmt "matrix(%, %, %, %, %, %)" - (mth/to-fixed a precision) - (mth/to-fixed b precision) - (mth/to-fixed c precision) - (mth/to-fixed d precision) - (mth/to-fixed e precision) - (mth/to-fixed f precision)))) + (mth/to-fixed (.-a this) precision) + (mth/to-fixed (.-b this) precision) + (mth/to-fixed (.-c this) precision) + (mth/to-fixed (.-d this) precision) + (mth/to-fixed (.-e this) precision) + (mth/to-fixed (.-f this) precision)))) (defn matrix? "Return true if `v` is Matrix instance." @@ -51,9 +52,9 @@ (defn matrix "Create a new matrix instance." ([] - (Matrix. 1 0 0 1 0 0)) + (pos->Matrix 1 0 0 1 0 0)) ([a b c d e f] - (Matrix. a b c d e f))) + (pos->Matrix a b c d e f))) (def number-regex #"[+-]?\d*(\.\d+)?(e[+-]?\d+)?") @@ -100,7 +101,7 @@ (sg/small-double) (sg/small-double) (sg/small-double) ) - (sg/fmap #(apply ->Matrix %))) + (sg/fmap #(apply pos->Matrix %))) ::oapi/type "string" ::oapi/format "matrix" ::oapi/decode decode @@ -120,24 +121,54 @@ (s/def ::matrix (s/and ::matrix-attrs matrix?)) - (defn close? [^Matrix m1 ^Matrix m2] - (and (mth/close? (.-a m1) (.-a m2)) - (mth/close? (.-b m1) (.-b m2)) - (mth/close? (.-c m1) (.-c m2)) - (mth/close? (.-d m1) (.-d m2)) - (mth/close? (.-e m1) (.-e m2)) - (mth/close? (.-f m1) (.-f m2)))) + (and ^boolean (mth/close? (.-a m1) (.-a m2)) + ^boolean (mth/close? (.-b m1) (.-b m2)) + ^boolean (mth/close? (.-c m1) (.-c m2)) + ^boolean (mth/close? (.-d m1) (.-d m2)) + ^boolean (mth/close? (.-e m1) (.-e m2)) + ^boolean (mth/close? (.-f m1) (.-f m2)))) (defn unit? [^Matrix m1] - (and (some? m1) - (mth/close? (.-a m1) 1) - (mth/close? (.-b m1) 0) - (mth/close? (.-c m1) 0) - (mth/close? (.-d m1) 1) - (mth/close? (.-e m1) 0) - (mth/close? (.-f m1) 0))) + (and ^boolean (some? m1) + ^boolean (mth/close? (.-a m1) 1) + ^boolean (mth/close? (.-b m1) 0) + ^boolean (mth/close? (.-c m1) 0) + ^boolean (mth/close? (.-d m1) 1) + ^boolean (mth/close? (.-e m1) 0) + ^boolean (mth/close? (.-f m1) 0))) + +(defn multiply! + [^Matrix m1 ^Matrix m2] + (let [m1a (.-a m1) + m1b (.-b m1) + m1c (.-c m1) + m1d (.-d m1) + m1e (.-e m1) + m1f (.-f m1) + m2a (.-a m2) + m2b (.-b m2) + m2c (.-c m2) + m2d (.-d m2) + m2e (.-e m2) + m2f (.-f m2)] + #?@(:cljs + [(set! (.-a m1) (+ (* m1a m2a) (* m1c m2b))) + (set! (.-b m1) (+ (* m1b m2a) (* m1d m2b))) + (set! (.-c m1) (+ (* m1a m2c) (* m1c m2d))) + (set! (.-d m1) (+ (* m1b m2c) (* m1d m2d))) + (set! (.-e m1) (+ (* m1a m2e) (* m1c m2f) m1e)) + (set! (.-f m1) (+ (* m1b m2e) (* m1d m2f) m1f)) + m1] + :clj + [(pos->Matrix + (+ (* 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))]))) (defn multiply ([^Matrix m1 ^Matrix m2] @@ -162,7 +193,7 @@ m2e (.-e m2) m2f (.-f m2)] - (Matrix. + (pos->Matrix (+ (* m1a m2a) (* m1c m2b)) (+ (* m1b m2a) (* m1d m2b)) (+ (* m1a m2c) (* m1c m2d)) @@ -171,51 +202,28 @@ (+ (* m1b m2e) (* m1d m2f) m1f))))) ([m1 m2 & others] - (reduce multiply (multiply m1 m2) others))) - -(defn multiply! - [^Matrix m1 ^Matrix m2] - (let [m1a (.-a m1) - m1b (.-b m1) - m1c (.-c m1) - m1d (.-d m1) - m1e (.-e m1) - m1f (.-f m1) - m2a (.-a m2) - m2b (.-b m2) - m2c (.-c m2) - m2d (.-d m2) - m2e (.-e m2) - m2f (.-f m2)] - #?@(:cljs [(set! (.-a m1) (+ (* m1a m2a) (* m1c m2b))) - (set! (.-b m1) (+ (* m1b m2a) (* m1d m2b))) - (set! (.-c m1) (+ (* m1a m2c) (* m1c m2d))) - (set! (.-d m1) (+ (* m1b m2c) (* m1d m2d))) - (set! (.-e m1) (+ (* m1a m2e) (* m1c m2f) m1e)) - (set! (.-f m1) (+ (* m1b m2e) (* m1d m2f) m1f)) - m1] - :clj [(Matrix. - (+ (* 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))]))) + (reduce multiply! (multiply m1 m2) others))) (defn add-translate "Given two TRANSLATE matrixes (only e and f have significative values), combine them. Quicker than multiplying them, for this precise case." - ([{m1e :e m1f :f} {m2e :e m2f :f}] - (Matrix. 1 0 0 1 (+ m1e m2e) (+ m1f m2f))) + ([^Matrix m1 ^Matrix m2] + (let [m1e (dm/get-prop m1 :e) + m1f (dm/get-prop m1 :f) + m2e (dm/get-prop m2 :e) + m2f (dm/get-prop m2 :f)] + (pos->Matrix 1 0 0 1 (+ m1e m2e) (+ m1f m2f)))) ([m1 m2 & others] (reduce add-translate (add-translate m1 m2) others))) +;; FIXME: optimize? + (defn substract [{m1a :a m1b :b m1c :c m1d :d m1e :e m1f :f} {m2a :a m2b :b m2c :c m2d :d m2e :e m2f :f}] - (Matrix. + (pos->Matrix (- m1a m2a) (- m1b m2b) (- m1c m2c) (- m1d m2d) (- m1e m2e) (- m1f m2f))) @@ -227,13 +235,24 @@ (defn translate-matrix ([pt] - (assert (gpt/point? pt)) - (Matrix. 1 0 0 1 - (dm/get-prop pt :x) - (dm/get-prop pt :y))) + (dm/assert! (gpt/point? pt)) + (pos->Matrix 1 0 0 1 + (dm/get-prop pt :x) + (dm/get-prop pt :y))) ([x y] - (Matrix. 1 0 0 1 x y))) + (pos->Matrix 1 0 0 1 x y))) + + +(defn translate-matrix-neg + ([pt] + (dm/assert! (gpt/point? pt)) + (pos->Matrix 1 0 0 1 + (- (dm/get-prop pt :x)) + (- (dm/get-prop pt :y)))) + + ([x y] + (pos->Matrix 1 0 0 1 (- x) (- y)))) (defn scale-matrix ([pt center] @@ -241,10 +260,10 @@ sy (dm/get-prop pt :y) cx (dm/get-prop center :x) cy (dm/get-prop center :y)] - (Matrix. sx 0 0 sy (- cx (* cx sx)) (- cy (* cy sy))))) + (pos->Matrix sx 0 0 sy (- cx (* cx sx)) (- cy (* cy sy))))) ([pt] - (assert (gpt/point? pt)) - (Matrix. (dm/get-prop pt :x) 0 0 (dm/get-prop pt :y) 0 0))) + (dm/assert! (gpt/point? pt)) + (pos->Matrix (dm/get-prop pt :x) 0 0 (dm/get-prop pt :y) 0 0))) (defn rotate-matrix ([angle point] @@ -258,15 +277,15 @@ ns (- s) tx (+ (* c nx) (* ns ny) cx) ty (+ (* s nx) (* c ny) cy)] - (Matrix. c s ns c tx ty))) + (pos->Matrix c s ns c tx ty))) ([angle] (let [a (mth/radians angle)] - (Matrix. (mth/cos a) - (mth/sin a) - (- (mth/sin a)) - (mth/cos a) - 0 - 0)))) + (pos->Matrix (mth/cos a) + (mth/sin a) + (- (mth/sin a)) + (mth/cos a) + 0 + 0)))) (defn skew-matrix ([angle-x angle-y point] @@ -276,7 +295,7 @@ ([angle-x angle-y] (let [m1 (mth/tan (mth/radians angle-x)) m2 (mth/tan (mth/radians angle-y))] - (Matrix. 1 m2 m1 1 0 0)))) + (pos->Matrix 1 m2 m1 1 0 0)))) (defn rotate "Apply rotation transformation to the matrix." @@ -337,6 +356,7 @@ (translate (gpt/negate pt))) mtx)) +;; FIXME: performance (defn determinant "Determinant for the affinity transform" [{:keys [a b c d _ _]}] @@ -346,14 +366,14 @@ "Gets the inverse of the affinity transform `mtx`" [{:keys [a b c d e f] :as mtx}] (let [det (determinant mtx)] - (when-not (mth/almost-zero? det) + (when-not ^boolean (mth/almost-zero? det) (let [a' (/ d det) b' (/ (- b) det) c' (/ (- c) det) d' (/ a det) e' (/ (- (* c f) (* d e)) det) f' (/ (- (* b e) (* a f)) det)] - (Matrix. a' b' c' d' e' f'))))) + (pos->Matrix a' b' c' d' e' f'))))) (defn round [mtx] @@ -377,11 +397,11 @@ point)) (defn move? - [{:keys [a b c d _ _]}] - (and (mth/almost-zero? (- a 1)) - (mth/almost-zero? b) - (mth/almost-zero? c) - (mth/almost-zero? (- d 1)))) + [m] + (and ^boolean (mth/almost-zero? (- (dm/get-prop m :a) 1)) + ^boolean (mth/almost-zero? (dm/get-prop m :b)) + ^boolean (mth/almost-zero? (dm/get-prop m :c)) + ^boolean (mth/almost-zero? (- (dm/get-prop m :d) 1)))) #?(:clj (fres/add-handlers! diff --git a/common/src/app/common/geom/point.cljc b/common/src/app/common/geom/point.cljc index 71f4a80146..20680e8166 100644 --- a/common/src/app/common/geom/point.cljc +++ b/common/src/app/common/geom/point.cljc @@ -16,6 +16,7 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.math :as mth] + [app.common.record :as cr] [app.common.schema :as sm] [app.common.schema.generators :as sg] [app.common.schema.openapi :as-alias oapi] @@ -29,7 +30,7 @@ ;; --- Point Impl -(defrecord Point [x y]) +(cr/defrecord Point [x y]) (defn s [pt] @@ -62,7 +63,7 @@ (map->Point p) (if (string? p) (let [[x y] (->> (str/split p #",") (mapv parse-double))] - (Point. x y)) + (pos->Point x y)) p))) (encode [p] @@ -76,7 +77,7 @@ :description "Point" :error/message "expected a valid point" :gen/gen (->> (sg/tuple (sg/small-int) (sg/small-int)) - (sg/fmap #(apply ->Point %))) + (sg/fmap #(apply pos->Point %))) ::oapi/type "string" ::oapi/format "point" ::oapi/decode decode @@ -90,7 +91,7 @@ (defn point "Create a Point instance." - ([] (Point. 0 0)) + ([] (pos->Point 0 0)) ([v] (cond (point? v) @@ -100,12 +101,12 @@ (point v v) (point-like? v) - (Point. (:x v) (:y v)) + (pos->Point (:x v) (:y v)) :else (ex/raise :hint "invalid arguments (on pointer constructor)" :value v))) ([x y] - (Point. x y))) + (pos->Point x y))) (defn close? [p1 p2] @@ -129,22 +130,24 @@ (and (point? p1) (point? p2))) - (Point. (+ (dm/get-prop p1 :x) - (dm/get-prop p2 :x)) - (+ (dm/get-prop p1 :y) - (dm/get-prop p2 :y)))) + (pos->Point (+ (dm/get-prop p1 :x) + (dm/get-prop p2 :x)) + (+ (dm/get-prop p1 :y) + (dm/get-prop p2 :y)))) (defn subtract "Returns the subtraction of the supplied value to both coordinates of the point as a new point." [p1 p2] - (assert (and (point? p1) - (point? p2)) - "arguments should be pointer instance") - (Point. (- (dm/get-prop p1 :x) - (dm/get-prop p2 :x)) - (- (dm/get-prop p1 :y) - (dm/get-prop p2 :y)))) + (dm/assert! + "arguments should be pointer instance" + (and (point? p1) + (point? p2))) + + (pos->Point (- (dm/get-prop p1 :x) + (dm/get-prop p2 :x)) + (- (dm/get-prop p1 :y) + (dm/get-prop p2 :y)))) (defn multiply "Returns the subtraction of the supplied value to both @@ -153,20 +156,20 @@ (assert (and (point? p1) (point? p2)) "arguments should be pointer instance") - (Point. (* (dm/get-prop p1 :x) - (dm/get-prop p2 :x)) - (* (dm/get-prop p1 :y) - (dm/get-prop p2 :y)))) + (pos->Point (* (dm/get-prop p1 :x) + (dm/get-prop p2 :x)) + (* (dm/get-prop p1 :y) + (dm/get-prop p2 :y)))) (defn divide [p1 p2] (assert (and (point? p1) (point? p2)) "arguments should be pointer instance") - (Point. (/ (dm/get-prop p1 :x) - (dm/get-prop p2 :x)) - (/ (dm/get-prop p1 :y) - (dm/get-prop p2 :y)))) + (pos->Point (/ (dm/get-prop p1 :x) + (dm/get-prop p2 :x)) + (/ (dm/get-prop p1 :y) + (dm/get-prop p2 :y)))) (defn min ([] nil) @@ -175,10 +178,10 @@ (cond (nil? p1) p2 (nil? p2) p1 - :else (Point. (c/min (dm/get-prop p1 :x) - (dm/get-prop p2 :x)) - (c/min (dm/get-prop p1 :y) - (dm/get-prop p2 :y)))))) + :else (pos->Point (c/min (dm/get-prop p1 :x) + (dm/get-prop p2 :x)) + (c/min (dm/get-prop p1 :y) + (dm/get-prop p2 :y)))))) (defn max ([] nil) ([p1] p1) @@ -186,21 +189,21 @@ (cond (nil? p1) p2 (nil? p2) p1 - :else (Point. (c/max (dm/get-prop p1 :x) - (dm/get-prop p2 :x)) - (c/max (dm/get-prop p1 :y) - (dm/get-prop p2 :y)))))) + :else (pos->Point (c/max (dm/get-prop p1 :x) + (dm/get-prop p2 :x)) + (c/max (dm/get-prop p1 :y) + (dm/get-prop p2 :y)))))) (defn inverse [pt] (assert (point? pt) "point instance expected") - (Point. (/ 1.0 (dm/get-prop pt :x)) - (/ 1.0 (dm/get-prop pt :y)))) + (pos->Point (/ 1.0 (dm/get-prop pt :x)) + (/ 1.0 (dm/get-prop pt :y)))) (defn negate [pt] (assert (point? pt) "point instance expected") - (Point. (- (dm/get-prop pt :x)) - (- (dm/get-prop pt :y)))) + (pos->Point (- (dm/get-prop pt :x)) + (- (dm/get-prop pt :y)))) (defn distance "Calculate the distance between two points." @@ -224,8 +227,8 @@ (dm/get-prop p2 :x)) dy (- (dm/get-prop p1 :y) (dm/get-prop p2 :y))] - (Point. (mth/abs dx) - (mth/abs dy)))) + (pos->Point (mth/abs dx) + (mth/abs dy)))) (defn length [pt] @@ -292,8 +295,8 @@ (assert (number? angle) "expected number") (let [len (length p) angle (mth/radians angle)] - (Point. (* (mth/cos angle) len) - (* (mth/sin angle) len)))) + (pos->Point (* (mth/cos angle) len) + (* (mth/sin angle) len)))) (defn quadrant "Return the quadrant of the angle of the point." @@ -313,22 +316,21 @@ ([pt decimals] (assert (point? pt) "expected point instance") (assert (number? decimals) "expected number instance") - (Point. (mth/precision (dm/get-prop pt :x) decimals) - (mth/precision (dm/get-prop pt :y) decimals)))) + (pos->Point (mth/precision (dm/get-prop pt :x) decimals) + (mth/precision (dm/get-prop pt :y) decimals)))) (defn round-step "Round the coordinates to the closest half-point" [pt step] (assert (point? pt) "expected point instance") - (Point. (mth/round (dm/get-prop pt :x) step) - (mth/round (dm/get-prop pt :y) step))) + (pos->Point (mth/round (dm/get-prop pt :x) step) + (mth/round (dm/get-prop pt :y) step))) (defn transform "Transform a point applying a matrix transformation." [p m] (when (point? p) - (if (nil? m) - p + (if (some? m) (let [x (dm/get-prop p :x) y (dm/get-prop p :y) a (dm/get-prop m :a) @@ -337,18 +339,51 @@ d (dm/get-prop m :d) e (dm/get-prop m :e) f (dm/get-prop m :f)] - (Point. (+ (* x a) (* y c) e) - (+ (* x b) (* y d) f)))))) + (pos->Point (+ (* x a) (* y c) e) + (+ (* x b) (* y d) f))) + p))) +(defn transform! + [p m] + + (dm/assert! + "expected valid rect and matrix instances" + (and (some? p) (some? m))) + + (let [x (dm/get-prop p :x) + y (dm/get-prop p :y) + a (dm/get-prop m :a) + b (dm/get-prop m :b) + c (dm/get-prop m :c) + d (dm/get-prop m :d) + e (dm/get-prop m :e) + f (dm/get-prop m :f)] + #?(:clj + (pos->Point (+ (* x a) (* y c) e) + (+ (* x b) (* y d) f)) + :cljs + (do + (set! (.-x p) (+ (* x a) (* y c) e)) + (set! (.-y p) (+ (* x b) (* y d) f)) + p)))) + +(defn matrix->point + "Returns a result of transform an identity point with the provided + matrix instance" + [m] + (let [e (dm/get-prop m :e) + f (dm/get-prop m :f)] + (pos->Point e f))) + ;; Vector functions (defn to-vec [p1 p2] (subtract p2 p1)) (defn scale [p scalar] - (Point. (* (dm/get-prop p :x) scalar) - (* (dm/get-prop p :y) scalar))) + (pos->Point (* (dm/get-prop p :x) scalar) + (* (dm/get-prop p :y) scalar))) (defn dot [p1 p2] @@ -361,14 +396,14 @@ [p1] (let [p-length (length p1)] (if (mth/almost-zero? p-length) - (Point. 0 0) - (Point. (/ (dm/get-prop p1 :x) p-length) - (/ (dm/get-prop p1 :y) p-length))))) + (pos->Point 0 0) + (pos->Point (/ (dm/get-prop p1 :x) p-length) + (/ (dm/get-prop p1 :y) p-length))))) (defn perpendicular [pt] - (Point. (- (dm/get-prop pt :y)) - (dm/get-prop pt :x))) + (pos->Point (- (dm/get-prop pt :y)) + (dm/get-prop pt :x))) (defn project "V1 perpendicular projection on vector V2" @@ -419,7 +454,7 @@ [p1 p2 t] (let [x (mth/lerp (dm/get-prop p1 :x) (dm/get-prop p2 :x) t) y (mth/lerp (dm/get-prop p1 :y) (dm/get-prop p2 :y) t)] - (Point. x y))) + (pos->Point x y))) (defn rotate "Rotates the point around center with an angle" @@ -441,7 +476,7 @@ y (+ (* sa (- px cx)) (* ca (- py cy)) cy)] - (Point. x y))) + (pos->Point x y))) (defn scale-from "Moves a point in the vector that creates with center with a scale @@ -457,10 +492,10 @@ [p] (let [x (dm/get-prop p :x) y (dm/get-prop p :y)] - (Point. (if (mth/almost-zero? x) 0.001 x) - (if (mth/almost-zero? y) 0.001 y)))) - + (pos->Point (if (mth/almost-zero? x) 0.001 x) + (if (mth/almost-zero? y) 0.001 y)))) +;; FIXME: perfromance (defn abs [point] (-> point diff --git a/common/src/app/common/geom/rect.cljc b/common/src/app/common/geom/rect.cljc index fe91fa4184..b7fbb68da8 100644 --- a/common/src/app/common/geom/rect.cljc +++ b/common/src/app/common/geom/rect.cljc @@ -11,9 +11,10 @@ [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.math :as mth] + [app.common.record :as rc] [app.common.transit :as t])) -(defrecord Rect [x y width height x1 y1 x2 y2]) +(rc/defrecord Rect [x y width height x1 y1 x2 y2]) (defn rect? [o] @@ -62,10 +63,7 @@ (when (d/num? x y width height) (let [w (mth/max width 0.01) h (mth/max height 0.01)] - (->Rect x y w h x y (+ x w) (+ y h))))) - - ([x y w h x1 y1 x2 y2] - (->Rect x y w h x1 y1 x2 y2))) + (pos->Rect x y w h x y (+ x w) (+ y h)))))) (def empty-rect (make-rect 0 0 0.01 0.01)) @@ -104,6 +102,31 @@ :x2 (+ x w) :y2 (+ y h))))) +(defn update-rect! + [rect type] + (case type + (:size :position) + (let [x (dm/get-prop rect :x) + y (dm/get-prop rect :y) + w (dm/get-prop rect :width) + h (dm/get-prop rect :height)] + (rc/assoc! rect + :x1 x + :y1 y + :x2 (+ x w) + :y2 (+ y h))) + + :corners + (let [x1 (dm/get-prop rect :x1) + y1 (dm/get-prop rect :y1) + x2 (dm/get-prop rect :x2) + y2 (dm/get-prop rect :y2)] + (rc/assoc! rect + :x (mth/min x1 x2) + :y (mth/min y1 y2) + :width (mth/abs (- x2 x1)) + :height (mth/abs (- y2 y1)))))) + (defn close-rect? [rect1 rect2] @@ -123,7 +146,6 @@ (defn rect->points [rect] - (dm/assert! "expected rect instance" (rect? rect)) @@ -140,6 +162,12 @@ (gpt/point (+ x w) (+ y h)) (gpt/point x (+ y h))])))) +(defn rect->point + "Extract the position part of the rect" + [rect] + (gpt/point (dm/get-prop rect :x) + (dm/get-prop rect :y))) + (defn rect->center [rect] (dm/assert! (rect? rect)) @@ -231,17 +259,22 @@ (when (d/num? minx miny maxx maxy) (make-rect minx miny (- maxx minx) (- maxy miny)))))) -(defn center->rect [{:keys [x y]} width height] - (when (d/num? x y width height) - (make-rect (- x (/ width 2)) - (- y (/ height 2)) - width - height))) +(defn center->rect + [point w h] + (when (some? point) + (let [x (dm/get-prop point :x) + y (dm/get-prop point :y)] + (when (d/num? x y w h) + (make-rect (- x (/ w 2)) + (- y (/ h 2)) + w + h))))) (defn s= [a b] (mth/almost-zero? (- a b))) +;; FIXME: performance (defn overlaps-rects? "Check for two rects to overlap. Rects won't overlap only if one of them is fully to the left or the top"