2012年02月09日
max script 勉強してみた その59 3dsmax 2012
前回はマルチリストボックスについて調べた。今回は残りのリスト系をざっと見て行きたい。
ドロップダウンリストは普段はコンパクトに1列だけ表示されてボタンで展開するリスト。

書式は以下の通り。
dropdownlist <name>
[<caption>]
[items:<array_of_strings>]
[selection:<number>]
[height:<number>]
[tooltip:<string>]
ちょっと注意しなきゃならないのはheightパラメータかな。これはクリックした時に出るドロップダウンリストの行数にドロップダウンリストの上に表示される1行を加えた数(ドロップダウンリストを展開した時の全行数)で設定する。例えばアイテムの数が10個でheightが6なら
utility utlddList01 "Drop Down List Test"
(
dropdownlist dd "Drop Down List"
items:#("1","2","3","4","5","6","7","8","9","10") height:6
)
ドロップダウンリストは下のように5行の大きさで表示されて、入りきらないアイテムはスクルールバーで表示を切り替えて選ぶことになる。

プロパティは以下の通り。
<dropdownlist>.items Array
<dropdownlist>.selection Integer
<dropdownlist>.selected String
<dropdownlist>.width Integer
<dropdownlist>.height Integer
<dropdownlist>.tooltip String
知っておいたほうがいいのはselectionが選択されている項目の番号で、selectedが選択されている項目の内容(文字列)になっているくらいかな。
イベントは次の2つ。選択と右クリックだけだ。
on <dropdownlist> selected <arg> do <expr>
on <dropdownlist> rightClick do <expr>
<arg>は選択された項目の番号が入る。
コントロールのイベントはメソッドとして呼び出せるものもあるようで、例えばスピナーのchangedイベントは、
<spinner>.changed <arg>
の形で呼び出すことが出来るし、ボタンのpressedは
<button>.pressed()
の形で呼び出すことが出来る。しかし残念ながらdropdownlistにはイベントに対応したメソッドが実装されていないので(clickedはプロパティ名と重なるからしょうがないとして、せめてrightclickくらいは実装して欲しい)、もしイベントハンドラで実行される機能を他から呼び出すようにしたいなら、ハンドラの内容を別のファンクションに定義し、ハンドラ内からそのファンクションを呼び出すようにして、ハンドラとその中身を分離するようにするしかないようだ。
例えば下のコードのようにボタンを押した時にスピナーのchangedイベントを呼び出すことは出来るけど、
utility utlddList02 "Drop Down List Test"
(
dropdownlist dd "Drop Down List"
items:#("1","2","3","4","5","6","7","8","9","10") height:6
spinner spn "Spinner"
button btn "click"
on dd selected itm do
(
format "% is selected\n" itm
)
on spn changed v do
(
format "spinner value changed to %\n" v
)
on btn pressed do
(
spn.value = 5.0
spn.changed 5.0
)
)
これをドロップダウンリストでやってもselectedというメソッドが無いのでエラーになる。
utility utlddList03 "Drop Down List Test"
(
dropdownlist dd "Drop Down List"
items:#("1","2","3","4","5","6","7","8","9","10") height:6spinner spn "Spinner"
button btn "click"
on dd selected itm do
(
format "% is selected\n" itm
)on spn changed v do
(
format "spinner value changed to %\n" v
)on btn pressed do
(
dd.selection = 3
dd.selected 3
)
)
そこで以下のようにイベントハンドラの中身を別ファンクションとして定義してハンドラと他からの呼び出して共有する手段をとるしかない。
utility utlddList04 "Drop Down List Test"
(
dropdownlist dd "Drop Down List"
items:#("1","2","3","4","5","6","7","8","9","10") height:6spinner spn "Spinner"
button btn "click"
fn dd_selected itm =
(
format "% is selected\n" itm
)on dd selected itm do
(
dd_selected itm
)on spn changed v do
(
format "spinner value changed to %\n" v
)on btn pressed do
(
dd.selection = 3
dd_selected 3
)
)
だそうだ。試していないけど、他のコントロールのイベントハンドラは同じ名前のメソッドとして呼び出せるみたいだよ。
コンボボックスは編集テキストボックス付きのリストボックス。

書式は以下の通り。
combobox <name>
[ <caption> ]
[items:<array_of_strings>]
[selection:<number>]
[height:<number>]
プロパティは以下の通り。
<combobox>.caption String
<combobox>.text String
<combobox>.items Array of Strings
<combobox>.selection Integer
<combobox>.selected String
<combobox>.width Integer
<combobox>.height Integer
captionはコントロールの上に表示されるラベルで、textは編集ボックスに出る文字列だ。max2012で試した限りでは、textプロパティが読み出す時は編集ボックスの値なのに代入するとキャプションの方が変わるおかしなことになっているようだ。だから編集ボックスの値をスクリプト側から直接書き換える手段はない。出来るのはリストから項目を選択して編集ボックスに反映させることだけだ。
イベントは以下のものがある。
on <combobox> selected <arg> do <expr>
on <combobox> doubleClicked <arg> do <expr>
on <combobox> entered <arg> do <expr>
on <combobox> changed <arg> do <expr>
on <combobox> rightClick do <expr>
changedとenteredは編集ボックスのイベントでchangedは編集ボックスの文字が変更されるたびに呼び出され、enteredは編集ボックスからフォーカスが外れたりEnterが押された時に呼び出される。
それではまた次回。
2012年02月08日
リプリケータを使ったメッシュオブジェクト内へのフィリング modo501 SP5
今回はwani さんから昔作ったスクリプトについてコメントを頂いたのでその話だ。
以前に作ったスクリプトなんだけど、選択したオブジェクトの内側にランダムにアイテムをコピーするようなスクリプトを作った。この当時はmodoのバージョンも203で、リプリケーターも存在しなかったので、ただ単純にアイテムをコピーして並べるか、インスタンスを並べるかしか出来なかった。でも501にはリプリケータがあるので、当時のようにわざわざアイテムをコピーしたりするより配置位置だけのレイヤーを作ってリプリケータにアイテムを並べさせた方が扱いやすい。
そこでスクリプトは大幅に単純化して選択アイテム内にポイントだけ散りばめるものにしてみた。以下はその使い方ね。
まずはメッシュアイテムとそれを満たしたいメッシュを別のレイヤーで用意する。

今回はToroidの中にMeshを詰めてみる。

ポリゴン選択モードで満たしたいオブジェクトのポリゴンを全て選択する。もしそのレイヤ内の全てのポリゴンが対象なら選択しなくてもいい。

次にスクリプト「fillpoints.py」をシステムメニューから「実行」や「スクリプトの実行」を使って起動する。

選択ポリゴン内に生成するパーティクルの数を聞いてくるので入力する。

リプリケータを追加して、アイテムを散りばめる場合はyes、ポイントが散りばめられたparticleレイヤーを作るだけにしたい場合はnoを選択。

yesを選択した場合は、散りばめるアイテムを選択する(今のところプルダウンに出るのはMeshアイテムだけなのでもしグループアイテムとかを散りばめたい場合は後でリプリケータの方で設定してね)。

するとこのように選択ポリゴン内に中心点を持つようにアイテムが散りばめられる。ディゾルブしてトロイドを半透明にすると中まで詰まっているのが確認できる。残念ながら表面から飛び出てるアイテムもあるけど、こういう事にならないようにするには、詰め込まれる側のオブジェクトをコピーして、ポリゴンをプッシュで内側に縮めて配置用の別オブジェクトを作っておいて、それに対してフィリングをする手もあるし、あとから不要なものだけ中心点にあるポイントを選択して削除したり移動したりしてもいい(particleレイヤでエレメント移動ツールを使えばいいね)。

そして生成されたリプリケータはこのように「原型となるアイテム」と「点位置の供給元」が設定されている。アイテムをランダムな方向に向けたりスケールを変えたりするのもここの設定で出来るよ。

ここで生成される「particle」というレイヤーはリプリケーターのインスタンスを配置するためにポイントが散りばめられているだけで、ポイント選択モードでドラッグして選択してみると、下の画像のようにポイントが存在するのが確認できる。リプリケータはこのポイントに対してインスタンスを配置しているだけなので、ポイントを移動したり削除したりすればそのポイントに対するインスタンスは移動したり消えたりする。だからポイントを生成した後で修正したい場合は「particle」レイヤのポイントを操作する事でも行うことが出来る。

ところでこれを作ってて気づいたんだけど、複数のリプリケータを作った時に原型となるアイテムと点位置の供給元が各アイテムごとに指定できなくなっている。SP6で直ってるのかな?ダウンロードして試してみないとね。2つの設定はリプリケータアイテムのチャンネルにも出てこないからパッケージインスタンスにチャンネル作り忘れてるんじゃないかな。
それではまた次回。
2012年02月07日
max script 勉強してみた その58 3dsmax 2012
前回はマルチリストボックスのコンストラクタについてパラメータを(特に<bitarray>について)調べた。今回はその続きから。
マルチリストボックス特有のプロパティは以下の2つ。
itemsはリストに表示する項目のリストを表す配列。
<MultiListBox>.items Array of Strings
配列値と同様の演算操作が可能だけど、表示を更新するにはこのプロパティに配列を代入する必要がある。例えば下のコードはアイテムをダブルクリックした時にそのアイテムを削除するものだ。一度ローカル変数tmpにコピーして、配列からダブルクリックされたアイテム番号の要素をdeleteitemで削除してから再びitemsプロパティに代入しなおしている。itemsに対して直接deleteitemをしたいところだけど、こうしないとリストの表示は更新されない。
utility utlMList01 "Multi List Box Test"
(
MultiListBox mlst_01 "Multi List Box"
items:#("line01","line02","line03","line04","line05")on mlst_01 doubleClicked v do
(
local tmp = mlst_01.items
deleteitem tmp v
mlst_01.items = tmp
)
)
要素の値を参照したいだけなら、
<MultiListBox>.items[ 番号 ]
という形でアクセスできる。
selectionは選択されている項目を表すBitArray配列。
<MultiListBox>.selection BitArray
これも選択状態の表示を更新する必要があるなら代入する必要がある。代入する値はBitArray値のほか、数値配列や数値も可能で、例えば下の2つの例はどちらも1番と3、4、5番の要素を選択状態にする。
<MultiListBox>.selection = #{1,3..5}
<MultiListBox>.selection = #(1,3,4,5)
また、数値ひとつを代入すれば、その要素だけ選択状態になり、
0を代入すれば、選択状態は解除される。<MultiListBox>.selection = 2
<MultiListBox>.selection = #{2}
<MultiListBox>.selection = #(2)
<MultiListBox>.selection = 0
<MultiListBox>.selection = #{}
<MultiListBox>.selection = #()
例えば以下のコードは選択状態を解除するClearボタンを付けてみたもの。ボタンが押されるとマルチリストボックスの選択が解除される。
utility utlMList02 "Multi List Box Test"
(
MultiListBox mlst_01 "Multi List Box"
items:#("line01","line02","line03","line04","line05")button btn_01 "Clear"
on btn_01 pressed do
(
mlst_01.selection = 0
)
)

イベントハンドラは次の4つ。
on <MultiListBox> selected <arg> do <expr>
on <MultiListBox> selectionEnd do <expr>
on <MultiListBox> doubleClicked <arg> do <expr>
on <MultiListBox> rightClick do <expr>
doubleClickedは項目がダブルクリックされた時にそのアイテム番号が<arg>に代入されて呼び出されるイベントハンドラ、rightClickはリストボックス内で右クリックした時に呼び出されるイベントハンドラだ。これらについてはリストボックスと変わらない。例えば下のコードはリストボックス内で右クリックをすると選択が全て解除されるものだ。
utility utlMList03 "Multi List Box Test"
(
MultiListBox mlst_01 "Multi List Box"
items:#("line01","line02","line03","line04","line05")on mlst_01 rightClick do
(
mlst_01.selection = 0
)
)
特徴的なのはselectedとselectionEndだ。selectedはアイテムの選択状態が変化した時にアイテムごとに呼び出されるイベントハンドラで、マルチリストボックスは複数のアイテムを一度に選択したり解除したり出来るので、その際選択状態が変更されるアイテムに対するイベントハンドラが上から順に呼び出される。そして一連のselectedイベントが終了した時点でselectionEndが呼び出される。
例えば以下のコードはイベントが発生するたびにMAXScriptリスナーにメッセージを出力するものだ。
utility utlMList04 "Multi List Box Test"
(
MultiListBox mlst_01 "Multi List Box"
items:#("line01","line02","line03","line04","line05")on mlst_01 selected v do
(
local state
if mlst_01.selection[v] then
state = "selected"
else
state ="not selected"
format "% is %\n" mlst_01.items[v] state
)
on mlst_01 selectionEnd do
(
format "Selection End\n\n"
)
)
これを実行して現れるリストボックスは初期状態でline01が選択状態になっている。

この状態でline01からline05までドラッグしながら選択すると、

リスナーパネルには以下のように表示される。
line02 is selected
line03 is selected
line04 is selected
line05 is selected
Selection End
line01は最初から選択されていたため、クリックされても選択状態が変化せず、イベントが発生していない事がわかる。
次にこの状態からline05をクリックすると、

line01〜line04は選択が解除されて状態が変わるけど、line05は状態が変わらないのでイベントが発生しない。
line01 is not selected
line02 is not selected
line03 is not selected
line04 is not selected
Selection End
このように状態が変化したアイテムに対してイベントを発生させるのがselectedなので注意が必要だ。変更された選択状態全体を調べたい場合はselectionEndイベント中にselectionプロパティを調べる方がいい。逆にリストアイテムの選択状態によってシーン内のオブジェクトの選択状態を変化させたいような場合は、毎回全てのアイテムの状態を調べてオブジェクトの選択状態に反映させるより、変化したものだけ反映させる方が効率がいい。
下のコードはリストで選択したアイテムに対応したオブジェクトを選択するものだ。
utility utlMList05 "Multi List Box Test"
(
MultiListBox mlst_01 "Multi List Box"on mlst_01 selected v do
(
if mlst_01.selection[v] then
selectMore (getNodeByName mlst_01.items[v])
else
deselect (getNodeByName mlst_01.items[v])
)
on utlMList05 open do
(
local items = for obj in $objects collect obj.name
local selnames = for obj in $selection collect obj.name
local selitems = #{}
for i = 1 to items.count do
if findItem selnames items[i] !=0 then
append selitems i
mlst_01.items = items
mlst_01.selection = selitems
)
)
これがその実行したところ。

それではまた次回。
2012年02月06日
modo501 SDK いじってみた その83 modo501 SP5
前回に引き続きサーフェスインターフェースの実装を調べてみたい。
TagCountはサーフェスに登録されているポリゴンタグの数を返す。BinCountもまったく同じようにGEARPART_COUNTを返していたけど、Binの方はSurfaceを構成する部品であるBinの数で、Binの数が変化するような事がなければ固定値でOKだけど、TagCountの方はサーフェスに登録されているポリゴンタグの数でポリゴンタグタイプによって値が異なる可能性がある。
ポリゴンタグタイプは以下の6通りがあるようだ(lxmesh.h)。
マテリアル
#define LXi_PTAG_MATR LXxID4('M','A','T','R')
パート
#define LXi_PTAG_PART LXxID4('P','A','R','T')
選択セット
#define LXi_PTAG_PICK LXxID4('P','I','C','K')
フォント名
#define LXi_PTAG_FONT LXxID4('F','O','N','T')
テキスト
#define LXi_PTAG_TEXT LXxID4('T','E','X','T')
文字揃え
#define LXi_PTAG_JUST LXxID4('J','U','S','T')
TagCountの引数typeによって登録されている個数を切り替えて返す必要があるわけだ。しかしこのサンプルの実装ではtypeパラメータは無視しているので、マテリアルグループレイヤーでマテリアルのマスクを作ってもパートのマスクを作っても選択セットのマスクを作っても同じ個数のポリゴンタグになる。
LxResult
CGearItemSurface::surf_TagCount (
LXtID4 type, unsigned int *count)
{
LxResult result = LXe_OK;*count = GEARPART_COUNT;
return result;
}
ちなみに LXxID4('M','A','T','R')は
#define LXxID4(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
というマクロで4文字のアスキーコードを32ビットに並べた値だ。
TagByIndexはtypeとindexで指定したポリゴンタグの名称を文字列で返す。これについてもtypeパラメータは無視している。
LxResult
CGearItemSurface::surf_TagByIndex (
LXtID4 type,
unsigned int index,
const char **stag)
{
LxResult result = LXe_OK;switch (index)
{
case GEARPART_TEETH:
stag[0] = GEAR_PART_TAG_TEETH;
break;case GEARPART_RIM:
stag[0] = GEAR_PART_TAG_RIM;
break;case GEARPART_SPOKES:
stag[0] = GEAR_PART_TAG_SPOKES;
break;case GEARPART_SHAFT:
stag[0] = GEAR_PART_TAG_SHAFT;
break;
}return result;
}
文字列についてはこう定義されている。
#define GEAR_PART_TAG_TEETH "Gear Teeth"
#define GEAR_PART_TAG_RIM "Gear Rim"
#define GEAR_PART_TAG_SPOKES "Gear Spokes"
#define GEAR_PART_TAG_SHAFT "Gear Shaft"
次にBinByIndexによって取得されるCGearItemBinについても調べて見たい。CGearItemBinはCGearItemSurface::surf_BinByIndexの中で、
CGearItemBin *bin =
src_pkg->bin_factory.Alloc (ppvObj);
としてbin_factoryによって生成される。bin_factoryはSurfaceBin、StringTag、TableauSurfaceの3つのインターフェースが追加されている。
CLxPolymorph<CGearItemBin> bin_factory;
bin_factory.AddInterface (
new CLxIfc_SurfaceBin<CGearItemBin>);
bin_factory.AddInterface (
new CLxIfc_StringTag<CGearItemBin>);
bin_factory.AddInterface (
new CLxIfc_TableauSurface<CGearItemBin>);
ILxSurfaceBinインターフェースをマニュアルで調べるとlx_surface.htmに以下の様に書かれている。
Finally we have surface bins which represents a segment of the surface with the same tags and thus the same shader. 'GetBBox' sets the bounding box for that bin 'FrontBBox' sets the front bounding box for that bin. The 'front' bounding box is formed by the geometry facing the viewer given by its position and direction vectors.
最終的にサーフェスのビンは同じタグしたがって、同じシェーダを持つサーフェスのセグメントを表すことができる。 「GetBBox」はそのビンのバウンディングボックスを返し、「FrontBBox」はそのビンのフロントバウンディングボックスを返す。「フロント」バウンディングボックスは、その位置と方向のベクトルによって与えたられたビュワーに面している形状によって形成される。The surface bin object should also present an ILxStringTags interface to get the polygon tags on the bin for shader tree masking, and an ILxTableauSurface interface to allow the surface to be sampled into a triangle soup.
LXxMETHOD( LxResult,
サーフェスビンオブジェクトには、シェーダーツリーマスキングのためにビン上でポリゴンタグを得るためのILxStringTagsインタフェース、そしてトライアングルスープでサーフェスがサンプリング出来るようにするためのILxTableauSurfaceインターフェイスも提供する必要がある。
GetBBox) (
LXtObjectID self,
LXtBBox *bbox);
LXxMETHOD( LxResult,
FrontBBox) (
LXtObjectID self,
LXtVector pos,
LXtVector dir,
LXtBBox *bbox);
とある。よってbin_factoryのようにILxSurfaceBinインターフェースを持つオブジェクトを生成するサーバーはILxStringTagsインターフェースとILxTableauSurfaceインターフェースもAddInterfaceしとかなくちゃならない。ILxStringTagsインターフェースはシェーダーを見つける時に呼び出されるILxTableauインターフェースのFindShaderメソッドでbinオブジェクトをtagオブジェクトとして渡してbinに設定されているポリゴンタグを調べるのに使われる。
LxResult
FindShader (
ILxUnknownID item,
ILxUnknownID tags,
void **ppvObj)
そのために実装しなきゃならないのはILxStringTagsインターフェースのGetメソッドで、以下のように実装されていて、ポリゴンタグタイプを渡してやって、それに対するタグ名を戻す仕組みだ。m_partはSetPartで設定したね。
LxResult
CGearItemBin::stag_Get (LXtID4 type, const char **tag)
{
switch (m_part)
{
case GEARPART_TEETH:
tag[0] = GEAR_PART_TAG_TEETH;
break;case GEARPART_RIM:
tag[0] = GEAR_PART_TAG_RIM;
break;case GEARPART_SPOKES:
tag[0] = GEAR_PART_TAG_SPOKES;
break;case GEARPART_SHAFT:
tag[0] = GEAR_PART_TAG_SHAFT;
break;
}return LXe_OK;
}
ILxTableauSurfaceインターフェースについてはせっかく実装してるのに、このサンプルではなぜか使わないで別に生成したオブジェクトからtableauを生成しているみたいだ。詳しくはCGearItemInstanceのtsrc_Elementsメソッドのところで調べてみたい。
それではまた次回。
2012年02月03日
max script 勉強してみた その57 3dsmax 2012
前回はリストボックスについて調べた。リストがわかれば同様のコントロールもほぼ理解できる。
マルチリストボックスは複数選択可能なリストボックス

書式は以下の通り。
MultiListBox <name>
[<caption>]
[items:<array_of_strings>]
[selection:{<bitarray> | <number_array> | <number>}] [height:<number> ]
パラメータはほとんどがおなじみのものだね。このリストボックスは複数選択が可能なのでselectionだけがちょっと特別な形になっていて、1つの項目を番号で指定する方法の他に<bitarray>値や<number_array>値を使って複数の項目を選択状態として指定できる。例えば上の画像で初期状態として3番目のアイテムを選択状態にしたければ、
utility utlMList01 "Multi List Box Test"
(
MultiListBox mlb_01 "Multi List Box"
items:#("list01","list02","list03","list04","list05")
selection:3
)
としてやればいい。

1と3と4を選択状態にしたいなら<number_array>値を使って、
utility utlMList01 "Multi List Box Test"
(
MultiListBox mlb_01 "Multi List Box"
items:#("list01","list02","list03","list04","list05")
selection:#(1,3,4)
)
としてやってもいいし、<bitarray>値を使って、
utility utlMList01 "Multi List Box Test"
(
MultiListBox mlb_01 "Multi List Box"
items:#("list01","list02","list03","list04","list05")
selection:#{1,3,4}
)
でもいい。

<bitarray>値は初めて出てきたのでちょっと調べてみるとその正体は、1から順番に番号が振られた集合の選択、非選択を1と0に置き換えて並べたビット列の値のようで、大量のエレメントの選択状態がコンパクトに保存できるメリットがある。書式は以下の通りで、表記上は配列とさほどの違いはない(ブラケットが「( )」のかわりに「{ }」になっている)。
#{ <selection> }
<selection>はカンマで区切られた次のリスト
<integer> -- integers must be > 0
<integer> .. <integer>
ただ、ビットが順に並んでいるのが前提なので、「この区間は全部選択してる」みたいな表現が出来て、それが「<integer> .. <integer>」なわけだ。例えば上のリストで1と3から5まで選択しているという場合は、
utility utlMList01 "Multi List Box Test"
(
MultiListBox mlb_01 "Multi List Box"
items:#("list01","list02","list03","list04","list05")
selection:#{1,3..5}
)
とすればいい。

これは結局「10111」みたいなビット列になるわけだね(格納の順番は逆かも知れないけど)。
ちなみにビット列は各要素が選択されているかされてないかの2つの状態(1か0か)だけなので、要素1つにアクセスするとboolean値になる。
<bitarray>[<integer>]
例えばこんな感じ。
ba = #{1,3,5..10}
#{1, 3, 5..10}
ba[1]
true
ba[2]
false
ba[6]
true
ba[7]=false
false
ba
#{1, 3, 5..6, 8..10}
また<bitarray>どうしで論理演算が行える。
<bitarray> + <bitarray> 論理和(OR)
<bitarray> * <bitarray> 論理積(AND)
-<bitarray> 否定(NOT)
例えばこんな3つの値を用意して、
ba1=#{1..10}
#{1..10}
ba2=#{4..7}
#{4..7}
ba3=#{3..5}
#{3..5}
それぞれをビット列であらわすと、
ba1:1111111111
ba2:0001111
ba3:00111
となる。論理和は2項のどちらかの要素がtrueであればtrueにする演算なので、
ba1+ba2
#{1..10}
ba2+ba3
#{3..7}
ビットで見ると以下の通り。各桁でどちらかが1のものは1になる。
ba1:1111111111
ba2:0001111
1111111111ba2:0001111
ba3:00111
0011111
論理積は2項の両方の要素がtrueの時だけtrueになる演算
ba1*ba2
#{4..7}
ba2*ba3
#{4..5}
ビットで見ると以下の通り。各桁でどちらかが1のものは1になる。
ba1:1111111111
ba2:0001111000
0001111000ba2:0001111
ba3:0011100
0001100
否定はビットが反転する。
-ba1
#{}
-ba2
#{1..3}
-ba3
#{1..2}
これだと−ba1は0、−ba2は3つ−ba3は2つの要素しか無いように見えるけど、要素数は変わらない。
ba1:1111111111
0000000000ba2:0001111
1110000ba3:00111
11000
だから例えば10個目の要素にfalseを入れておけば要素数が増えて、10個の要素になるので、反転した時に10個目の要素まで反転されて出てくる。
ba4=#{3..5}
#{3..5}
ba4.count
5
ba4[10]=false
false
ba4.count
10
ba4
#{3..5}
-ba4
#{1..2, 6..10}
さらに集合AのBと共通する要素をfalseにする演算もできる。
<bitarray> - <bitarray>
上の演算子で置き換えると
<bitarray> * (-<bitarray>)
例えば
ba1-ba2
#{1..3,8..10}
ba2-ba3
#{6..7}
ビットで見ると以下の通り。各桁でどちらかが1のものは1になる。
ba1:1111111111
ba2:0001111
1110000111ba2:0001111
ba3:00111
0000011
<bitarrya>値には以下のプロパティがある。
countは<bitarray>の要素数
<bitarray>.count : Integer
numberSetは<bitarray>のtrueになっている要素数
<bitarray>.numberSet
ba1
#{1..10}
ba1.numberset
10
ba2
#{4..7}
ba2.numberset
4
isEmptyは<bitarray>で有効になっている要素があるかどうかをtrue、falseで返す。
<bitarray>.isEmpty
要素数に係わらず、全ての要素がfalseならtrueになる。
ba1
#{1..10}
-ba1
#{}
(-ba1).count
10
(-ba1).isEmpty
true
さらに<bitarray>には以下のメソッドがある。
appendは<bitarray>に有効な要素を追加する。
append <bitarray> <integer>
例えばこんな感じで有効な要素を追加していける。重複した場合は何も起こらない。
ba5=#{}
#{}
append ba5 3
#{3}
append ba5 7
#{3, 7}
append ba5 7
#{3, 7}
joinは2つの<bitarray>の論理和をとる。+演算子と同じだ。
join <bitarray> <bitarray>
findItemは<integer>番の要素が有効な時は<integer>番を、無効なら0を返す。
findItem <bitarray> <integer>
こんな感じ。
ba2
#{4..7}
finditem ba2 3
0
finditem ba2 4
4
finditem ba2 5
5
finditem ba2 10
0
deleteItemは<integer>番の要素をfalseにする。
deleteItem <bitarray> <integer>
例えばこんな感じ。無効になっている要素を指定しても変わらない。
deleteItem ba1 1
#{2..10}
deleteItem ba1 3
#{2, 4..10}
deleteItem ba1 4
#{2, 5..10}
deleteItem ba1 4
#{2, 5..10}
copyは<bitarray>のコピーを作る。
copy <bitarray>
値の代入は参照がコピーされるだけで本体は増えないから、元の値に影響を与えないでとっておきたいならcopyする必要がある。下の例ではba6を代入したba7とコピーを代入したba8がba6を変更した時どうなるかを試してみたもの。単純に代入したba7は参照しているオブジェクトが同じなのでba6を変更すればba7も変わるけど、コピーを作って代入したba8は影響を受けていない。
ba6 = #{1..5}
#{1..5}
ba7 = ba6
#{1..5}
ba8 = copy ba6
#{1..5}
deleteitem ba6 1
#{2..5}
ba6
#{2..5}
ba7
#{2..5}
ba8
#{1..5}
今回はほとんど<bitarray>値の話になっちゃったな。
それではまた来週。