Skip to content

Instantly share code, notes, and snippets.

@chrisburnell
Created April 24, 2018 13:56
Show Gist options
  • Save chrisburnell/d49dad087c0a2c2a8634fddc922d7ba6 to your computer and use it in GitHub Desktop.
Save chrisburnell/d49dad087c0a2c2a8634fddc922d7ba6 to your computer and use it in GitHub Desktop.
Mathematics for SCSS
///
// 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