// Inverse trigonometric functions with Sass using the infinite series expansion. // Latest version! // First got the idea after seeing this post: // http://www.japborst.net/blog/sass-sines-and-cosines.html // precision could be better with a smaller default error // current default error is 2.5% of a degree // and works well enough // completely useless to make it drop under .5% of a degree $default_err: pi()/14400; @function asin($val, $e: $default_err) { /* * $val: the value for which we compute the arc sine; * $e: tolerated error; lower = better precision * * the function returns the arc sine of $val * * Examples: * asin(.5) returns 29.97584deg (using default tolerated error) * asin(0, pi()/180) returns 0deg */ $sum: 0; // the "infinite" sum we compute $sign: 1; // sign of angle $flag: 0; // 0 if angle in absolute value < 45deg, 1 otherwise $i: 0; // current index $c: 1; $j: 2*$i + 1; @if abs($val) > sin(45deg) { $flag: 1; $sign: $val/abs($val); $val: sqrt(1 - pow($val, 2)); } $term: $c*pow($val, $j)/$j; @while abs($term) > $e { $sum: $sum + $term; $i: $i + 1; $c: $c*(2*$i - 1)/(2*$i); $j: 2*$i + 1; $term: $c*pow($val, $j)/$j; } $result: $sign*($flag*pi()/2 + pow(-1, $flag)*$sum); @return $result/pi()*180deg; } @function acos($val, $e: $default_err) { /* * $val: the value for which we compute the arc cosine * $e: tolerated error; lower = better precision * * the function returns the arc cosine of $val * * Examples: * acos(.5) returns 60.02416deg (using default tolerated error) * acos(0, pi()/180) returns 90deg */ @return 90deg - asin($val, $e); } @function atan($val, $e: $default_err) { /* * $val: the value for which we compute the arc tangent * $e: tolerated error; lower = better precision * * the function returns the arc tangent of $val * * Examples: * atan(1) returns ~45deg (using default tolerated error) * atan(0, pi()/180) returns 0deg */ $val: $val/sqrt(1 + pow($val, 2)); @return asin($val, $e); } $n-levels: 13; $n-edges: 4; $a: 2em; $t: 5s; $base-angle: 360deg/$n-edges; html { overflow: hidden; background: #222; color: white; &:after { $dim: $n-levels*$a; position: absolute; top: 50%; left: 50%; margin: -$dim/2; width: $dim; height: $dim; box-shadow: 0 0 0 50vmax; content: ''; } } .assembly { &, *, :before { position: absolute; top: 50%; left: 50%; } animation: rot $t linear infinite; } .square { $n: 1; @for $l from 1 to $n-levels { $n-level: $l*$n-edges; &:nth-child(n + #{$n + 1}):before { animation-name: ani#{$l}; } @at-root { @keyframes ani#{$l} { #{($l + 1)/$n-levels*90%}, 100% { transform: rotate($base-angle/2) scale(if($l%2 == 0, 1/cos($base-angle/2), 0)); } } } @for $i from 0 to $n-level { $poly-ir: $l*$a; $poly-cr: $l*$a/cos($base-angle/2); $poly-edge: 2*$poly-cr*sin($base-angle/2); $j: $i%$l; $poly-edge-diff: abs($j - $l/2)*$poly-edge/$l; $dist: sqrt(pow($poly-ir, 2) + pow($poly-edge-diff, 2)); $curr-angle: $base-angle*floor($i/$l) + atan(($j - $l/2)*$poly-edge/$l/$poly-ir); &:nth-child(#{$n + $i + 1}) { transform: rotate($curr-angle) translate($dist) rotate(-$curr-angle); } } $n: $n + $n-level; } content: $n; &:before { margin: -$a/2; width: $a; height: $a; background: white; animation: ani0 $t linear infinite; content: ''; } } @keyframes ani0 { #{1/$n-levels*90%}, 100% { transform: rotate($base-angle/2) scale(1/cos($base-angle/2)); } } @keyframes rot { to { transform: rotate($base-angle/2) scale(cos($base-angle/2)); } }