chess_board.jpg


チェスボードを描画する

 今回は、チェスボードを描画してみます。unicodeの拡張文字(絵文字)を使っているので、ちゃんと駒の絵柄が見えていると良いのですが、下図が駒の並べ方です。

               
               
               
               

 チェスボードは、左下(自分から見て左手前)が黒いマスになるように置き、各駒を上図の様に並べます。
 白い駒のクイーンは白いマスに、黒い駒のクイーンは黒いマスに置くので、クイーンとキングは、盤面を挟んで正面に向き合う形になります。
 その他、上図下側の白い駒で説明しますと、一番下(手前)の段に両端から、ルーク、ナイト、ビショップの順で、中央のクイーンとキングを挟みます。その上の段には、8個のポーンが並びます。これが初期配置。

駒をデザインする

 各々の駒をデザインして行きます。ガラスみたいな透明な属性に設定してみたいので、ポリゴンではなく、二次曲面を使ったソリッドモデルで作成したいと思います。駒には相対的なサイズがあるので、チェスの駒の写真などを参考にして、底面の半径からの相対サイズを拾いながら作業します。データとしては、先頭で底面の半径BRを定義しておき、高さなどのサイズは1.23*BRなどの様に相対的に指定します。

ポーンとルーク

 まずはポーン。

pawn_rook.jpg

 図中、矢印の部分には「タワーの様な構造体を作る」でご紹介した、一葉双曲面(手続きhyperboloid)を使用しています。後は、データをご覧いただくと分かりますが、楕円面を平面でカットした部分と、球面で構成しています。

  procedure pawn l,m,n
  {
    // ポーンの駒
    // 底面中心座標(l,m,n)、z軸正方向が上
    para l,m,n;
    // 底から順に積み上げていく
    boundary cuboid 0,0,1.34*BR, BR,BR,1.34*BR
    {
      or
      {
        elldsk 0,0,0.00*BR, 0.84*BR,0.16*BR;
        elldsk 0,0,0.16*BR, 0.84*BR,0.32*BR;
        and
        {
          hyperboloid 0,0,1.52*BR, 0,0,1, 0.26*BR,1.04*BR,0.70*BR;
          plane 0,0,1.52*BR, 0,0,1;
          plane 0,0,0.48*BR, 0,0,-1;
        }
        elldsk 0,0,1.52*BR, 0.34*BR,0.12*BR;
        elldsk 0,0,1.64*BR, 0.44*BR,0.24*BR;
        sphere 0,0,2.28*BR, 0.40*BR;
      }
    }
  }


 データは、下の面から順々に形状を作りながら、積み上げて行っています。こうしないと、作っている自分も途中で訳が分からなくなってくるので。

 ルークもほぼ同じです。ただ、ポーンの駒は、他の駒より少し小さめにできているようなので、サイズ感が少し違っています。後は、トップのデザインをルークっぽく(城?砦?)作成して完成。トップの部分は、円錐面と楕円面で作成しました。

ナイトとビショップ

 ビショップの方は、比較的単純なデザインが多かったので、こんな感じ(↓)で完成しました。

knight_bishop.jpg

 問題はナイト。この駒だけは、デザインが複雑。馬の頭の部分を作り込まないと、どうしてもナイトっぽくなりません。しかし、馬の頭を二次曲面で作るとなると、結構大変です。二次曲面では、複雑な形状を定義するのは困難ですから。
 まずは、下図の様に、ラフな図柄を作成してみました。

knight.png

 「これが馬の頭か〜?」と、我ながら思ってしまいますが……。二次曲面で作ることをイメージして、最大限デフォルメしてみた結果がこれ。頭と首になる台形は、3Dでは円錐面(円錐台)を使います。全体を包み込むように大きな円を描きましたが、これで円錐面の上下を切り取ったり、首の後ろの部分に関しては、そのまま円弧を残して、たてがみの様にしようかと考えました。

 この画像をOlive+に張り付け、「編集」メニューから、「簡易編集」を選ぶと、以下のような簡易編集モードになります。

coord.png

 十字カーソルが表示されるコマンド(「スポイト・(点)」など)に設定して、上図の様に、座標を拾いながら、二次曲面を定義して行きます。
 この場合の難点は、y軸の座標値が反転して完成することです。グラフィック画面上では、左上隅が原点(0, 0)で、そこから下にずれると、y座標が大きくなります。つまり、y軸の正方向が下を向いていて、通常イメージする方向と逆転しています。座標を拾った時に、負数に読み替えるなど、いくつか方法は考えられますが、ここでは、気にせず、そのままの座標値を拾ってデータを作成します。完成した後で上下を反転します。

 上の様なグラフィックから座標を拾った時点で、サイズ感も全くずれてしまっているので、他の駒とサイズを合わせるためには、一次変換の中の相似変換を使って、調整が必要です。

  sim 0.16, -0.16, 0.16;

 他の駒のサイズと比較しながら、0.16倍と言う比率を決定しました。注目は、真ん中のパラメータだけがマイナスであること。sim命令は、x/y/z軸方向の拡大・縮小率(>1で拡大、<1で縮小。よって、0.16は縮小)を指定する相似変換命令です。真ん中のパラメータは、y軸方向の拡大・縮小率に相当します。ここが負の数になったことで、y軸方向の正と負が逆転します。つまり、先ほど、y軸の方向が逆転したグラフィック画像から拾った座標値が、ここで反転されて、正しい方向を向くわけです。

 たてがみの部分は当初、円弧をそのまま使おうと思ったのですが、駒として組み上げたところ、台座の部分から、はみ出してしまったため、台座の大きさでカットしました。はみ出したままにしておくと、他の駒と並べた時に重なったり、バランスが悪かったりすると思ったので……。
 デフォルメした形状が、少しでも馬に見える様、目もつけてみました。これで完成。

クイーンとキング

 他の駒と比べ、一段と大きくなっています。いくつかの駒の写真を参考に、トップの部分のデザインを作成しました。

queen_king.jpg

 トップの部分は、一葉双曲面と楕円面の組み合わせです。クイーンは王冠?キングの方はよくわかりませんでしたが……、クロス(十字架)の様な、違うような……?

マクロ配列を使って配置する

 Xi-TRACEのデータ記述では、「#(<数値>)」と言う書式で、数値を文字列に変換して埋め込むことができます。ただし、この場合の<数値>には、「0または正の整数」という条件がついています。これは、この書式を使って配列変数モドキを使うという野望があったからです(1^^;)。

配列変数のおさらい

 野望の解説の前に、<数値>に関する補足を。<数値>が負の数だった場合、「#(<数値>)」はエラーになります。もう一つの条件として、<数値>は「整数」を期待していますが、実数(浮動小数点数)値が渡されたとしても、エラーにはならず、<数値>の少数第一位を四捨五入した整数値が指定されたものと見なします。また、<数値>の最大値を32767(15ビット)に制限しているため、<数値>が32767を超えた場合もエラーとなります。

 「#(<数値>)」の書式は、ダブルクオテーションマーク("〜")で括られた文字列の内部以外であれば、どこにでも置くことができる仕様になっています。それが、例えば、変数名の中であってもOKなわけです。つまり、以下の様な書式が全て合法となります。

  var idx=10;
  var array#(33);
  var array#(idx);
  var array#(idx*3 + 1);


 この書式が見つかると、Xi-TRACEは、<数値>の部分を計算して、その結果を「0または正の整数」に変換、そのまま、「#(<数値>)」の部分に埋め込んで、解釈を続けようとします。上で示した例は、次のように書いたのと同等に扱われるわけです。

  var idx=10;
  var array33;
  var array10;
  var array31;


 実際には、「配列変数」として扱っているわけではなく、数値を含む変数名の定義と使用をサポートしているだけ、なのです。

マクロも配列

 さて、ここまでは、以前のデータ解説でも説明したことがありました。今回使うのは、マクロの配列です。マクロは、「$(<マクロ名>)」という書式で、マクロに定義した文字列を、その書式の場所に埋め込むための機能です。マクロはmacro命令で宣言した後、mlet命令などを使って定義内容を変更できるため、文字列変数の様な使い方が可能なのですが、そこはマクロなので、文字列変数以上の使い方が可能です。

  macro str="sphere";   マクロstrを定義して、"sphere"を設定する
  $(str) 0,0,0, 30; → sphere 0,0,0, 30; $(str)が"sphere"に置き換わって、球面を定義するsphere命令と解釈される。
  mlet str="cuboid";   マクロstrに"cuboid"を再設定する。
  $(str) 0,0,0, 30; → cuboid 0,0,0, 30; $(str)が"cuboid"に置き換わって、直方体を定義するcuboid命令と解釈される。

 上例で、マクロ名strの部分に、「#(<数値>)」の書式をくっつけて、マクロも配列モドキとして使うことができます。

  macro str0="sphere";
  macro str1="cuboid";
  var idx=1;
  $(str#(idx)) 0,0,0, 30;


 上のリストでは、idx=1ですので、「str#(idx)」が「str1」と解釈され、さらに、「$(str1)」が「cuboid」と解釈されるので、直方体を定義するcuboid命令が実行されます。もし、idx=0とした場合は、同様なメカニズムでsphere命令が実行されることになります。

二次元配列?

 チェスボードの盤面は、8×8マスですので、どのマスにどの駒を配置するのか?を指定するためには、本来であれば二次元配列を使うのが便利です。Xi-TRACEでも、例えばアンダスコア(_)などの適切な文字を使って、添え字を分離すれば、二次元配列モドキを使用することも可能です。

  var ix=4;
  var iy=2;
  macro str#(ix)_#(iy)="pawn";


 チェスボードの駒の配置に使うためには、この調子で、8×8=64個のマクロに駒の情報を登録しなければなりませんが、さすがにつらそうです。一方、一次元配列であれば、配列定義と定数の割り当てをサポートできる、marray命令が使えます。

  marray piece:0=
    rook,  knight,  bishop,  queen,  king,  bishop,  knight,  rook,
    pawn,  pawn,  pawn,  pawn,  pawn,  pawn,  pawn,  pawn,
    empty,  empty,  empty,  empty,  empty,  empty,  empty,  empty,
    empty,  empty,  empty,  empty,  empty,  empty,  empty,  empty,
    empty,  empty,  empty,  empty,  empty,  empty,  empty,  empty,
    empty,  empty,  empty,  empty,  empty,  empty,  empty,  empty,
    pawn,  pawn,  pawn,  pawn,  pawn,  pawn,  pawn,  pawn,
    rook,  kni180,  bishop,  queen,  king,  bishop,  kni180,  rook;

 上のリストでは、「piece」に数字がついた、「piece0、piece1、……」というマクロを定義しながら、後ろに続く文字列を設定して行きます。文字列は、8×8=64個並んでいますので、「piece0〜piece63」までのマクロが定義されることになります。
 本来二次元配列であるチェスボードの横座標をix=0〜7、縦座標をiy=0〜7とすると、上で定義した一次元配列マクロにアクセスするためには、以下のようにします。

  $(piece#(8*iy + ix))

 マクロpieceに設定した、「pawn」、「king」などの文字列は、それぞれの駒を定義するための手続きの名前になっています。先ほど、手続きpawn(ポーンの駒を定義する)のリストをご覧に入れました。後にリンクする完成データをご覧いただくと、その他の駒も手続きで定義してあります。

 盤面に駒を並べるためには、以下のようにします。

  var idx;
  var ix;
  var iy;
  iy=0;
  while (iy<8)
  {
    ix=0;
    while (ix<8)
    {
      idx=8*iy + ix;
      attr $(piatr#(idx));
      $(piece#(idx)) 2*CS*ix,2*CS*iy,0;
      ix=ix + 1;
    }
    iy=iy + 1;
  }


 駒には、白と黒の駒が存在しますので、駒の形状を指定するマクロpieceとは別に、駒の色(属性)を指定するマクロpiatrも、定義してあります。
 上のリストでは以下の様に、直接、マクロを手続き名に展開して実行しています。

  $(piece#(idx)) 2*CS*ix,2*CS*iy,0;

 先ほどのmarray命令に戻ってみると、駒の名前を連想できる、pawn、rook、等の他に、empty、kni180などの少し変わった手続き名が設定されている個所があります。

  procedure empty l,m,n
  {
    // 何も置かない(空の)手続き
  }
  procedure kni180 l,m,n
  {
    // ナイトの駒を180°回転して配置する
    para l,m,n;
    rotz 180;
    knight 0,0,0;
  }


 emptyは何も駒を置かない場所と言う意味で、空の手続きを定義してあります。ナイトの駒だけは、馬の頭が向く方向があるため、向かい合う白と黒の駒で180°回転させる必要があり、kni180という手続きを定義しました。
 $(piece#(idx))の書式で、直接手続きを呼び出しているため、もし、マクロに設定した手続き名が存在しないと、エラーになってしまいます。この様な使い方では、マクロに設定する文字列を間違えないように注意する必要があります。


完成動画

 完成したデータと動画です。

Xi-TRACEデータ chess_board.xitr

 Xi-TRACE R13の追加機能を使用していますので、ダウンロードコーナから最新版をDLしてください。



 キングから順に、駒を並べていく様子を動画にしました。この動画もまた、後ろの方のフレームに行けば行くほど、描画すべき物体数が増えるため、計算に時間がかかる様になります。計算時間の見積もり時にはご注意ください。

 このページの最初の方で、「ガラスみたいな透明な属性」にしたいと書きました。透明は透明ですが、黒駒(赤い)の屈折率はルビィに相当する1.77、白駒(透明)の屈折率は水晶に相当する1.54としてあります。
 右京さんの机に置いてあるセットとは少し違いますが、屈折率に相当する物質で作られているとすると、こちもかなり高価なセットになるかと。


home

download

gallery