CindyScript における座標系の取り扱い
スクリプトによって全体的な外観をすぐに変えてしまう CindyScript の重要な特徴があります。通常、
CindyScript の座標系はシンデレラの図形描画での座標系と同じです。しかし、特殊演算子によって座標系を変えることができます。そのあとは、この修正された画面に対して描画が行われます。座標変換を行うときは注意が必要です。それらをいくつか行うときは、どこで実際の描画を行っているかを決定するのが難しいかも知れません。それでも、変換の演算子を使いやすくするために、CindyScript は
gsave
と
grestore
の演算子を用意しています。それは、 PostScript に似て、現在の描画エンジンをスタックに push/pop するのです。これは、図形の表現情報の他に、現在の座標変換を含みます。そこで、現在の座標系を
gsave()
....
grestore()
で封じ込めるかも知れません。まず、初めに演算子を紹介し、それから例を示します。
注意: 現在の Cinderella 2.6 版では、円の変換は実装されていません。円についてはユークリッド変換(回転、平行移動、鏡像、拡大縮小)だけがサポートされています。アフィン変換と射影変換は今後のリリースとなります。
座標系の平行移動: translate(<list>)
説明: この演算子は [<real>,<real>] 型の <list> を与えて、このベクトルにより描画する座標系を移動します。
座標系の回転: rotate(<real>)
説明: この演算子は、実数
<real>
の角だけ座標系を回転します。角は弧度法です。度数法で与えたい場合は
°
演算子を使います。この演算子は、その数に
pi/180
を掛けます。したがって、
rotate(30°)
とすれば、座標系を 30° 回転します。
拡大・縮小: scale(<real>)
説明: この演算子は
<real>
によって、拡大・縮小を行います。
例: 次の表は、頂点の座標が
[0,0],[0,1],[1,1],[1,0]
である正方形を描く前に、座標変換をしたときの効果を示します。
| | |
|
translate([1,0]); | rotate(30°); | scale(2));
|
次の表は、異なる変換の組み合わせを示します。演算子の順序は、すこし直感に反するかも知れません。座標変換はオブジェクトを描画する前に行われるのです。
|
| |
|
translate([1,0]); rotate(30°); | rotate(30°); translate([-0.5,-0.5]); | rotate(30°); scale(2)); translate([-0.5,-0.5]);
|
再帰的に、あるいは繰り返しておこなう座標変換は驚くような効果をもたらします。次の図は、このコードで描かれました。(
square
は一つの正方形を描くためのリストとします)
repeat(90,
drawall(square);
translate((1,1));
scale(0.92);
rotate(30°);
)
射影基底との関係: setbasis(<basis>)
説明: シンデレラの幾何学部分は、すべての描画が関わる基底を定められるようになっています。これらの基底とは、平行移動、相似、アフィン、射影です。これらの基底は、CindyScript でも使えます。
setbasis()
演算子はシンデレラで使われる基底を定義します。引数はシンデレラで使われる基底のラベルでなければなりません。 setbasis 演算子を適用したあとは、CindyScript の以前の座標変換は使われなくなります。しかし、それらは
gsave
で保存しておき、
grestore
で呼び出すことができます。
例: 次の例で、基底
Bas0
は点A,B,C,Dによって定められる射影基底です。これは、一辺の長さが1である正方形(単位正方形)の4頂点
[0,0],[0,1],[1,1],[1,0]
が、点 A,B,C,D に対応するような基底であることを意味します。コードの1行目が基底変換です。次の3つの行は、単位正方形内の格子を描きます。結果の図はその後に示されています。
setbasis(Bas0);
x=(0..10)/10;
drawall(apply(x,([#,0],[#,1])));
drawall(apply(x,([0,#],[1,#])));
参照: 変換と基底
平行移動基底を設定する: setbasis(<vec1>)
相似基底を設定する: setbasis(<vec1>,<vec2>)
アフィン基底を設定する: setbasis(<vec1>,<vec2>,<vec3>)
射影基底を設定する: setbasis(<vec1>,<vec2>,<vec3>,<vec4>)
説明: CindyScript の内部基底を、既存の点に基づく基底に直接関連付けることも可能です。すると、シンデレラでの基底を変えなくて済みます。これは、基底を定義するための点を与えれば簡単に実行できます。
例: 次のコードはこの性質を示すものです。
sq
の部分は正方形の格子を描く関数の定義です。この格子をいろいろな基底によって描きます。
sq:=(draw([0,0],[1,0]);
draw([0,0.25],[1,0.25]);
draw([0,0.5],[1,0.5]);
draw([0,0.75],[1,0.75]);
draw([0,1],[1,1]);
draw([0,0],[0,1]);
draw([0.25,0],[0.25,1]);
draw([0.5,0],[0.5,1]);
draw([0.75,0],[0.75,1]);
draw([1,0],[1,1]));
setbasis(A);
sq;
setbasis(B,C);
sq;
setbasis(D,E,F);
sq;
setbasis(G,H,L,K);
sq;
表現と基底スタック
座標系変換は全体的な表示に影響します。一時的に座標系変換を行い、そのあと以前の状態に戻したいことはよくあります。CindyScriptにはそのための関数が用意されています。
gsave
演算子は、すべての図形の状態(座標変換、大きさ、色、透明度)についての情報をスタックに保存します。 grestore
はこの情報をスタックから引き出します。したがって、 gsave() ..... grestore()
の間は、他のコードの部分に影響を与えることがありません。
図の状態の一時保存: gsave()
説明: この演算子は、描画に関する現在の情報をすべてスタックに保存します。この情報には座標系、点や線、文字列の大きさや色、透明度などを含みます。
図の状態を戻す: grestore()
説明: この演算子は最後にスタックに保存された図の状態情報を取り出して、その状態に戻します。
グラフの状態を消去する: greset()
説明: この演算子は、スタックに保存されていた座標系やすべての図形に関する色、大きさなどの情報を消去し、初期状態に戻します。
参照: オブジェクトの外観と表現
レイヤー
ある意味で、 Cindyscript で描画される図には3番目の次元があります。各々の図は特定の層(レイヤー)にあります。レイヤーは数で指定されます。図が描かれるとレイヤーは付随する番号の順に塗られます。レイヤーが指定されなければ、Cindyscript で描画される図は背景層すなわち幾何要素のある層で描画されます。初期状態ではCindyscriptのdrawが実行される前にレイヤーは消去されます。しかしながら、自動消去されないレイヤーをマークすることも可能です。
描画レイヤーを設定する: layer(<int>)
説明:この関数はレイヤーを指定したレベルに設定します。初期状態では描画が行われる前にレイヤーは消去されます。
例:次の例は、通常の描画順で4つにオーバーラップして円盤を描きます。
cir(x,y):=(
fillcircle((x,y),3,color->(1,.8,0));
drawcircle((x,y),3,color->(0,0,0),size->3);
);
cir(0,0);
cir(1,1);
cir(2,2);
cir(3,3)
それぞれの円盤を描画する前にレイヤー関数が呼ばれると、それぞれの円盤を逆順に描画することができます。
cir(x,y):=(
fillcircle((x,y),3,color->(1,.8,0));
drawcircle((x,y),3,color->(0,0,0),size->3);
);
layer(6);
cir(0,0);
layer(5);
cir(1,1);
layer(4);
cir(2,2);
layer(3);
cir(3,3)
| |
通常の描画 | 逆順にレイヤーで描画 |
レイヤーを空にする。: clearlayer(<int>)
説明: この関数は、与えられたレイヤーを空にします。
レイヤーからすべての図を消去する: clrscr()
説明: この関数は、スクリーンで実行されたすべての図を消去します。図が本当に消去された状態になっているかどうかを確認することは時々役に立ちます。特に、他のスクリプトスロットから
repaint()
関数が呼ばれているときに必要になるでしょう。
レイヤーの自動消去: autoclearlayer(<int>,<boolean>)
説明: レイヤーのフラグの自動消去はこの関数で行なえます。初期状態で、すべてのレイヤーは画面の再計算の間に自動的に消去されます。この自動消去フラグを
false
にすることでこれを切り替えることができます。
レイヤーが自動消去されなければすべての描画命令が保存されます。シンデレラはビットマップを保存しないであらゆるステップで画面を描き直すので、レイヤーのために多くの描画命令を使うとパフォーマンスに影響します。
例: initialization スロットに次のコードを書くと、消去されないレイヤーを背景あるいは前面に描くことができ、
draw スロットに書くと、何か操作するたびにレイヤーを書き換えます。
autoclearlayer(-4,false);
layer(-4);
repeat(1000,
p = [random(20)-5,random(20)-5];
color([random(),random(),random()]);
fillpolygon(apply([[0,0],[1,0],[1,1],[0,1]],p+#),alpha->.2);
);
layer(0);
このコードをInitializationスロットに描くと、レイヤーー4で背景を描きます。自動消去フラグが false なので、次の図のように、他の作業をする間も表示したままになります。これを
draw スロットに書いて同じ効果を得るためには、乱数によってできる異なる長方形と色をすべて保存しておかなければなりません。
|
A random background |
スクリーン境界の決定: screenbounds()
説明: もし射影平面の有限部分だけ使い、
球面表示 を使わないのであれば、スクリーン境界を求めることに意味があります。この関数は、見える範囲の長方形を定義している4つの同次座標のリストを返します。
この関数はユークリッド表示においてのみ有効です。
画面解像度の決定: screenresolution()
説明: 双曲表示と球面表示と対照的に、ユークリッド表示はいたるところに同じ画面解像度を持ちます。この関数は、原点と
[0,1]
間の画素数を与えます。同じ図に対していくつかのユークリッド表示があるとき、この関数はすべての画面解像度の最大値を返します。
例:点に色をつけてチェッカーボードを作ることができます。次のコードは、現在の拡大率に関わりなくすべての画素に色をつけます。画素単位で色をつけることを勧めているわけではありません。しかし、これが必要なことがあるかもしれません。ほとんどの場合、
colorplot
関数 が使いやすく、より高速です。
upperleft=(screenbounds()_1).xy;
lowerright=(screenbounds()_3).xy;
width in pixels=screenresolution()*(lowerright.x-upperleft.x);
height in pixels=screenresolution()*(upperleft.y-lowerright.y);
repeat(width in pixels/2, x,
start->upperleft.x, stop->lowerright.x,
repeat(height in pixels/2, y,
start->upperleft.y, stop->lowerright.y,
draw((x,y),border->false,size->.5,color->[0,0,0]);
)
)