Effective ATS:
シェルピンスキーの三角形を描く

この記事では、JavaScript (JS) コードと ATS コードを組み合わせた例を紹介します。この実装は私にとって、改良を基本としたプログラミングを主張するための良い機会にもなりました。

アニメーションを作る

次のコードは、典型的なアニメーションの実装です。

//
extern
fun drawAnim(): void
extern
fun drawAnim_init(): void
extern
fun drawAnim_loop(): void
extern
fun drawFrame (): void
//
implement
drawAnim() =
{
  val () = drawAnim_init ()
  val () = drawAnim_loop ()
}
implement
drawAnim_loop() =
{
  val () = drawFrame ()
  val () = sleep (1(*sec*)) // HX: this needs to be fixed
  val () = drawAnim_loop ()
}
//

このコードの内容は次のようなものです: [drawAnim] はアニメーションの実行を実行するのに呼び出されるメイン関数です。この関数はなんらかの初期化を行なう [drawAnim_init] を呼び出し、その後 [drawAnim_loop] を呼び出します。この [drawAnim_loop] は、[drawFrame] を呼び出してフレーム描画し、[sleep] を呼び出してスリープした後、ループします。

けれども、[drawAnim_loop] 中での [sleep] 呼び出しには問題があります。Web ブラウザの中でアニメーションを動作させたいとすると、もしサポートされていれば [sleep] 呼び出しは Web ブラウザ全体を停止させてしまうことを意味します。このような実装は許容できないでしょう。その代わりに、[drawAnim_loop] を次のように実装できます:

//
implement
drawAnim_loop() =
{
  val () = drawFrame ()
  val () = $extfcall (void, "setTimeout", drawAnim_loop, 1000(*ms*))
}
//

本質的に、JS における外部関数 [setTimeout] 呼び出しは、1000 ミリ秒 (つまり 1 秒) 後にその呼び出しをスケジュールするように Web ブラウザに要求します。

上記のアニメーションを具体例を読者に理解してもらうために、次のような JS を使った [drawFrame] の単純な実装を示します:

%{$
//
var
canvas =
document.getElementById
  ("Patsoptaas-Evaluate-canvas");
//
var ctx2d = canvas.getContext( '2d' );
//
var theToggle = 0
//
function
drawFrame()
{
  var w = canvas.width;
  var h = canvas.height;
  if (theToggle) ctx2d.fillStyle = "#ffff00"; // yellow
  if (!theToggle) ctx2d.fillStyle = "#0000ff"; // blue
  theToggle = 1 - theToggle;
  ctx2d.rect(0, 0, w, h);
  ctx2d.fill();
}
//
jQuery(document).ready(function(){drawAnim();});
//
%} // end of [%{$]
//

ここでは JS コードの詳細を解説しません。 このコードは本質的に、文字列 "Patsoptaas-Evaluate-canvas" によって指定されたキャンバスを配置します。 [drawFrame] の実装は、キャンバス全体に青色と黄色を交互に描画します。 これまで示したコードの全体は Sierpinski-3angle-part.dats から入手できます。 このコードは オンライン ですぐにテストできます。

シェルピンスキーの三角形を描く

読者は、シェルピンスキーの三角形の描画アニメーションを オンライン で確認できます。

1つの自然数が与えられたとき、レベル n のシェルピンスキーの三角形は n における帰納的に定義できます。レベル 0 のシェルピンスキーの三角形は1つの正三角形です。正の数 n において、APR, PBQ, PQC の3つの三角形がレベル n-1 のシェルピンスキーの三角形ならば、三角形 ABC はレベル n におけるシェルピンスキーの三角形です。このとき、P, Q, R はそれぞれ、3辺 AB, BC, CA の中点です。

色を表わす抽象型 [color] と、JS において同様の名前で定義された2つの色を表わす [BLUE] と [YELLOW] を導入しましょう。

//
abstype color
//
macdef BLUE = $extval(color, "BLUE")
macdef YELLOW = $extval(color, "YELLOW")
//

次のコードは、与えられた色で塗り潰された三角形 ABC を描画するために呼び出される関数 [drawTriangle] のインターフェイスです:

//
extern
fun
drawTriangle
(
  c: color // color for filling
, Ax: double, Ay: double // x-y-coordinates for A
, Bx: double, By: double // x-y-coordinates for B
, Cx: double, Cy: double // x-y-coordinates for C
) : void = "mac#"
//

与えられば色でシェルピンスキーの三角形を描画する別の関数 [drawSTriangle] を導入しましょう。この関数を [drawTriangle] を使って実装します:

//
extern
fun
drawSTriangle
(
  c: color
, Ax: double, Ay: double // x-y-coordinates for A
, Bx: double, By: double // x-y-coordinates for B
, Cx: double, Cy: double // x-y-coordinates for C
, level: int
) : void = "mac#"
//
implement
drawSTriangle
(
  c, Ax, Ay, Bx, By, Cx, Cy, level
) = (
//
if
level > 0
then let
  val Px = (Ax + Bx) / 2
  and Py = (Ay + By) / 2
  val Qx = (Bx + Cx) / 2
  and Qy = (By + Cy) / 2
  val Rx = (Cx + Ax) / 2
  and Ry = (Cy + Ay) / 2
//
  val () = drawTriangle (c, Px, Py, Qx, Qy, Rx, Ry)
//
  val level1 = level - 1
  val () = drawSTriangle (c, Ax, Ay, Px, Py, Rx, Ry, level1)
  val () = drawSTriangle (c, Px, Py, Bx, By, Qx, Qy, level1)
  val () = drawSTriangle (c, Rx, Ry, Qx, Qy, Cx, Cy, level1)
in
  // nothing
end // end of [then]
else () // end of [else]
//
) (* end of [drawSTriangle] *)
//

関数 [drawFrame] は次のように実装できます:

//
local
//
extern
fun
theLevel_getinc(): int = "mac#"
//
in
//
implement
drawFrame () =
{
//
  val Ax = theAx_get() // x-coordinate of A
  val Ay = theAy_get() // y-coordinate of A
  val Bx = theBx_get() // x-coordinate of B
  val By = theBy_get() // y-coordinate of B
  val Cx = theCx_get() // x-coordinate of C
  val Cy = theCy_get() // y-coordinate of C
//
  val level = theLevel_getinc ()
  val ((*void*)) = drawTriangle (BLUE, Ax, Ay, Bx, By, Cx, Cy)
  val ((*void*)) = drawSTriangle (YELLOW, Ax, Ay, Bx, By, Cx, Cy, level)
//
} (* end of [drawFrame] *)
//
end // end of [local]
//

描画するキャンバスを配置した後、そのキャンバス上にシェルピンスキーの三角形の頂点となる3つの点 A, B, C が選ばれます。JS で実装された [theAx_get] と [theAy_get] を呼び出すことで、A の xy 座標を取得できます。B と C の xy 座標についても同様に取得できます。関数 [theLevel_getinc] を使って、描画されるべきシェルピンスキーの三角形のレベルを得ます。[drawTriangle] の実装を含む JS コード側の詳細については次を見てください:

//
%{$
//
var
canvas =
document.getElementById
  ("Patsoptaas-Evaluate-canvas");
var
ctx2d = canvas.getContext( '2d' );
//
function
theAx_get() { return 0; }
function
theAy_get() { return canvas.height; }
function
theBx_get() { return canvas.width/2; }
function
theBy_get() { return 0; }
function
theCx_get() { return canvas.width; }
function
theCy_get() { return canvas.height; }
//
var
theLevel = 0;
function
theLevel_getinc()
{
  var
  level = theLevel;
  theLevel = (level+1)%7;
  return level;
}
//
function
drawTriangle
(
  color, Ax, Ay, Bx, By, Cx, Cy
)
{
  ctx2d.beginPath();
  ctx2d.moveTo(Ax, Ay);
  ctx2d.lineTo(Bx, By);
  ctx2d.lineTo(Cx, Cy);
  ctx2d.closePath();
  ctx2d.fillStyle = color; ctx2d.fill();
  return;
}
//
jQuery(document).ready(function(){drawAnim();});
//
%} // end of [%{$]
//

このシェルピンスキーの三角形のアニメーション描画実装のコード全体は Sierpinski-3angle-final.dats から入手でき、また オンライン 上で容易にテストできます。 もちろん、Sierpinski-3angle-final.dats をなんらかのC言語コードにコンパイルするために patsopt を使うこともできます。 その後、atscc2js を使って、そのC言語コードを JS コードにコンパイルすることもできます。 このコンパイル手順詳細は Makefile から知ることができます。 生成された JS コードは Sierpinski-3angle.html から入手できます。


この記事は Hongwei Xi によって書かれ、 Japan ATS User Group によって翻訳されています。