小さなプログラム例
この節では、CindyScript を使った小さなプログラムの例をあげます。それぞれの例は、CindyScript でいろいろなものの取り扱いかたを示します。
重心
3つの点の重心
D を設定します。状況により色が変わるようにします。この例では、点Dのx座標の正負により色が変わります。
//in draw
D.xy=(A+B+C)/3;
D.color=if(D.x>0,
(1,1,0),
(0,0,1)
)
// in draw とあるのは、Drawスロットに書く、という意味です。
このコードは順番に実行されます。点Dの位置を計算して
D.xy
に代入することでDの位置が決まります。点Dの色はDの位置によって if 文で設定されます。
選択と集積
x軸の上下で点を分類してネットワーク状に線で結びます。適当に点をいくつかとっておいて次のスクリプトをdrawスロットに書いて実行します。
//in draw
pts=allpoints();
above=select(pts,p,p.y>0);
below=pts--above;
segs=pairs(above);
drawall(segs,color->(0.6,0,0));
segs=pairs(below);
drawall(segs,color->(0,0.6,0))
x軸より上の点を
select
演算子により選びだします。それを全体から引くとx軸より下の点が得られます。
pairs
演算子で点を2つずつ結びます。
凸型外殻の描画
すべての点を内部に含む凸型の外殻を描きます。数の不安定性に対処する
~>
演算子の働きを見てください。
//in draw
pts=allpoints();
leftof(A,B):=select(pts,p,area(A,B,p)~>0);
rightof(A,B):=select(pts,p,area(A,B,p)~<0);
isedge(A,B):=(leftof(A,B)==[]%rightof(A,B)==[]);
segments=pairs(pts);
hull=select(segments,seg,isedge(seg_1,seg_2));
drawall(hull);
凸型外殻の計算は非常にうまい幾何学的原則によってなされます。凸型外殻の中にすべての点が入ります。
leftof
と
rightof
というユーザー定義関数は線分ABのどちら側にあるかで点を分類します。さらにこれを用いて
isedge
を定義し2つの点のペアが凸型外殻の端となるかどうかを調べます。ここで、あいまいな比較
~<0
の使い方に着目してください。最後に、すべての端の線を描画します。
アナログ時計
時針、分針、秒針をもったアナログ時計を作ります。まず、原点を始点とする3つの矢線を描いておきます。その終点をB,C,Dとします。それから次のスクリプトを Timer Tick スロットに書いて実行します。
//in timerstep
t=time();
p(x):=(sin(2*pi*x),cos(2*pi*x));
B.xy=p(t_3/60)*4;
C.xy=p(t_2/60)*5;
D.xy=p((t_1*60+t_2)/(12*60))*3.5;
apply(1..12,i,draw(p(i/12)*5));
apply(1..60,i,draw(p(i/60)*5,size->1));
drawtext((3,5),t);
点はスクリプトで描きます。時計盤の円は「円を描く」モードで描いておきます。この時計は、
time
関数がコンピュータのシステム時間にアクセスするのを利用しています。tがリストであることを利用して秒針は1秒ごとに動くようになっています。
最短距離にある点
点Aに最も近い点を緑の円で囲んで示します。
//in draw
pts=allpoints();
s=sort(pts,|#-A|);
p=s_2;
draw(p,size->20);
上のコードではA自身が最短になるために、距離で並べ替えた後、2番目に近いものをとっています。もうひとつの方法は、あらかじめ点Aを除外しておくものです。次のコードをご覧ください。
//in draw
pts=allpoints()--[A];
s=sort(pts,|#-A|);
p=s_1;
draw(p,size->20);
簡単な模様
点を動かして簡単な模様を描きます。線の色は点の色がそのまま使われます。まず、点をいくつかとり、色を変えておきます。(下図では3つ)//in draw 以下のスクリプトを Drawスロットに、// in init 以下を Initializationスロットに書いて実行します。
//in draw
forall(pts,p,
p:"trace"=p:"trace" ++ [p.xy]
);
tr0=[[1,0],[0,1]];
tr1=[[-1,0],[0,1]];
tr2=[[-1,0],[0,-1]];
tr3=[[1,0],[0,-1]];
trs=[tr0,tr1,tr2,tr3];
forall(trs,t,
forall(pts,p,
connect((p:"trace")*t,color->p.color,size->2);
);
);
//in init
pts=allpoints();
forall(pts,p,p:"trace"=[]);
変数
"trace"
に、自分自身の軌跡がどんどん追加されていくことを見てください。
回帰直線
すべての点に対する回帰直線を描きます。最小2乗法により正方形を描いていきます。
//in draw
//Least-square line
pts=allpoints();
m=apply(pts,(1,#.x));
y=apply(pts,#.y);
ma=transpose(m)*m;
mb=transpose(m)*y;
mainv=inverse(ma);
v=mainv*mb;
f(x):=v_2*x+v_1;
plot(f(x));
//Draw the squares
sq(x,y1,y2):=(
d=y2-y1;
p=((x,y1),(x,y2),(x+d,y2),(x+d,y1),(x,y1));
drawpoly(p,color->(1,0.5,0.5),alpha->0.4);
connect(p,color->(.8,0,0));
);
forall(pts,sq(#.x,#.y,(f(#.x))));
このコードでは、回帰直線を見つけるために高度な行列計算をしています。
ひまわり
チュートリアルのひまわりよりちょっといい方法です。
//in draw
n=round(100*|B,E|);
d=0.01*(|D,F|/|D,C|-.5);
ang=137.508°+d;
repeat(n,i,
w=ang*i;
r=sqrt(i)*.2;
p=(sin(w),cos(w))*r;
draw(p,color->(1,0.8,0));
);
drawtext((-5,-6),"n="+n);
drawtext((0,-6),"w="+180*ang/pi+"?");
このコードは、ヒマワリが成長するときに現れる特定の角度
ang=137.508deg.
を用いています。
関数プロット
関数式を自由に入力してそのグラフを描きます。
//in draw
//Parameter sliders
a=K.x;
b=L.x;
c=M.x;
d=N.x;
drawtext(K+(0.2,0.2),"a="+a);
drawtext(L+(0.2,0.2),"b="+b);
drawtext(M+(0.2,0.2),"c="+c);
drawtext(N+(0.2,0.2),"d="+d);
//Parse and plot
f(x):=parse(Text0.val);
plot(f(x),color->red(0.6),size->2);
関数式は、シンデレラの文字入力ボタンで "Text0'' のところに書きます。これを直接解釈し、上のスライダできまる係数を使ってグラフを描きます。
色をまぜる
3つの点A,B,Cを赤、緑、青の電球としましょう。この3つの色の混ざり具合をシミュレートします。
//in draw
colorplot(
(1-|#,A|/4,
1-|#,B|/4,
1-|#,C|/4),
(-5,-5),(5,5),pxlres->3
)
このカラーミキサーは実に単純です。
colorplot
関数が、平面上の各点における色の混ざり具合を計算します。
ジュリア集合
フラクタルとして有名なジュリア集合です。複素数平面上の点Cが表す複素数によって計算されます。
//in draw
g(z,c):=z^2+c;
julia(z):=(
iter=0;
while(iter<100 & |z|<2,
z=g(z,complex(C.xy));
iter=iter+1;
);
1-iter/100;
);
colorplot(julia(complex(#))
,A,B,startres->16,pxlres->1);
ここでも
colorplot
関数が大きな役割を果たします。上のコードで定義されている
julia
関数は、各点の色を反復的に計算します。
startres
と
pxlres
修飾子を使うことでより細かい図を描くことができます。
群れのシミュレーション
魚の群れをシミュレートします。それぞれの魚は隣の魚と同じ方向でしかもその場所に泳ごうとします。すべての魚は点Uによって与えられる障害を避けなければなりません。
//in draw
ms=allmasses()--[U];
lim=0.5;
apply(ms,m,
near=select(ms,p,|p-m|<4);
avg=sum(near,m,m.v)/length(near);
m.v=m.v+.2*(avg);
if(|m.v|>lim,m.v=lim*m.v/|m.v|)
//Draw connections
apply(near,draw(#,m,color->(0,0,0),alpha->0.2));
);
ここで、魚と障害物は正電荷を帯びた粒子としてモデル化されます。これにより、魚は互いに押しのけあいます。スクリプトはそれぞれの点の速度を設定します。