- このトピックには5件の返信、3人の参加者があり、最後にonoken_otaにより1ヶ月、 2週前に更新されました。
-
投稿者投稿
-
-
2022年7月1日 00:26 #11103onoken_ota参加者
お世話になっております。
マリオネット解説講座いつも楽しく拝見させていただいてます。スクリプトプラグインを作成する際に躓いたので質問させてください。
複数の3D図形を選択している状態で、一番上の図形でそれ以外の図形をまとめて削り取るコマンドをつくろうとしています。
コードは以下の内容で、テストファイルではうまくいきました。
#--------------------------------------- import vs # ---------------------------------------------------------- # script----------------------------------------------------- # ---------------------------------------------------------- def HH1( hh ): global ha ,h1 h1 = hh ha += 1 def HH2( hh ): global ha,h1 ha -= 1 if ha >0 : h2 = vs.HDuplicate(h1,0,0) (a, objHN) = vs.SubtractSolid(hh, h2) vs.SetDSelect(objHN) ha = 0 vs.ForEachObject(HH1, "((VSEL=TRUE))") vs.ForEachObject(HH2, "((VSEL=TRUE))") vs.DelObject(h1) #---------------------------------------
テスト動画はtwitterに挙げています。
できた。昔から欲しい機能で、一つ一つ切り欠き図形にすることも多かったので、さっさと作ればよかった。2022だと削り取った図形のテクスチャが転写されるのかな。 pic.twitter.com/JJmKPtZZTL
— オノケン(太田則宏) (@onokennote) June 27, 2022
これで問題ないと思っていたところ、実際のプロジェクトで使用時に問題がおきたのですが、
vs.ForEachObject(HH1, “((VSEL=TRUE))”)
の検索でヒットするオブジェクトが、実際の操作画面では4つしか選択していない状態でも、900以上のオブジェクトがヒットしてしまって誤作動もしくはフリーズしてしまいました。(ha を書き出してみると948となりました。)おそらく、グループやシンボル内など、操作画面では現れていないけれどもステータスが選択状態となっているものがヒットしているのでは、という気がします。
これを上記の例だと、操作画面のオブジェクト情報パレットで選択図形数とカウントされている4つだけをピックアップするにはどうすれば良いでしょうか。(英語のフォーラムで探しても、それっぽいのが見つかりそうで見つかりませんでした。)
(ちなみに、例えば4つの図形を一旦グループ化した上で、グループに入ってから実行すると問題なく作動しました。)
もし、対応方法がわかりましたらご教示いただけないでしょうか。
よろしくお願い致します。
-
2022年7月1日 14:21 #11106木原 和信参加者
onoken_ota 様
いつもお世話になっております。エーアンドエー研究開発部です。
複数の図形の個別での削り取りを実行するサンプルプログラム拝見いたしました。私の環境で実行したところ問題なく動作し、ソースコードにも特別問題のある記述は見つけられません。
ご指摘の通り、グループ内の図形や他レイヤの図形などで選択された状態の図形を誤って取得している可能性は考えられます。上記含め複数のシチュエーションで検証しましたが、プログラムがフリーズする等の問題は再現しませんでした。
今回、残念ながら直接的な解決方法をお伝えすることができません。かわりにとはなりますが考えられる対策をいくつかご紹介します。
1. 図形数の問題
対象の図形数が多いまたは形状が複雑な場合に、Vectorworksの削り取りの処理の負荷が大きくなり、PCがクラッシュしている可能性が考えられます。図形の数を減らしたり、他のアプリケーションを終了する等、メモリ負荷の軽減をご検討ください。
2. ForEachObjectの問題
ForEachObjectの不具合が考えられます。ForEachObjectを使用しない方法をお試しください。「選択された図形を検索する」代替としては以下の方法があります。
2-1. ForEachObjectInLayerを使用する
ForEachObjectInLayerはForEachObjectと同様に、条件を指定してドキュメント内の図形を検索します。今回のように「選択された図形を、グループ内とコンテナ内を対象外として、現在のレイヤー上を探す」場合、次のように書けます。
vs.ForEachObjectInLayer( HH1, 2, 0, 0 ) vs.ForEachObjectInLayer( HH2, 2, 0, 0 )
2-2. FSActLayerとNextSObjのループで検索する
ForEach系統の検索機能を使用せずに、重なり順の前から後ろに順番に一つずつ確認します。
- FSActLayer:アクティブなレイヤの選択されている図形のうち最前(見た目上は最背面)の図形を取得します。
- NextSObj:選択されている図形のうち指定した図形の次の図形を取得します。
obj = vs.FSActLayer() while obj != 0: HH1( obj ) obj = vs.NextSObj( obj )
LSActLayerとPrevSObjでも同様のことが可能です。LSActLayerは見た目上一番手前の図形が取得できますので、HH1ルーチンは不要になります。
obj = vs.LSActLayer() h1 = obj while obj != 0: ha += 1 obj = vs.PrevSObj( obj )
3. スコープの問題
VectorworksPython特有のコールバック関数(今回で言うところのHH1とHH2)はスコープ範囲の定義が曖昧です。グローバル変数を使用した場合でも値を正しく引き継くことができていない可能性があります。グローバル変数を使用しない方法をお試しください。
3-1. VectorScriptに書き換える
PythonでなくVectorScriptで記述すればこの問題は発生しません。
3-2. Vectorworksのメモリ領域に値を記憶する
Rpstr_Set○○関数を使用してVectorworksのメモリ領域に値を直接保存すれば、サブルーチン、コールバック関数どこからでもアクセス(読み書き)できます。ここに記憶したデータはVectorworksを終了すると失われます。
ha = vs.Rpstr_SetValueInt('ha', 0 ) vs.Rpstr_SetValueInt('ha', ha+1 )
3-3. リストを使用する
経験則になりますがリスト型の変数はコールバック関数からでも安定しています。hlist = [] def HH1( hh ): hlist.append( hh ) vs.ForEachObject(HH1, "((VSEL=TRUE))") h1 = hlist.pop(-1) for hh in hlist: h2 = vs.HDuplicate(h1,0,0) (a, objHN) = vs.SubtractSolid(hh, h2) vs.DelObject(h1)
以上、ご参考になりましたら幸いです。
-
2022年7月1日 21:54 #11111onoken_ota参加者
木原様
いつも丁寧にご回答いただきありがとうございます。
大変助かりました。頂いたご回答に関しまして、
1. 図形数の問題
処理対象が多すぎるための挙動のようでしたが、スクリプトエディタの条件入力ツールで「表示状態かつ選択の有無が選択されている」を選んだ際にすでにエディタの左下に出てくる条件に合致する図形が900以上になっているため、VSEL=TRUEの挙動の問題かと思いました。(英語フォーラムでVSELで検索すると挙動に対するスレッドがあるっぽかったですが、理解できませんでした。)2-1. ForEachObjectInLayerを使用する
教えていただいたように修正するとバッチリ動きました。VSELの検索を使わずに済んだためかと思われます。2-2. FSActLayerとNextSObjのループで検索する
最初のコードでは、手前のオブジェクトを選択させたりするために結構無駄なルーチンがあった(スマートでない)のと、上記VSELに関して調べている際に、こんな感じのループに関して書かれてるっぽかったので、期待していた解決策はこんなイメージでした。
試行錯誤して、obj = vs.LSActLayer() h1 =obj obj = vs.PrevSObj( obj ) while obj != 0: obj0 = obj obj = vs.PrevSObj( obj ) h2 = vs.HDuplicate(h1,0,0) (a, obj2) = vs.SubtractSolid(obj0, h2) vs.DelObject(h1)
でうまく動きました。
(ループ中でvs.SubtractSolidを処理すると、objのハンドルを見失うか何かで、2番目のオブジェクトのみが削り取られただけでループが終了してしまうようでしたので、いろいろ試してvs.SubtractSolidをする前にPrevSObjを取得するようにしてみました。)3. スコープの問題
pythonのスコープに関しては完全に理解できるとは言い難く、たびたび引っかかっています。
(global宣言してもエラーがどうしても出てしまうので、nonlocalに書き換えたらエラーがなくなったりしますが、どういう場合にどちらが適切なのかは理解できておらず、今後の課題です。)ということで、FSActLayerとNextSObjのループを使うのが一番スマートっぽいのでこれを採用したいと思います。
今後も選択図形を扱いたくなることがありそうに思いますので、大変勉強になりました。
ありがとうございます。
-
2022年7月3日 13:53 #11112onoken_ota参加者
追伸
3. スコープの問題
vectoreorksのコールバックス関数のスコープ自体が不安定ということですね。
それであればいろいろと合点がいきました。
vectorscriptは変数の宣言が面倒ですぐエラー出してしまうので(汗)、今度からなるべくリストを使用する形を採用したいと思います。メモリ領域の利用も参考になります。 -
2022年7月3日 17:20 #11113Kimura参加者
こんにちは、蛇足かもしれませんが、
今回のケースでは、スコープの問題というよりは、図形リストの操作という視点から、木原さんの3-3にあるように自前でリストを作る方式が良いと思います。
なぜかと言いますと、Vectorworks内部では全ての図形がリストになっていて、PrevSObjやNextSObjなどで順繰りにたどることができるのですが、ForEachObjectでこれらをたどっている最中に図形を削除するとうまくたどれなくなってしまうのです。
英語の関数リファレンスにはこっそり書いてあるのですが、ForEachObjectの処理中に(コールバック関数で)DelObjectを使ってはいけませんというのは同根です。
https://developer.vectorworks.net/index.php/VS:ForEachObjectということで、一番安全なのは、3-3のようにいったん対象図形のリストを作り、それらに対して削り取りの操作を行う方法です。
(ちなみに、標準のコマンドでもできそうでしたので、Twitterでフォローさせていただきました。)
木村
-
2022年7月5日 22:41 #11115onoken_ota参加者
木村様
ご助言ありがとうございます。
ループ中にリストの内容が変わってしまう可能性があるので、先に図形のリストに放り込んでおいたほうが安全ということですね。それで一度躓いたのですが納得できました。ありがとうございます。
(関数リファレンスは日本語版を見ることが多いですが、英語版には「したがって、ハンドル配列を作成するためだけにForEachObjectを使用します。次に、戻ってハンドルを繰り返し処理し、不要なハンドルを削除します。(Google翻訳)」としっかり書いてありますね。英語版もチェックするようにします。)
-
-
投稿者投稿
- このトピックに返信するにはログインが必要です。