penpot/common/src/app/common/math.cljc
Andrey Antukh 3ceb4cf895 ♻️ Make svg to shapes conversion code multiplatform
- Move clojure code to common
- Rewrite some native-js code into optimized clojure
2023-11-07 12:48:31 +01:00

226 lines
4.5 KiB
Clojure

;; 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.math
"A collection of math utils."
(:refer-clojure :exclude [abs min max])
#?(:cljs
(:require-macros [app.common.math :refer [min max]]))
(:require
#?(:cljs [goog.math :as math])
[clojure.core :as c]))
(defmacro min
[& params]
(if (:ns &env)
`(js/Math.min ~@params)
`(c/min ~@params)))
(defmacro max
[& params]
(if (:ns &env)
`(js/Math.max ~@params)
`(c/max ~@params)))
(def PI
#?(:cljs (.-PI js/Math)
:clj Math/PI))
(defn nan?
[v]
#?(:cljs (js/isNaN v)
:clj (Double/isNaN v)))
;; NOTE: on cljs we don't need to check for `number?` so we explicitly
;; ommit it for performance reasons.
(defn finite?
[v]
#?(:cljs (and (not (nil? v)) (js/isFinite v))
:clj (and (not (nil? v)) (number? v) (Double/isFinite v))))
(defn finite
[v default]
(if (finite? v) v default))
(defn abs
[v]
#?(:cljs (js/Math.abs v)
:clj (Math/abs (double v))))
(defn sin
"Returns the sine of a number"
[v]
#?(:cljs (js/Math.sin v)
:clj (Math/sin v)))
(defn cos
"Returns the cosine of a number."
[v]
#?(:cljs (js/Math.cos v)
:clj (Math/cos v)))
(defn acos
"Returns the arccosine of a number."
[v]
#?(:cljs (js/Math.acos v)
:clj (Math/acos v)))
(defn tan
"Returns the tangent of a number."
[v]
#?(:cljs (js/Math.tan v)
:clj (Math/tan v)))
(defn atan2
"Returns the arctangent of the quotient of its arguments."
[x y]
#?(:cljs (js/Math.atan2 x y)
:clj (Math/atan2 x y)))
(defn neg
"Negate the number"
[v]
(- v))
(defn sq
"Calculates the square of a number"
[v]
(* v v))
(defn pow
"Returns the base to the exponent power."
[b e]
#?(:cljs (js/Math.pow b e)
:clj (Math/pow b e)))
(defn sqrt
"Returns the square root of a number."
[v]
#?(:cljs (js/Math.sqrt v)
:clj (Math/sqrt v)))
(defn cubicroot
"Returns the cubic root of a number"
[v]
(if (pos? v)
(pow v (/ 1 3))
(- (pow (- v) (/ 1 3)))))
(defn floor
"Returns the largest integer less than or
equal to a given number."
[v]
#?(:cljs (js/Math.floor v)
:clj (Math/floor v)))
(defn round
"Returns the value of a number rounded to
the nearest integer.
If given step rounds to the next closest step, for example:
(round 13.4 0.5) => 13.5
(round 13.4 0.3) => 13.3"
([v step]
(* (round (/ v step)) step))
([v]
#?(:cljs (js/Math.round v)
:clj (Math/round (float v)))))
(defn ceil
"Returns the smallest integer greater than
or equal to a given number."
^double
[v]
#?(:cljs (js/Math.ceil v)
:clj (Math/ceil ^double v)))
(defn precision
[v n]
(when (and (number? v) (integer? n))
(let [d (pow 10 n)]
(/ (round (* v d)) d))))
(defn to-fixed
"Returns a string representing the given number, using fixed precision."
[v n]
#?(:cljs (.toFixed ^js v n)
:clj (str (precision v n))))
(defn radians
"Converts degrees to radians."
[degrees]
#?(:cljs (math/toRadians degrees)
:clj (Math/toRadians degrees)))
(defn degrees
"Converts radians to degrees."
[radians]
#?(:cljs (math/toDegrees radians)
:clj (Math/toDegrees radians)))
(defn hypot
"Square root of the squares addition"
[a b]
#?(:cljs (js/Math.hypot a b)
:clj (Math/hypot a b)))
(defn distance
"Calculate the distance between two points."
[[x1 y1] [x2 y2]]
(let [dx (- x1 x2)
dy (- y1 y2)]
(-> (hypot dx dy)
(precision 2))))
(defn log10
"Logarithm base 10"
[x]
#?(:cljs (js/Math.log10 x)
:clj (Math/log10 x)))
(defn clamp [num from to]
(if (< num from)
from
(if (> num to) to num)))
(defn almost-zero? [num]
(< (abs (double num)) 1e-4))
(defn round-to-zero
"Given a number if it's close enough to zero round to the zero to avoid precision problems"
[num]
(if (< (abs num) 1e-4)
0
num))
(defonce float-equal-precision 0.001)
(defn close?
"Equality for float numbers. Check if the difference is within a range"
([num1 num2]
(close? num1 num2 float-equal-precision))
([num1 num2 precision]
(<= (abs (- num1 num2)) precision)))
(defn lerp
"Calculates a the linear interpolation between two values and a given percent"
[v0 v1 t]
(+ (* (- 1 t) v0)
(* t v1)))
(defn max-abs
[a b]
(max (abs a)
(abs b)))
(defn sign
"Get the sign (+1 / -1) for the number"
[n]
(if (neg? n) -1 1))