Created
April 24, 2018 13:56
-
-
Save chrisburnell/d49dad087c0a2c2a8634fddc922d7ba6 to your computer and use it in GitHub Desktop.
Mathematics for SCSS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// | |
// Math SCSS | |
/// | |
// Constants | |
$E: 2.718281828459045; | |
$PI: 3.141592653589793; | |
$LN2: 0.6931471805599453; | |
$SQRT2: 1.4142135623730951; | |
// Returns base to the exponent power. | |
// @param {Number} $base The base number | |
// @param {Number} $exp The exponent to which to raise base | |
// @return {Number} | |
// @example | |
// pow(4, 2) // 16 | |
// pow(4, -2) // 0.0625 | |
// pow(4, 0.2) // 1.31951 | |
@function pow ($base, $exp) { | |
@if $exp == floor($exp) { | |
$r: 1; | |
$s: 0; | |
@if $exp < 0 { | |
$exp: $exp * -1; | |
$s: 1; | |
} | |
@while $exp > 0 { | |
@if $exp % 2 == 1 { | |
$r: $r * $base; | |
} | |
$exp: floor($exp * 0.5); | |
$base: $base * $base; | |
} | |
@return if($s != 0, 1 / $r, $r); | |
} @else if $base == 0 and $exp > 0 { | |
@return 0; | |
} @else { | |
$expint: floor($exp); | |
$r1: pow($base, $expint); | |
$r2: _exp(log($base) * ($exp - $expint)); | |
@return $r1 * $r2; | |
} | |
} | |
// A good approximation for $x close to 0. | |
@function _exp ($x) { | |
$ret: 0; | |
$i: 1; | |
@for $n from 0 to 24 { | |
$ret: $ret + $i; | |
$i: $i * $x / ($n + 1); | |
} | |
@return $ret; | |
} | |
// Returns the factorial of a non-negative integer. | |
// @param {Number} $x A non-negative integer. | |
// @return {Number} | |
// @example | |
// fact(0) // 1 | |
// fact(8) // 40320 | |
@function fact ($x) { | |
@if $x < 0 or $x != floor($x) { | |
@warn "Argument for `fact()` must be a positive integer."; | |
@return null; | |
} | |
$ret: 1; | |
@while $x > 0 { | |
$ret: $ret * $x; | |
$x: $x - 1; | |
} | |
@return $ret; | |
} | |
// Returns the square root of a number. | |
// @param {Number} $x | |
// @example | |
// sqrt(2) // 1.41421 | |
// sqrt(5) // 2.23607 | |
@function sqrt ($x) { | |
@if $x < 0 { | |
@warn "Argument for `sqrt()` must be a positive number."; | |
@return null; | |
} | |
$ret: 1; | |
@for $i from 1 through 24 { | |
$ret: $ret - ($ret * $ret - $x) / (2 * $ret); | |
} | |
@return $ret; | |
} | |
// Returns E^x, where x is the argument, and E is Euler's constant, the base of the natural logarithms. | |
// @param {Number} $x | |
// @example | |
// exp(1) // 2.71828 | |
// exp(-1) // 0.36788 | |
@function exp ($x) { | |
@return pow($E, $x); | |
} | |
// Returns a two-element list containing the normalized fraction and exponent of number. | |
// @param {Number} $x | |
// @return {List} fraction, exponent | |
@function frexp ($x) { | |
$exp: 0; | |
@if $x < 0 { | |
$x: $x * -1; | |
} | |
@if $x < 0.5 { | |
@while $x < 0.5 { | |
$x: $x * 2; | |
$exp: $exp - 1; | |
} | |
} @else if $x >= 1 { | |
@while $x >= 1 { | |
$x: $x / 2; | |
$exp: $exp + 1; | |
} | |
} | |
@return $x, $exp; | |
} | |
// Returns $x * 2^$exp | |
// @param {Number} $x | |
// @param {Number} $exp | |
@function ldexp ($x, $exp) { | |
$b: if($exp >= 0, 2, 1 / 2); | |
@if $exp < 0 { | |
$exp: $exp * -1; | |
} | |
@while $exp > 0 { | |
@if $exp % 2 == 1 { | |
$x: $x * $b; | |
} | |
$b: $b * $b; | |
$exp: floor($exp * 0.5); | |
} | |
@return $x; | |
} | |
// Returns the natural logarithm of a number. | |
// @param {Number} $x | |
// @param {Number} $b The base number | |
// @example | |
// log(2) // 0.69315 | |
// log(10) // 2.30259 | |
// log(2, 10) // 0.30103 | |
@function log ($x, $b: null) { | |
@if $b != null { | |
@return log($x) / log($b); | |
} | |
@if $x <= 0 { | |
@return 0 / 0; | |
} | |
$k: nth(frexp($x / $SQRT2), 2); | |
$x: $x / ldexp(1, $k); | |
@return $LN2 * $k + _log($x); | |
} | |
// a good aproximation for $x close to 1 | |
@function _log ($x) { | |
$x: ($x - 1) / ($x + 1); | |
$x2: $x * $x; | |
$i: 1; | |
$s: $x; | |
$sp: null; | |
@while $sp != $s { | |
$x: $x * $x2; | |
$i: $i + 2; | |
$sp: $s; | |
$s: $s + $x / $i; | |
} | |
@return 2 * $s; | |
} | |
// Returns the sine of a number. | |
// @param {Number} $x A number in rad or deg. Assuming unitless number to be in rad. | |
// @example | |
// sin(1.0472) // 0.86603 | |
// sin(60deg) // 0.86603 | |
@function sin ($x) { | |
$x: unitless-rad($x); | |
@return cos($x - $PI / 2); | |
} | |
// Returns the cosine of a number. | |
// @param {Number} $x A number in rad or deg. Assuming unitless number to be in rad. | |
// @example | |
// cos(0.7854) // 0.70711 | |
// cos(45deg) // 0.70711 | |
@function cos ($x) { | |
$x: unitless-rad($x) % ($PI * 2); | |
$ret: 1; | |
$i: 1; | |
@for $n from 1 to 24 { | |
$i: $i * -1 * $x * $x / (2 * $n) / (2 * $n - 1); | |
$ret: $ret + $i; | |
} | |
@return $ret; | |
} | |
// Returns the tangent of a number. | |
// @param {Number} $x A number in rad or deg. Assuming unitless number to be in rad. | |
// @example | |
// tan(0.5236) // 0.57735 | |
// tan(30deg) // 0.57735 | |
@function tan ($x) { | |
$x: unitless-rad($x); | |
@return sin($x) / cos($x); | |
} | |
// Returns the cosecant of a number. | |
// @param {Number} $x A number in rad or deg. Assuming unitless number to be in rad. | |
// @example | |
// csc(1.0472) // 1.1547 | |
// csc(60deg) // 1.1547 | |
@function csc ($x) { | |
$x: unitless-rad($x); | |
@return 1 / sin($x); | |
} | |
// Returns the secant of a number. | |
// @param {Number} $x A number in rad or deg. Assuming unitless number to be in rad. | |
// @example | |
// sec(0.7854) // 1.41422 | |
// sec(45deg) // 1.41422 | |
@function sec ($x) { | |
$x: unitless-rad($x); | |
@return 1 / cos($x); | |
} | |
// Returns the cotangent of a number. | |
// @param {Number} $x A number in rad or deg. Assuming unitless number to be in rad. | |
// @example | |
// cot(0.5236) // 1.73205 | |
// cot(30deg) // 1.73205 | |
@function cot ($x) { | |
$x: unitless-rad($x); | |
@return 1 / tan($x); | |
} | |
// Returns the arcsine of a number. | |
// @param {Number} $x A number between -1 and 1. | |
// @example | |
// asin(0.1) // 0.10017 | |
// asin(-1) // -1.5708 | |
@function asin ($x) { | |
@if $x > 1 or $x < -1 { | |
@warn "Argument for `asin()` must be a number between -1 and 1"; | |
@return null; | |
} | |
@return atan($x / sqrt(1 - $x * $x)); | |
} | |
// Returns the arccosine of a number. | |
// @param {Number} $x A number between -1 and 1. | |
// @example | |
// acos(0.1) // 1.47063 | |
// acos(-1) // 3.14159 | |
@function acos ($x) { | |
@if $x > 1 or $x < -1 { | |
@warn "Argument for `acos()` must be a number between -1 and 1"; | |
@return null; | |
} | |
@return $PI / 2 - asin($x); | |
} | |
// Returns the arctangent of a number. | |
// @param {Number} $x | |
// @example | |
// atan(0.1) // 0.09967 | |
// atan(-1) // -0.7854 | |
@function atan ($x) { | |
$i: 24; | |
$sgn: 0; | |
$a: 0; | |
@if $x > 1 { | |
$sgn: 1; | |
$x: 1 / $x; | |
} @else if $x < -1 { | |
$sgn: -1; | |
$x: 1 / $x; | |
} | |
@while $i > 0 { | |
$a: ($i * $i * $x * $x) / (2 * $i + 1 + $a); | |
$i: $i - 1; | |
} | |
@if $sgn > 0 { | |
@return $PI / 2 - $x / (1 + $a); | |
} @else if $sgn < 0 { | |
@return -$PI / 2 - $x / (1 + $a); | |
} @else { | |
@return $x / (1 + $a); | |
} | |
} | |
// Returns the arctangent of the quotient of its arguments. | |
// @param {Number} $y | |
// @param {Number} $x | |
// @example | |
// atan2(0, 0) // 0 | |
// atan2(0, -0.0) // 3.14159 | |
// atan2(-0.0, 0) // 0 | |
// atan2(-0.0, -0.0) // -3.14159 | |
// atan2(0, 1) // 0 | |
// atan2(0, -1) // 3.14159 | |
@function atan2 ($y, $x) { | |
@if $x > 0 { | |
@return atan($y / $x); | |
} @else if $x < 0 { | |
@if $y < 0 { | |
@return atan($y / $x) - $PI; | |
} @else { | |
@return atan($y / $x) + $PI; | |
} | |
} @else { | |
@if $y < 0 { | |
@return - $PI / 2; | |
} @else if $y > 0 { | |
@return $PI / 2; | |
} @else { | |
@if 1 / $x == 1 / 0 { | |
@return 0; | |
} @else { | |
@if 1 / $y == 1 / 0 { | |
@return $PI; | |
} @else { | |
@return -$PI; | |
} | |
} | |
} | |
} | |
} | |
// Strip unit from a number | |
@function strip-unit ($number) { | |
@if unitless($number) { | |
@return $number; | |
} @else { | |
@return $number / ($number * 0 + 1); | |
} | |
} | |
@function strip-units ($number) { | |
@return strip-unit($number); | |
} | |
// Convert deg to rad | |
@function deg-to-rad ($deg, $unit: true) { | |
@return strip-unit($deg) * $PI / 180 * if($unit, 1rad, 1); | |
} | |
// Convert rad to deg | |
@function rad-to-deg ($rad, $unit: true) { | |
@return strip-unit($rad) * 180 / $PI * if($unit, 1deg, 1); | |
} | |
// Convert to unitless rad | |
@function unitless-rad ($angle) { | |
@if unitless($angle) { | |
@return $angle; | |
} @else if unit($angle) == rad { | |
@return $angle / 1rad; | |
} @else if unit($angle) == deg { | |
@return deg-to-rad($angle, false); | |
} @else if type-of($angle) != number or not unitless($angle) { | |
@warn "#{ $angle } is not a valid number."; | |
@return $angle; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment