マリオネット・スクリプト解説講座

第39回「VectorScriptで図形をいくつも描いてみよう!」

FundamentalsArchitectLandmarkSpotlightDesigner2023

今回はプログラムの繰り返し処理を利用して図形の複製に挑戦します。合わせて変数を用いた配置座標の操作についても解説します。(執筆 A&A川崎)

前回(第38回)2Dの図形をスクリプトで描きました。Vectorworksのベースになる2D図形を描いたのですが、図形を1つだけ描く場合は、当然手で描いた方が早いでしょう。そこで今回は、プログラムの醍醐味でもある繰り返しを利用して、図形をいくつも描くことに挑戦してみましょう。

四角を描くのが最も説明しやすいので、Rectを使って複数の図形を描いてみましょう。

PROCEDURE shikaku5; 
BEGIN
    Rect( 0.0, 50.0, 50.0, 0.0 );
    Rect( 0.0, 50.0, 50.0, 0.0 );
    Rect( 0.0, 50.0, 50.0, 0.0 );
    Rect( 0.0, 50.0, 50.0, 0.0 );
    Rect( 0.0, 50.0, 50.0, 0.0 );
END;
Run( shikaku5 );

四角形は5個描かれます。スクリプトは上から下へ順に処理されていきます。では1万個描くとしたらどうでしょう。1万行書くのはスマートではありません。ここで、スクリプトの繰り返し構文を使います。

繰り返しの構文はいくつかありますが、繰り返す回数が決まっている場合、FOR文を使います。

FOR カウンタ := いくつから TO いくつまで DO
BEGIN
    繰り返す内容
END;

FOR、:=、TO、DOは固定です。この1文の後にBEGIN、END;も固定で挟まれた部分を繰り返します。カウンタのところには変数を用意します。スクリプトに数を数えさせるためと覚えてください。

FOR ii := 1 TO 5 DO

このように書くとカウンタとしての変数iiに最初に1を入れ、BEGIN、END;を1回実行するたびにiiは自動的に1ずつ加算されていき、iiの中身が5になるまで繰り返す。すなわちBEGIN、END;の間に書かれた命令を5回繰り替えすということになります。

ちなみにiiは変数ではありますが、このFOR文が動いている間はスクリプトでユーザ自身が変更を加えてはいけません。本来変数は値を好きに入れられる入れ物ですが、FOR文を繰り返している間に変更を加えると正しい動きができなくなるからです。

さて、それでは1万個四角形を描くには…

PROCEDURE shikaku10000; 
VAR
    ii : INTEGER;
BEGIN
    FOR ii := 1 TO 10000 DO
    BEGIN
        Rect( 0.0, 50.0, 50.0, 0.0 );
    END;
END;
Run( shikaku10000 );

ここで追加の説明ですが、VectorScriptはPythonと違って、変数は予め型の宣言しておかなければなりません。VARというところにiiという変数名で整数(INTEGER)の箱を用意すると覚えてください。

実行すると当然同じ位置に1万個の四角形ができます。これで繰り返し構文の意味は理解できたと思いますので、このスクリプトを変数化することでスクリプトの幅を広げていきたいと思います。

まず、四角形を作成するRectについてもう少し詳しく説明します。

Rect( 0.0, 50.0, 50.0, 0.0 );

こちらを実行すると、左上が(0,50)、右下(50,0)の四角形が描かれますが、4つの引数の前2つが左上、後ろ2つが右下ということがわかります。さらに言えば実座標ということになります。

すなわち、

Rect( 10.0, 50.0, 60.0, 0.0 );

とすれば、右(x方向)に10.0だけずれた位置に四角形が描かれるんだなと想像がつくと思います。このような引数を動的に与えられたら、1万個の四角形それぞれを異なる座標に配置できるスクリプトに発展していきます。

動的にというのは、スクリプトの実行中に数値を変化させることを意味します。ここでいうと手続きRectへの引数を数値を決めうちするのではなく、Rectの処理を繰り返すごとに違う値に変化させられるようにします。

ではどのようにしたらよいか、ここで追加の変数を用意します。

PROCEDURE shikaku5; 
VAR
    ii : INTEGER;
    p1x, p1y, p2x, p2y : REAL;
BEGIN
    p1x := 0.0; 
    p1y := 50.0;
    p2x := 50.0;
    p2y := 0.0; 
    FOR ii := 1 TO 5 DO
    BEGIN
        Rect( p1x, p1y, p2x, p2y );
    END;
END;
Run( shikaku5 );

p1x, p1y, p2x, p2yという変数名で実数(REAL)の箱を4つ用意します。変数は、

p1x := 0.0;

のようにすることで、FOR文のiiのようにデータを格納することができます。FOR文に入る前に、

p1x := 0.0;
p1y := 50.0;
p2x := 50.0;
p2y := 0.0;

と変数に数値を入れることを一般的に初期化といい、上記の初期化の直後では、

Rect( p1x, p1y, p2x, p2y );

Rect( 0.0, 50.0, 50.0, 0.0 );

この2つのRectは同じ意味になり同じ位置に5個四角形が描かれます。ここから四角形を右に10ずつ動かすにはどうしたら良いでしょう?

PROCEDURE shikaku5; 
VAR
    ii : INTEGER;
    p1x, p1y, p2x, p2y : REAL;
BEGIN
    p1x := 0.0; 
    p1y := 50.0;
    p2x := 50.0;
    p2y := 0.0; 
    FOR ii := 1 TO 5 DO
    BEGIN
        Rect( p1x, p1y, p2x, p2y );
        p1x := p1x + 10.0;
        p2x := p2x + 10.0;
    END;
END;
Run( shikaku5 );

Rect( 10.0, 50.0, 60.0, 0.0 );

とすれば10.0、右にずれた四角形になるということは、

Rect( 0.0+10.0, 50.0, 50.0+10.0, 0.0 );

ということになりますね。とはいえ、

Rect( p1x+10.0, p1y, p2x+10.0, p2y );

としてしまっては、

Rect( 10.0, 50.0, 60.0, 0.0 );

の四角形が5つ描かれてしまいます。そうならないために、1つ四角形を描いた後に次の描画のために引数を変化させるため、

p1x := p1x + 10.0;
p2x := p2x + 10.0;

とします。これはp1xの箱に、今の数値p1xと10.0を足して、新しくp1xとするという意味で、簡単に解釈すれば、p1xとp2xを繰り返すたびにそれぞれ10.0足すということをしています。

p1xの内容は、0.0→10.0→20.0→30.0→40.0と繰り返すたびに変化していきます。

p2xの内容は、50.0→60.0→70.0→80.0→90.0と繰り返すたびに変化していきます。

さてここで四角形を描いた時のオブジェクト情報パレットでのパラメータを思い出して欲しいのですが、x方向とy方向の大きさを幅(w)、高さ(h)というパラメータで表現していますね。この部分をスクリプトに反映してみましょう。

PROCEDURE shikaku5;
CONST
    PtheWidth = 50.0;
    PtheHeight = 50.0;
    PxSu = 5;
    PxShift = 10.0;
VAR
    ii : INTEGER;
    p1x, p1y : REAL;
BEGIN
    p1x := 0.0; 
    p1y := 0.0;
    FOR ii := 1 TO PxSu DO
    BEGIN
        Rect( p1x, p1y+PtheHeight, p1x+PtheWidth, p1y);
        p1x := p1x + PtheWidth + PxShift;
    END;
END;
Run( shikaku5 );

CONSTとは定数宣言といい、

PtheWidth = 50.0;
PtheHeight = 50.0;

はPtheWidth、PtheHeightという箱に予め50.0を入れておくを意味します。定数として使用した箱は中身を入れ替えることができません。ですので固定した値を使用したい場合に定数を使用します。

ここでは四角形の幅と高さを50.0に固定しています。練習のため定数を使用して固定値としていますが、四角形の幅や高さを可変としたい場合など変数として宣言しても構いません。

それでは定数と変数は何が違うかというと、定数は実行中に変化させることができません、定数ですので。

Rect( p1x, p1y+PtheHeight, p1x+PtheWidth, p1y );

ここで、左上の座標に幅と高さを加えることで、右下の座標が算出できるため、p2x, p2yは必要なくなります。また、ずらしたときに四角形が重ならないようにするには、

p1x := p1x + PtheWidth + PxShift;

とします。ここでは現在の繰り返しの四角形の左端の座標から、次の繰り返しの時の四角形の左端の座標を計算していますので、ずらす距離は「四角形の幅+四角形の間隔」となり、実際の座標は

次の左端の座標 plx = 現在の左端の座標 plx + 四角形の幅 PtheWidth + 四角形の間隔 PxShift

となるわけです。

ついでになりますが、x方向への数(繰り返し回数)もPxSu = 5;と定数化しています。合わせてFOR ii := 1 TO PxSu DOも定数に置き換えておきましょう。ここまで定数化する理由は最後にわかります。

さて、ここまでで、5個の四角形がx方向に10.0間隔を置いて描かれるようになりました。こうなってくるとy方向はどうなのってなりますよね、ここまでの流れが理解できていれば、想像は簡単にできますね。

x方向に描いた塊を、y方向に同じように10.0間隔を空けて5回繰り返せばいいんじゃないと。

PROCEDURE shikaku55;
CONST
    PtheWidth = 50.0;
    PtheHeight = 50.0;
    PxSu = 5;
    PySu = 5;
    PxShift = 10.0;
    PyShift = 10.0;
VAR
    ii, jj : INTEGER;
    p1x, p1y : REAL;
BEGIN
    p1y :=0.0;
    FOR jj := 1 TO PySu DO
    BEGIN
        p1x := 0.0; 
        FOR ii := 1 TO PxSu DO
        BEGIN
            Rect( p1x, p1y+PtheHeight, p1x+PtheWidth, p1y);
            p1x := p1x + PtheWidth + PxShift;
        END;
        p1y := p1y + PtheHeight + PyShift;
    END;
END;
Run( shikaku55 );

答えは、FORを2重にすれば解決です。x方向に5個描いては、元に戻ってy方向にシフトしてを5回繰り返すことで、予想通りの5×5の四角形が描かれます。

y方向用の定数PyShift = 10.0;、PySu = 5;を追加し、y方向用のカウンタjjを宣言します。

FOR jj := 1 TO PySu DO
BEGIN
    〜
END;

で囲むことで、y方向の繰り返しの数だけ

FOR ii := 1 TO PxSu DO
BEGIN
    〜
END;

このx方向の処理を繰り返すことになります。

この時、p1x := 0.0;をjjの内側のループで行わないと、四角形の2段目(2ループ目以降)がx方向にずれっぱなしになってしまうため、jjのFOR文が繰り返されるたびにp1x := 0.0;は0.0で初期化します。

また、5個x方向に描いた後、

p1y := p1y + PtheHeight + PyShift;

とすることで、x方向に幅とx方向の隙間分ずれていくのと同様にy方向に高さ分とy方向の隙間分ずれていくようにします。

さてここで、それぞれの変数を定数化した恩恵を味わってみましょう。

CONST
    PtheWidth = 100.0;{四角形の幅}
    PtheHeight = 50.0;{四角形の高さ}
    PxSu = 10;{x方向の個数}
    PySu = 5;{y方向の個数}
    PxShift = 40.0;{x方向の隙間}
    PyShift = 10.0;{y方向の隙間}

このように、スクリプトは、メインのBEGIN、END;の間が実行プログラムになりますが、定数化したことで、プログラムをいじらずに、与えるデータによって結果に変化をもたらすことができます。

最後に、このスクリプトを再利用可能なスクリプト、オブジェクト化してみましょう。オブジェクト化する最大の利点は、先ほどプログラムをいじらずに与えるデータによって結果を変化させる方法として定数化したCONSTのデータをいじりましたが、この部分をパラメータ化し、オブジェクト情報パレットから入力することで図形に変化をもたらすことができます。

といってもここまでやってきたことでスクリプトは90%完成しています。CONSTに書いた定数をオブジェクトのパラメータに登録するだけです。

プラグインマネージャーから新規作成ボタンを押してオブジェクトの名前を入力し1点型オブジェクトを選んでOKを押します。

作成されたプラグインを選んで定義の編集ボタンを押しパラメータタブを選択します。

ここに、先ほどのCONSTに宣言した6つの定数をパラメータとして登録します。パラメータの名前はスクリプト内で使っている名前、頭のPは取って登録してください。

説明が逆になりますが、本来パラメータ名を決めて、それをスクリプト内で参照するときに名前の頭にPを付けるというのが決まりです。

フィールド名はオブジェクト情報パレットに出てくる名前です。日本語で大丈夫です。

型というのが、整数ならINTEGERを選び、実数ならNumberを選んでください(REALはありません)。

初期値はそれぞれデフォルト値としてオブジェクトを最初に置いたときに入ってくる数値になります。

6つのパラメータを登録したら、今度はスクリプト編集ボタンを押してスクリプトをコピー&ペーストします。

ここで最後にCONSTの部分はパラメータとして外からやってくるのコメント{}にします。

試しにコメントする前にコンパイルボタン(左から2つ目の歯車ボタン)を押してみましょう。エラーになりますね。これはパラメータとしてすでに定義された名前なので重複してますということです。

CONSTと6つの定数をコメントにした後にコンパイルボタンを押してみましょう、コンパイルが成功しましたね。

最後おまけに、カテゴリがその他になっていますので、定義の編集ボタンを押し一般タブからカテゴリのプルダウンを新規…を選んでカテゴリ名を新しく付けてみましょう。

ここまできたらあとは作業画面に登録するだけです。作業画面の編集でツールタブから新規ツールセットを作っておき、先ほど登録したカテゴリの中のオブジェクトをドラッグします。

さぁ実行してみましょう。ツールを選んだ状態でマウスを図面上で動かすと、デフォルトで設定した数値に従って破線で図形が現れます。図面上の任意の位置でクリックすることでオブジェクトが配置されます。

オブジェクト情報パレットでは定義したパラメータが編集可能なフィールドとして表示されます。オブジェクト情報パレットからパラメータをいじることで図形はリアルタイムに変化します。

この機能を利用できる製品

Fundamentals

Vectorworks Fundamentals

2D/3D汎用作図機能に、プレゼンボード作成機能や図面と連動できる表計算機能など、数多くの基本作図機能に加え、高品質レンダリング&3Dビジュアライズ機能を搭載したVectorworksシリーズの基本製品
詳細情報 購入ページ

Architect

Vectorworks Architect

建築設計や内装、ディスプレイデザインに対応した先進的なBIM・インテリア設計支援機能、拡張機能、さらには豊富な建築向けのデータライブラリを搭載した建築/内装業界向け製品
詳細情報 購入ページ

Landmark

Vectorworks Landmark

地形モデルや多彩な植栽、灌水設備計画等に対応するランドスケープデザイン機能、さらには豊富な造園向けのデータライブラリを搭載した都市計画/造園業界向け製品
詳細情報 購入ページ

Spotlight

Vectorworks Spotlight

ステージプランニングやライティング計画に対応した先進的な舞台照明計画支援機能、さらには各種メーカー製のトラスや照明機材、音響機器等の豊富なデータライブラリを搭載したエンタテインメント業界向け製品
詳細情報 購入ページ

Designer

Vectorworks Design Suite

専門分野別(建築設計/ディスプレイデザイン、ランドスケープデザイン、ステージデザイン&スポットライトプランニング)の設計支援機能、拡張機能、さらには豊富なデータライブラリを搭載した最上位の製品
詳細情報 購入ページ