カテゴリー
【Dart Sass対応】scssによる三角関数(cos,sin等)の取扱う方法 〜 基礎から応用まで
※ 当ページには【広告/PR】を含む場合があります。
2020/03/29
2021/06/07
$ npm install node-sass
#OR
$ yarn add node-sass
Scssで独自関数を扱うための基本
ローカル変数 ($)
$
$str: 'a strong guy';
$val: 1;
:
$str: 'I am ' + $str; // I am a strong guy
$val: $val * 2; // 2
@function (関数)
@function pi() {
@return 3.14159265359;
}
@function add_one($num) {
@return $num + 1;
}
関数を使うときの呼び出し
$init_val: 1;
$init_val: add_one($init_val); // 2
$init_val: add_one($init_val); // 3
$init_val: add_one($init_val); // 4
$init_val: $init_val / $init_val; // 1
$init_val: $init_val * pi(); // 3.14159265359
$init_val: $init_val - pi(); // 0
$init_val: $init_val + pi(); // 3.14159265359
$init_val: $init_val % 3; // 0.14159265359
配列 (list)
List
[]
()
$hoge_list: (0, 1, 2, 3, 4);
$len: length($hoge_list); // 5
$val: nth($hoge_list, 1); // 0
$val: nth($hoge_list, 2); // 1
$val: nth($hoge_list, 3); // 2
$val: nth($hoge_list, 4); // 3
$val: nth($hoge_list, 5); // 4
$hoge_list
length
nth
@for
@for
$hoge_list: (0, 1, 2, 3, 4);
$val: 0;
@for $i from 1 through length($hoge_list) {
$val: nth($hoge_list, $i);
}
{}
$val
@if
$result: '';
$mode: 1;
@if $mode == 0 {
$result: 'Good';
} @else if $mode == 1 {
$result: 'Not bad';
} @else {
$result: 'Worst';
}
$result
@debug
console.log
@debug
$misterous_val: 'Unknown';
// ...
@debug $misterous_val; // Unknown
実装編その1 〜 テイラー展開法による三角関数を作る
テイラー展開式とは
Eq. (1)
Eq. (2)
指数計算するための関数
@function pow($bs, $exp) {
$val: $bs;
@if $exp > 1 {
@for $i from 2 through $exp {
$val: $val * $bs;
}
} @else if $exp < 1 {
@for $i from 0 through -$exp {
$val: $val / $bs;
}
}
@return $val;
}
階乗の計算
@function fact($num) {
$val: 1;
@if $num > 0 {
@for $i from 1 through $num {
$val: $val * $i;
}
}
@return $val;
}
角度からラジアンへの変換
unit()
@function pi() {
@return 3.14159265359;
}
@function rad($angle) {
$unit: unit($angle);
$val: $angle / (1 + 0 * $angle);
// 変数に角度(deg)が渡されていると、ラジアンに変換
// それ以外は、問答無用でラジアン
@if $unit == "deg" {
$val: $val / 180 * pi();
}
@return $val;
}
三角関数の計算
$accuracy: 10; // 計算精度
@function sin($angle) {
$theta: rad($angle);
$sin: $theta;
@for $n from 1 through $accuracy {
$sin: $sin + pow(-1, $n) / fact(2 * $n + 1) * pow($a, 2 * $n + 1);
}
@return $sin;
}
@function cos($angle) {
$a: rad($angle);
$cos: 1;
@for $n from 1 through $accuracy {
$cos: $cos + pow(-1, $n) / fact(2 * $n) * pow($a, 2 * $n);
}
@return $cos;
}
// ...
@for $s1 from 0 through 9 {
$angle: 10deg * $s1;
@debug $angle, sin($angle), cos($angle);
}
// ...
DEBUG: 0deg, 0, 1
DEBUG: 10deg, 0.1736481777, 0.984807753
DEBUG: 20deg, 0.3420201433, 0.9396926208
DEBUG: 30deg, 0.5, 0.8660254038
DEBUG: 40deg, 0.6427876097, 0.7660444431
DEBUG: 50deg, 0.7660444431, 0.6427876097
DEBUG: 60deg, 0.8660254038, 0.5
DEBUG: 70deg, 0.9396926208, 0.3420201433
DEBUG: 80deg, 0.984807753, 0.1736481777
DEBUG: 90deg, 1, 0
実装編その2 〜 CORDICアルゴリズムで三角関数を作る
sassでゴリゴリとテイラー展開やらせるのは思うほど実用性がないんではないか…
ちょっとだけCORDICアルゴリズムの解説
Eq. (3)
Eq. (4)
Sassでビット演算できるのか?
'2で割る'
'自分の好みの数で割って'
CORDICアルゴリズムの実装
$cordic_dataset: (
0: (45, 1),
1: (26.56505118, 2),
2: (14.03624347, 4),
3: (7.125016349, 8),
4: (3.576334375, 16),
5: (1.789910608, 32),
6: (0.8951737102, 64),
7: (0.4476141709, 128),
8: (0.2238105004, 256),
9: (0.1119056771, 512),
10: (0.05595289189, 1024),
11: (0.02797645262, 2048),
12: (0.01398822714, 4096),
13: (0.006994113675, 8192),
14: (0.003497056851, 16384),
15: (0.001748528427, 32768),
16: (0.0008742642137, 65536),
17: (0.0004371321069, 131072),
18: (0.0002185660534, 262144),
);
$s_all: 1.646760258;
$cordic_dataset
Eq. (5)
範囲制限を考慮した補正
あらゆる角度
@function check_domain($angle) {
$reduced_angle: $angle % 360;
$result: (0, 0, 0);
$constraint_angle: $reduced_angle % 90;
@if ($reduced_angle >= 0) and ($reduced_angle < 90) {
$result: ($constraint_angle, 1, 1);
} @else if ($reduced_angle >= 90) and ($reduced_angle < 180) {
$result: (90 - $constraint_angle, -1, 1);
} @else if ($reduced_angle >= 180) and ($reduced_angle < 270) {
$result: ($constraint_angle, -1, -1);
} @else {
$result: (90 - $constraint_angle, 1, -1);
}
@return $result;
}
CORDICコード
@function cossintan($angle, $mode) {
$modifier: check_domain($angle);
$p_c: 1 / $sum_all;
$q_c: 0;
$theta: 0;
$p_next: 0;
$q_next: 0;
$result: 0;
@for $n from 1 through length($cordic_dataset) {
$coeffecient: nth(nth($cordic_dataset, $n), 2);
@if nth($modifier, 1) > $theta {
$p_next: $p_c - ($q_c / nth($coeffecient, 2));
$q_next: $q_c + ($p_c / nth($coeffecient, 2));
$theta: $theta + nth($coeffecient, 1);
} @else {
$p_next: $p_c + ($q_c / nth($coeffecient, 2));
$q_next: $q_c - ($p_c / nth($coeffecient, 2));
$theta: $theta - nth($coeffecient, 1);
}
$p_c: $p_next;
$q_c: $q_next;
}
@if $mode == 0 {
$result: $p_c * nth($modifier, 2); // cos
} @else if $mode == 1 {
$result: $q_c * nth($modifier, 3); // sin
} @else {
$result: $q_c / $p_c * nth($modifier, 2) * nth($modifier, 3); // tan
}
@return $result;
}
cos
sin
tan
検証
@for $t from 0 through 18 {
$angle: 10 * $t;
@debug unquote('angle:') $angle, unquote('cos:') cossintan($angle, 0), unquote('sin:') cossintan($angle, 1);
}
DEBUG: angle: 0, cos: 1.0000000001, sin: -0.0000014786
DEBUG: angle: 10, cos: 0.9848072373, sin: 0.1736511027
DEBUG: angle: 20, cos: 0.9396914558, sin: 0.3420233442
DEBUG: angle: 30, cos: 0.8660238404, sin: 0.500002708
DEBUG: angle: 40, cos: 0.7660461494, sin: 0.6427855763
DEBUG: angle: 50, cos: 0.6427855763, sin: 0.7660461494
DEBUG: angle: 60, cos: 0.500002708, sin: 0.8660238404
DEBUG: angle: 70, cos: 0.3420233442, sin: 0.9396914558
DEBUG: angle: 80, cos: 0.1736511027, sin: 0.9848072373
DEBUG: angle: 90, cos: -0.0000014786, sin: 1.0000000001
DEBUG: angle: 100, cos: -0.1736511027, sin: 0.9848072373
DEBUG: angle: 110, cos: -0.3420233442, sin: 0.9396914558
DEBUG: angle: 120, cos: -0.500002708, sin: 0.8660238404
DEBUG: angle: 130, cos: -0.6427855763, sin: 0.7660461494
DEBUG: angle: 140, cos: -0.7660461494, sin: 0.6427855763
DEBUG: angle: 150, cos: -0.8660238404, sin: 0.500002708
DEBUG: angle: 160, cos: -0.9396914558, sin: 0.3420233442
DEBUG: angle: 170, cos: -0.9848072373, sin: 0.1736511027
DEBUG: angle: 180, cos: -1.0000000001, sin: 0.0000014786
実装編その3 〜 改良版!CORDICアルゴリズムで三角関数を作る
式をちょっと修正
Eq. (6)
Sassの@while文を利用する
@while
@for
@while
$cordic_dataset: (
0: ( 45, 1, 1.414213562),
1: ( 26.56505118, 2, 1.118033989),
2: ( 6.340191746, 9, 1.006153904),
3: ( 0.8951737102, 64, 1.000122063),
4: ( 0.09167316899, 625, 1.00000128),
5: (7.368284362e-3, 7776, 1.000000008),
6: (4.870060902e-4, 117649, 1),
7: (2.732075668e-5, 2097152, 1),
8: (1.331013796e-6, 43046721, 1),
9: (5.729577951e-8, 1e9, 1),
10: ( 2.209e-9, 2.5937425e10, 1)
);
@function cordic_2($angle, $mode) {
$modifier: check_domain($angle);
$p_c: 1;
$q_c: 0;
$p_next: $p_c;
$q_next: $q_c;
$result: 0;
$delta: nth($modifier, 1);
$stage_index: 1;
$upper_limit: length($cordic_dataset);
$resolution: 1e-8;
@while $delta > $resolution {
$coefficient: nth(nth($cordic_dataset, $stage_index), 2);
@if $delta >= nth($coefficient, 1) {
$p_next: ($p_c - $q_c / nth($coefficient, 2)) / nth($coefficient, 3);
$q_next: ($q_c + $p_c / nth($coefficient, 2)) / nth($coefficient, 3);
$delta: $delta - nth($coefficient, 1);
} @else {
$stage_index: $stage_index + 1;
@if $stage_index > $upper_limit {
$delta: $resolution;
}
}
$p_c: $p_next;
$q_c: $q_next;
}
@if $mode == 0 {
$result: $p_c * nth($modifier, 2); // cos
} @else if $mode == 1 {
$result: $q_c * nth($modifier, 3); // sin
} @else {
$result: $q_c / $p_c * nth($modifier, 2) * nth($modifier, 3); // tan
}
@return $result;
}
@for $s1 from 0 through 9 {
$angle: 10 * $s1;
@debug unquote('angle:') $angle, unquote('cos:') cordic_2($angle, 0), unquote('sin:') cordic_2($angle, 1);
}
DEBUG: angle: 0, cos: 1, sin: 0
DEBUG: angle: 10, cos: 0.9848077558, sin: 0.173648178
DEBUG: angle: 20, cos: 0.9396926244, sin: 0.3420201445
DEBUG: angle: 30, cos: 0.8660254039, sin: 0.4999999998
DEBUG: angle: 40, cos: 0.7660444442, sin: 0.6427876103
DEBUG: angle: 50, cos: 0.6427876113, sin: 0.7660444447
DEBUG: angle: 60, cos: 0.5000000017, sin: 0.8660254065
DEBUG: angle: 70, cos: 0.3420201443, sin: 0.9396926231
DEBUG: angle: 80, cos: 0.1736481781, sin: 0.9848077543
DEBUG: angle: 90, cos: 0, sin: 1.0000000005
まとめ
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー