Adobe Illustratorで関数曲線


Illustratorで数学の曲線を描く。【2017年6月―全面的に加筆修正】


参考文献:


javascriptで書いたらいいらしい。以下に載っけてるコードをエディタとかメモ帳とかで拡張子「.js」をつけて保存し、イラレの「ファイル>スクリプト>その他のスクリプト」から選択すれば、実行されてパスが表示される。離散化された点を線で結んだだけだから近似曲線になるけど、十分な点数を用意すればまあ問題ないかと。(やっていて分かったけど、どうも900点を超えてくるとエラーを吐くようである。カクカクが気になるのであればパスの単純化で一応なめらかにできる。)






1. 放物線(二次関数)


x1 = -50; //スタート
x2 = 50;  //エンド
step = 0.5;   //描画ステップ
  
anchorpoint = [];
count = 0;
for (x=x1; x<=x2; x+=step)
{
 y = 0.05 * Math.pow(x,2);
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt

n次関数ならMath.pow(x,2) ⇒ Math.pow(x,n)



2. 三角関数


theta1 = 0; //最初の角度(度数法)
theta2 = 360;  //最後の角度(度数法)
step = 1;   //描画ステップ
  
anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 y = 100 * Math.sin(theta_rad);
 anchorpoint[count++] = [theta, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt

ここではとりあえずsin(x)を。cos(x)ならMath.sin() ⇒ Math.cos()に変更。同じノリでtanをやっちゃうと、発散してすごいことになるので注意。描画はされるけどキャンバスからめちゃくちゃはみ出るので極は除いた方がいい。ちなみにイラレの「効果>パスの変形>ジグザグ(なめらか)」で直線をうにゃらせたものは正弦波とは全く異なる。



3. 波数k1のsin波を波数k2のsin波でAM変調させた波


theta1 = 0; //最初の角度(度数法)
theta2 = 360;  //最後の角度(度数法)
step = 1;   //描画ステップ

k1 = 1;  //波数1
k2 = 10; //波数2
  
anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 y = 100 * Math.sin(k1*theta_rad) * Math.sin(k2*theta_rad);
 anchorpoint[count++] = [theta, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt




4. 指数関数的に減衰する振動


theta1 = 0; //最初の角度(度数法)
theta2 = 360;  //最後の角度(度数法)
step = 1;   //描画ステップ

k = 5; //波数

anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 y = 100 * Math.cos(k*theta_rad) * Math.exp(-0.01*theta);
 anchorpoint[count++] = [theta, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt



5. 指数関数的に減衰する歳差運動


theta1 = 0; //最初の角度(度数法)
theta2 = 3600;  //最後の角度(度数法)
step = 5;   //描画ステップ
  
anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 x = 100 * Math.exp(-0.1*theta_rad)*Math.sin(theta_rad);
 y = 100 * Math.exp(-0.1*theta_rad)*Math.cos(theta_rad);
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt



6. アルキメデスの螺旋


theta1 = 0; //最初の角度(度数法)
theta2 = 3600;  //最後の角度(度数法)
step = 5;   //描画ステップ
   
anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 x = Math.cos(theta_rad) * theta_rad;
 y = Math.sin(theta_rad) * theta_rad;
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt

この螺旋は蚊取り線香を描くときに使える。見てると目が回る。

c.f. その他の螺旋の方程式(放物螺旋とか描いてみるとモアレが出て楽しい。)



7. ガウシアン(Gaussian, 正規分布)


x1 = -100; //スタート
x2 = 100;  //エンド
step = 1;   //描画ステップ

anchorpoint = [];
count = 0;
for (x=x1; x<=x2; x+=step)
{
 y = 100 * Math.exp(-(x*x)/(20*20));
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt



8. ローレンチアン(Lorentzian, コーシー分布)


x1 = -100; //スタート
x2 = 100;  //エンド
step = 1;   //描画ステップ

anchorpoint = [];
count = 0;
for (x=x1; x<=x2; x+=step)
{
 y = 10000/(100+x*x);
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt



9. サイクロイド曲線(cycloid)



theta1 = 0; //最初の角度(度数法)
theta2 = 360;  //最後の角度(度数法)
step = 1;   //描画ステップ

anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 x = 50 * (theta_rad - Math.sin(theta_rad));
 y = 50 * (1 - Math.cos(theta_rad));
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt

c.f. 媒介変数表示された有名な曲線7つ
他の有名な媒介変数表示された曲線も、上で関数だけ書き換えれば描ける。



10. フレネル積分(Fresnel integrals)


x = 0.0; //x初期値
dt = 0.01;  //描画ステップ

function func1(t) {
  return Math.cos(t*t);
}

anchorpoint = [];
count = 0;
for (t=0; t<=8; t+=dt)
{
 dx = dt * func1(t);
 x += 100 * dx;
 anchorpoint[count++] = [10*t, x];
}
//document.write(anchorpoint)
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt

sinの方も同じ様に作れる。雑な解説は下のクロソイドのところで。



11. クロソイド曲線(clothoid, フレネル積分, コルニュ螺旋)


x = 0.0; //x初期値
y = 0.0; //y初期値
dt = 0.01;  //描画ステップ

function func1(t) {
  return Math.cos(t*t);
}

function func2(t) {
  return Math.sin(t*t);
}

anchorpoint = [];
count = 0;
for (t=0; t<=8; t+=dt)
{
 dx = dt * func1(t);
 dy = dt * func2(t);
 x += 100 * dx;
 y += 100 * dy;
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = false; //塗りなし
pObj.stroked = true; //線あり
pObj.strokeWidth = 1; //線幅1pt


クロソイド曲線は一般に媒介変数表示で、 \begin{align} x(l) &= \int_{0}^{l}\cos(t^2)dt \nonumber\\ y(l) &= \int_{0}^{l}\sin(t^2)dt \nonumber \end{align} のフレネル積分になっている。なので、ここでは微分方程式 \begin{align} \frac{dx}{dt} &= \cos(t^2) \nonumber\\ \frac{dy}{dt} &= \sin(t^2) \nonumber \end{align} をオイラー法で解いて表示している。
マイナス側も必要だったらfor文のところ、for (t=-4.5; t<=4.5; t+=dt)で。



12. おまけ。 MONTBLANCのロゴみたいなの(中の星部分)


媒介変数表示した円の半径をcosで変調させてる。(ここでは狙って6周期)

theta1 = 0; //最初の角度(度数法)
theta2 = 360;  //最後の角度(度数法)
step = 1;   //描画ステップ
  
anchorpoint = [];
count = 0;
for (theta=theta1; theta<=theta2; theta+=step)
{
 theta_rad = theta * Math.PI / 180;
 R = 50 + 8 * Math.cos(6*theta_rad);
 x = R * Math.sin(theta_rad);
 y = R * Math.cos(theta_rad);
 anchorpoint[count++] = [x, y];
}
docObj = activeDocument;
pObj = docObj.pathItems.add();
pObj.setEntirePath(anchorpoint);
pObj.filled = true; //塗りあり
pObj.stroked = false; //線なし
pObj.strokeWidth = 1; //線幅1pt

こんな感じでデザインされたのかなと思ってやってみたけど、比べると若干ずれがあった。cosの係数次第でもう少し寄せられるかもしれない。




余談になるけど、ガウシアン、ローレンチアンは拡大縮小で任意のガウシアン、ローレンチアンに変形できる。よって、どんなパラメータを持つガウシアンであろうと(つまり分散とかが何であっても)、上記コードを実行して出てきたガウシアンのパスを縦横に適当に拡大縮小すれば再現することができる。(言うまでもなく、放物線や三角関数もそうである。)

例えばガウシアンの場合、もともとの関数形は \begin{align} y = a\exp\left\{-\frac{(x-b)^2}{2c^2}\right\} \end{align} $b$ は $x$ 軸方向の平行移動なので無視するとして、パラメータ $a, c$ がそれぞれ $a', c'$ 倍されても \begin{align} y &= a'a\exp\left\{-\frac{x^2}{2(c'c)^2}\right\}\\ \frac{y}{a'} &= a\exp\left\{-\frac{(x/c')^2}{2c^2}\right\}\\ \end{align} ここで、$y' = y/a'$, $x' = x/c'$ と変数変換すれば、 \begin{align} y' = a\exp\left\{-\frac{x'^2}{2c^2}\right\} \end{align} となり、これは元の関数形($b$ を無視した場合)と同じである。ということで結局、$a$ を $a'$ 倍、$c$ を $c'$ 倍したところで、それは結局、元のガウシアンを縦方向に $a'$ 倍、横方向に $c'$ 倍したものにすぎない。




あと諸々の複雑な関数(ガンマ関数とかベッセル関数とか)になると、グラフソフト(gnuplotとかMatplotlibとか)で描いてepsで吐き出したものを読み込んだ方が早い。というか、jsでどう書いたらいいのかわからない。


Adobe Illustratorで関数曲線 Adobe Illustratorで関数曲線 Reviewed by mug on 10/30/2016 Rating: 5

0 件のコメント:

Powered by Blogger.