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

第45回「NURBS曲線の頂点設定」

FundamentalsArchitectLandmarkSpotlightDesigner2023

今回は少しマニアックな内容です。NURBS曲線の頂点座標を設定するスクリプト関数「NurbsSetPt3D」について深掘りしていきます。

NURBS曲線は3Dのモデリングでよく使用されている図形ですが、3Dパス図形や、3Dパス型のプラグインオブジェクトのパス図形など直接見えないところでも使用されています。

NURBS曲線には関連のスクリプトのAPIが用意されていますので、NURBS曲線の生成や移動、変形などを自動化するプログラムの作成が可能です。

NURBS曲線の頂点座標を設定する関数「NurbsSetPt3D」の使い方について、私自身勘違いしてハマってしまった経験から、今回は、その解決方法をみなさんに共有します。

45-1. NURBS曲線のAPI

まずは、NURBS曲線関連のAPIを確認しましょう。A&Aホームページで公開されているスクリプトリファレンスを開きます。

スクリプトリファレンス

「View By Class」を選択して、関数のカテゴリごとに分類する表示に変更します。

次に「Objects – NURBS」のカテゴリを選択します。

NURBS曲線関連(曲面含む)のAPIがまとまって表示されます。

NurbsSetPt3Dを選択すると、関数の説明が表示されます。

曲線の場合は、index1index2で頂点の位置を指定した上で、新しい座標値(x,y,z)を設定するという内容です。

45-2. 1区間のNURBS曲線でのNurbsSetPt3D

今回は頂点が分かりやすいように、次数1のNURBS曲線を作成します。

それでは、NurbsSetPt3Dを使って、NURBS曲線の頂点座標を変更してみましょう。

VectorScript:

NurbsSetPt3D( objectHd :HANDLE; index1 :LONGINT; index2 :LONGINT; pX :REAL; pY :REAL; pZ :REAL );

Python:

vs.NurbsSetPt3D(objectHd, index1, index2, p)

2つ目の頂点の座標を (0,200,0) から (0,200,50) に変更します。

VectorScript:

Procedure test;
begin
    NurbsSetPt3D( FSActLayer, 0, 1, 0,200,50 );
    SetObjectVariableBoolean( FSActLayer, 1167, True );
end;
run( test );

Python:

vs.NurbsSetPt3D( vs.FSActLayer(), 0, 1, (0,200,50) )
vs.SetObjectVariableBoolean( vs.FSActLayer(), 1167, True )

index1は区間の番号です。0から始まります。1区間目を0として数え、2区間目は1、3区間目は2となります。今回のNURBS曲線は1区間で構成されていますので、index1には0を入力します。

index2は区間の中での頂点の番号です。こちらも0から始まります。2点目を設定するのでindex2は1を入力します。

SetObjectVariableBoolean( FSActLayer, 1167, True );

このコードはNUBRS曲線が変更された時に図形をリセット(再描画)してくれるおまじないです。忘れずに入れておきましょう。

スクリプトを実行して3Dビューで確認すると、2点目だけz方向に高くなっているのを確認できます。

次に、3つ目の頂点を (100,200,0) から (100,200,100) に設定します。2点目と同様の考え方でindex1には0、index2には2を入力します。

VectorScript:

NurbsSetPt3D( FSActLayer, 0, 2, 100,200,100 );

Python:

vs.NurbsSetPt3D( vs.FSActLayer(), 0, 2, (100,200,100) )

スクリプトを実行すると、3点目の頂点が100の高さに移動したことを確認できます。

45-3. 複数区間のNURBS曲線でのNurbsSetPt3D

NURBS曲線は複数区間で構成される場合があります。次の2つのNURBS曲線は、5点の頂点を持つNURBS曲線です。見た目は同じに見えますが、1つ目のNURBS曲線は1区間、2つ目のNURBS曲線は4区間で構成されています。この複数区間のNURBS曲線がクセもので、複数区間あることに気がつかずに、プログラムが想定外の結果にしかならずしばらくハマってしまいました(泣)。

それぞれ3点目の頂点を(100,100,100)に変更してみましょう。

45-3-1. 1区間のNURBS曲線の場合

1区間の3点目なので、index1を0、index2を2として NurbsSetPt3D に設定します。

VectorScript:

Procedure test;
begin
    NurbsSetPt3D( FSActLayer, 0, 2, 100,100,100 );
    SetObjectVariableBoolean( FSActLayer, 1167, True );
end;
run( test );

Python:

vs.NurbsSetPt3D( vs.FSActLayer(), 0, 2, (100,100,100) )
vs.SetObjectVariableBoolean( vs.FSActLayer(), 1167, True )

45-3-2. 4区間のNURBS曲線の場合

1区間と4区間でスクリプトの内容を変える必要がありますが、理解を深めるために1区間の場合と同じスクリプトを4区間のNURBS曲線に対して実行してみます。

エラーが表示されました。「indexの指定が範囲外のため失敗しました」という内容です。indexの部分を修正する必要があるようです。

今回変更したいNURBS曲線の見た目上の3点目は、2区間目の2点目なので、index1を1、index2を1に修正します。

VectorScript:

NurbsSetPt3D( FSActLayer, 1, 1, 100,100,100 );

Python:

vs.NurbsSetPt3D( vs.FSActLayer(), 1, 1, (100,100,100) )

修正したスクリプト実行します。エラーは表示されませんが、結果はどうでしょうか。

確かに3点目の座標が変更されていますが、線が途切れてしまっておかしなことになっています。これは、NURBS曲線の見た目上の3点目は「2区間目の2点目」ともう一つ「3区間目の1点目」としての頂点でもある、ということが原因です。

複数区間あるNURBS曲線では、1つの頂点を変更するとき、その頂点が区間の境目にあたる場合は、前後の区間ごとに頂点の情報を変更する必要があります。これを踏まえてスクリプトをを修正します。

2区間目の2点目のNurbsSetPt3Dに加えて、3区間目の1点目のNurbsSetPt3Dを追加します。

VectorScript:

Procedure test;
begin
    NurbsSetPt3D( FSActLayer, 1, 1, 100,100,100 );
    NurbsSetPt3D( FSActLayer, 2, 0, 100,100,100 );
    SetObjectVariableBoolean( FSActLayer, 1167, True );
end;
run( test );

Python:

vs.NurbsSetPt3D( vs.FSActLayer(), 1, 1, (100,100,100) )
vs.NurbsSetPt3D( vs.FSActLayer(), 2, 0, (100,100,100) )
vs.SetObjectVariableBoolean( vs.FSActLayer(), 1167, True )

スクリプトを実行します。今度は、線が途切れることなく見た目上の3点目の頂点が(100,100,100)に設定されていることが確認できました。

45-4. NURBS曲線の変形の自動化

NURBS曲線の変形の自動化を考えると、プログラムの中でNURBS曲線の区間の数や頂点の数を把握して、その数に応じて動的に処理を変える必要があります。

「Objects – NURBS」カテゴリのAPIではNURBS曲線の区間の数や頂点の数を取得するものが用意されていますので、これらを活用しながらコーディングしていくことになります。

最後に、NURBS曲線の情報を取得する関数を活用した「n番目の頂点の高さを変更するサンプル」をご紹介します。仕様もアルゴリズムも多様に考えられる内容ですので、みなさんも汎用的で効率の良い方法を考えてみてください。

VectorScript:

procedure test;
var
    nurbsobj : handle;
    ptx,pty,ptz,newZ : real;
    ii,jj,cnt, numseg, numpts, ptID, ptSubID, segID : integer;
begin
    nurbsobj := FSActLayer;                         { 選択状態のNURBS曲線を対象とする }

    ptID := IntDialog( '変更したい頂点番号を指定してください(0始まり)。', '1' );

    GetPolyPt3D( nurbsobj, ptID, ptx,pty,ptz );     { 指定した頂点の現在の座標を取得 }
    newZ := DistDialog( '頂点の新しい高さ指定してください。', Num2StrF( ptz ) );

    numseg := NurbsCurveGetNumPieces( nurbsobj );   { 区間の数を取得 }
    cnt := 0;   { 頂点の位置のフラグ }

    { 区間ごとの頂点数を取得しながら、指定した"見た目の頂点"の位置を調べる }
    for ii := 0 to numseg-1 do
    begin
        numpts := NurbsGetNumPts( nurbsobj, ii );   { 区間ごとの頂点の数を取得 }
        for jj:=0 to numpts-2 do
        begin
            if cnt = ptID then                      { これまでの区間ごとのの頂点数の合計が、指定した頂点番号と一致したとき }
            begin
                segID := ii;                        { 区間のインデックス }
                ptSubID := jj;                      { 区間の中の頂点番号のインデックス }
            end;
            cnt := cnt+1;
        end;
    end;

    { 探査した頂点の高さを変更 }
    NurbsSetPt3D( nurbsobj, segID, ptSubID, ptx,pty,newZ );
    { 区間の1点目の場合は前の区間の最後の点も変更する }
    if ptSubID = 0 then 
        NurbsSetPt3D( nurbsobj, segID-1, NurbsGetNumPts( nurbsobj, segID-1 )-1, ptx,pty,newZ );

    SetObjectVariableBoolean( nurbsobj, 1167, True );
end;
run(test);

Python:

nurbsobj = vs.FSActLayer()                          #{ 選択状態のNURBS曲線を対象とする }

ptID = vs.IntDialog( '変更したい頂点番号を指定してください(0始まり)。', '1' )

ptx,pty,ptz = vs.GetPolyPt3D( nurbsobj, ptID );     #{ 指定した頂点の現在の座標を取得 }
newZ = vs.DistDialog( '頂点の新しい高さ指定してください。', vs.Num2StrF( ptz ) )

numseg = vs.NurbsCurveGetNumPieces( nurbsobj );     #{ 区間の数を取得 }
cnt = 0 #{ 頂点の位置のフラグ }
segID = 0
ptSubID = 0

#{ 区間ごとの頂点数を取得しながら、指定した"見た目の頂点"の位置を調べる }
for ii in range( numseg ):
    numpts = vs.NurbsGetNumPts( nurbsobj, ii )      #{ 区間ごとの頂点の数を取得 }
    for jj in range( numpts-1 ):
        if cnt == ptID:                             #{ これまでの区間ごとのの頂点数の合計が、指定した頂点番号と一致したとき }
            segID = ii                              #{ 区間のインデックス }
            ptSubID = jj                            #{ 区間の中の頂点番号のインデックス }
        cnt += 1

#{ 探査した頂点の高さを変更 }
vs.NurbsSetPt3D( nurbsobj, segID, ptSubID, (ptx,pty,newZ) )
#{ 区間の1点目の場合は前の区間の最後の点も変更する }
if ptSubID == 0:    
    vs.NurbsSetPt3D( nurbsobj, segID-1, vs.NurbsGetNumPts( nurbsobj, segID-1 )-1, (ptx,pty,newZ) )

vs.SetObjectVariableBoolean( nurbsobj, 1167, True )

今回の投稿にて、本年最後の投稿となります。本年もVectorworks Design Blogにお付き合いくださり、ありがとうございました。Vectorworks Design Blogでは、来年も引き続き 1/17にリリースされるVectorworks 2024を中心に情報を発信して参ります。みなさまどうか良いお年をお迎えください。

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

Fundamentals

Vectorworks Fundamentals

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

Architect

Vectorworks Architect

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

Landmark

Vectorworks Landmark

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

Spotlight

Vectorworks Spotlight

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

Designer

Vectorworks Design Suite

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