Skip to content

Commit

Permalink
Fix pow function in order to fix luminance test
Browse files Browse the repository at this point in the history
lumninance uses float exponents which our current pow implmentation doens't support. Replaced with a powerfull math pow function provide by Tobias Bengfort.

Write some more assertions specifient all variations of input values that a pow function can have.
  • Loading branch information
renatodeleao committed Nov 29, 2019
1 parent 07623d2 commit 7695443
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 13 deletions.
2 changes: 1 addition & 1 deletion scss/tools/functions/__tests__/_fn.luminance.spec.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

@include describe("luminance()") {
@include it("Returns luminance of color") {
@include assert-equal(luminance(#555), 0.04987, $inspect: true);
@include assert-equal(luminance(#555), 0.0908417112, $inspect: true);
}
}
26 changes: 24 additions & 2 deletions scss/tools/functions/__tests__/_fn.pow.spec.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,29 @@
@import "../_fn.pow";

@include describe("pow()") {
@include it("Returns value powered to exponent") {
@include assert-equal(pow(2, 2), 4);
@include it("Returns result of position integer number powered to positive integer exponent") {
@include assert-equal(pow(2, 2, 2), 4);
}

@include it("Returns result of positive integer number powered to negative integer exponent") {
@include assert-equal(pow(2, -2, 2), 0.25);
}

@include it("Returns result of positive integer number powered to a float exponent") {
@include assert-equal(pow(2, 2.5), 5.6568542495);
}

@include it("Returns result of positive float number powered to a positive integer exponent") {
@include assert-equal(pow(2.5, 2), 6.25);
}

@include it("Returns result of positive float number powered to a positive integer exponent") {
@include assert-equal(pow(2.5, -2, 2), 0.16);
}

/// hack despite the number output is the same the test still fails
/// probably related to sass precision, requires investigation
@include it("Returns result of positive float number powered to a positive float exponent") {
@include assert-equal("#{pow(1.5, 1.5)}", "1.8371173071");
}
}
109 changes: 99 additions & 10 deletions scss/tools/functions/_fn.pow.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,106 @@
$PI: 3.141592653589793;

@function -math-exp-taylor-0($x, $steps) {
$item: 1;
$result: 1;

@for $i from 1 to $steps {
$item: $item * $x / $i;
$result: $result + $item;
}

@return $result;
}

@function -math-ln-taylor-1($x, $steps) {
$z: ($x - 1) / ($x + 1);

$power: $z;
$result: $z;

@for $i from 1 to $steps {
$power: $power * $z *$z;
$result: $result + $power / (2 * $i + 1);
}

@return 2 * $result;
}

@function -math-sin-taylor-0($x, $steps) {
$item: $x;
$result: $x;

@for $i from 1 to $steps {
$item: -$item * $x * $x / (2 * $i) / (2 * $i + 1);
$result: $result + $item;
}

@return $result;
}

@function -math-pow-int($base, $exponent) {
@if $exponent < 0 {
@return 1 / -math-pow-int($base, -$exponent);
} @else if $exponent == 0 {
@return 1;
} @else if $exponent == 1 {
@return $base;
} @else {
$exp: floor($exponent / 2);
$pow: -math-pow-int($base, $exp);
@if $exp * 2 == $exponent {
@return $pow * $pow;
} @else {
@return $pow * $pow * $base;
}
}
}

@function -math-log-approx($x) {
@if $x <= 0 {
@error "cannot calculate log of #{$x}";
} @else if $x >= 1 {
// choose the smaller option (-1) because it yield better
// results in ln().
@return str-length(inspect(round($x))) - 1;
} @else {
@return -1 * str-length(inspect(round(1 / $x)));
}
}

@function ln($x, $steps: 32) {
$ln10: 2.302585092994046;
$approx: -math-log-approx($x);
// $y is in range [1, 10]
$y: $x / -math-pow-int(10, $approx);
@return $approx * $ln10 + -math-ln-taylor-1($y, $steps);
}

///
/// power number to exponent
/// power a number to exponent, that can be positiv or negative int or float
/// when we get sass dependencies right, consider moving this to the
/// external package dependency like https://github.com/xi/sass-planifolia or
/// https://www.npmjs.com/package/sass-math-pow
///
/// @group Tools
/// @author Hugo Giraudel
/// @author Tobias Bengfort
/// @link https://www.sassmeister.com/gist/5bbe8480c48e2fc10ab5
/// @link https://gist.github.com/davidkpiano/ad6e6771df050ff3727f
/// @link https://css-tricks.com/snippets/sass/power-function/
///
@function pow($number, $exponent) {
$value: 1;
/// @param {Number} $x - integer or float
/// @param {Number} $exponent - integer or float
/// @param {Number} $steps [32] - Number of iterations, more equals more precision, less perf

@if $exponent > 0 {
@for $i from 1 through $exponent {
$value: $value * $number;
@function pow($x, $exponent, $steps: 32) {
$exp1: round($exponent);
$exp2: $exponent - $exp1;
$pow1: -math-pow-int($x, $exp1);
@if $exp2 == 0 {
@return $pow1;
} @else {
$y: ln($x, $steps) * $exp2;
$pow2: -math-exp-taylor-0($y, $steps);
@return $pow1 * $pow2;
}
}

@return $value;
}

0 comments on commit 7695443

Please sign in to comment.