こんな風にラジオボタンがある時
radioButton1をクリックしたら、そのチェックが消えてほしい。
という上司の要求にこたえるべく以下のように実装
まずラジオボタンがクリックされる直前の状態を記憶するメンバ変数を定義。
bool checkwhendown;
次にマウスダウンとクリックのイベントで呼び出す関数を以下に定義。
void RadioMouseDown(System::Object^ sender,System::Windows::Forms::MouseEventArgs^ e){ if (e->Button == System::Windows::Forms::MouseButtons::Left) { checkwhendown = dynamic_cast<RadioButton^>(sender)->Checked; } } void RadioMouseClick(System::Object^ sender,System::EventArgs^ e){ if (dynamic_cast<System::Windows::Forms::MouseEventArgs^>(e)->Button == System::Windows::Forms::MouseButtons::Left) { if( checkwhendown ) dynamic_cast<RadioButton^>(sender)->Checked = false; } }
各ラジオボタンの各メンバ関数から上記関数を呼び出す
_MouseDownがあったときに現在のラジオボタンの状態を保存。
その後「普通の」ラジオボタンの挙動が走ってしまい、クリックされたラジオボタンにチェックが入ってしまう。
そこでそのあとに飛んでくる_Clickイベントで保存していた状態を調べ、元々trueだったラジオボタンがクリックされていたら、falseをセットする
System::Void radioButton1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { RadioMouseDown(sender,e); } System::Void radioButton2_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { RadioMouseDown(sender,e); } System::Void radioButton3_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { RadioMouseDown(sender,e); } System::Void radioButton4_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { RadioMouseDown(sender,e); } System::Void radioButton5_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { RadioMouseDown(sender,e); } System::Void radioButton1_Click(System::Object^ sender, System::EventArgs^ e) { RadioMouseClick(sender,e); } System::Void radioButton2_Click(System::Object^ sender, System::EventArgs^ e) { RadioMouseClick(sender,e); } System::Void radioButton3_Click(System::Object^ sender, System::EventArgs^ e) { RadioMouseClick(sender,e); } System::Void radioButton4_Click(System::Object^ sender, System::EventArgs^ e) { RadioMouseClick(sender,e); } System::Void radioButton5_Click(System::Object^ sender, System::EventArgs^ e) { RadioMouseClick(sender,e); }
VC++.NET 2012で、ラジオボタンおよびチェックボックスのサイズが変わらなくて困った。リソースエディタのプロパティいじっても、ソースから直接SizeやWidth,Heightを入れても変わってくれない。
下記ではAppearanceをButtonにしているので普通のボタンのように見えている。
この状態だとプッシュボタンとサイズが異なってしまい不格好になる。
AutoSizeプロパティをFalseにする事で、サイズの問題は解決する。
加えて、TextAlignもMiddleCenterにしておくと、文字がセンタリングされてプッシュボタンと雰囲気が同じになる。
ずっと、有名なOpenGL入門の記事をコピペコピペで来ていたが、最近はこれだけではテクスチャを貼れなくなっている。
下記、赤下線太字で示した二行を加える必要がある。
それ以外は上記サイトと全く同じ。
#include <windows.h> #include <GL/gl.h> #include <GL/glut.h> #define TEXSIZE 64 GLubyte bits[TEXSIZE][TEXSIZE][3]; GLuint texName; void disp(void) { glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, texName); glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex2f(-0.9, -0.9); glTexCoord2f(0, 1); glVertex2f(-0.9, 0.9); glTexCoord2f(1, 1); glVertex2f(0.9, 0.9); glTexCoord2f(1, 0); glVertex2f(0.9, -0.9); glEnd(); glFlush(); } void timer(int value) { glRotatef(1, 0.5, 1, 0.25); glutPostRedisplay(); glutTimerFunc(50, timer, 0); } int main(int argc, char ** argv) { unsigned int i, j; for (i = 0; i < TEXSIZE; i++) { int r = (i * 0xFF) / TEXSIZE; for (j = 0; j < TEXSIZE; j++) { bits[i][j][0] = (GLubyte)r; bits[i][j][1] = (GLubyte)((j * 0xFF) / TEXSIZE); bits[i][j][2] = (GLubyte)~r; } } glutInit(&argc, argv); glutInitWindowSize(400, 300); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH); glutCreateWindow("Window Title"); glutDisplayFunc(disp); glutTimerFunc(100, timer, 0); glEnable(GL_TEXTURE_2D); glGenTextures(1, &texName); glBindTexture(GL_TEXTURE_2D, texName); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D( GL_TEXTURE_2D, 0, 3, TEXSIZE, TEXSIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, bits ); glutMainLoop(); return 0; }
ngPlantで木を作り、それをBlenderでレンダリングする。
ngPlantで貼り付けたテクスチャがそのまま持って行けるので、少し設定をいじるだけで簡単にレンダリングできる。
光源等々設定し、レンダリングする。
2015/04/19 岩手県盛岡市 石割桜。
見る機会は10回以上あったきがするが、ちょうどいい満開に遭遇したのはこれが初めてかもしれない。
花自体はとても見事に咲いているのだが、支えの棒がこうもたくさんあっては、老体を無理矢理酷使させられているように見えて哀れに思えてくる。いい加減休ませてやったらどうか。
ngPlantで木を作る作業の続きで、今日は枝に葉っぱをつける。
葉はテクスチャでつけるので、まずは葉の画像を用意する。gimpなどで葉っぱっぽい何かを描いて、.tga .jpg .pngのいずれかの形式で保存する。別にペイントでもいいが、背景を透明にできないのでせめてアルファを使えるソフトがいい。
早速やってみよう。
・・・できただろうか。
まずngPlantを起動し、葉っぱが生える枝を用意する。
今回は説明のために主幹をそのまま使用。
① 葉っぱをつけたいBranchを右クリック→[append branch]→[New branch]で枝を追加する。
② 追加されたBranchを右クリック→[Stem model]→[Quad]を選択。
③ 見づらいので、まず[Stem]のLengthを大きくする。
④ このままWidthを大きくすると重なってしまうので、[Branching]へ行きLimit maxをOnし、Max. numberを3程度にする。これで枝から生えている葉っぱは三枚となる(はず)。
⑤ [Stem]に戻りWidthを大きくする。少し細長い四角形にする。
⑥ [Material]タブへ移動
解説:
① Sampleはメンバ変数A,Bを持つ
② SampleのインスタンスはA=1,B=2で初期化される
③ showA,showBはA,Bを画面に表示する
④ A,Bはprivateで、setもfriend関数も用意していない。従ってA,Bを書き換える方法は存在しない
class Sample{ int A; int B; public: Sample(){ A=1; B=2; } void showA(){ std::cout << "A = " << A << std::endl; } void showB(){ std::cout << "B = " << B << std::endl; } }; int _tmain(int argc, _TCHAR* argv[]) { Sample instance; instance.showA(); instance.showB(); int k; std::cin >> k; return 0; }
はずなんだが、Sampleに一切手を加えないでA,Bを編集することができる。
int _tmain(int argc, _TCHAR* argv[]) { Sample instance;
instance.showA(); instance.showB(); int k; std::cin >> k; return 0; }
意図してこれをやる人間はそうそういないと思うが、間違ってこうなっしまうケースは結構頻繁にある。そして謎の理屈でポインタがだめなやつだという烙印を押されるのである。
というわけでngPlantの続き。主幹に枝を追加した後、枝振りを作っていく。
で、ngPlantは、木をポリゴンで作成するツールだ。
初期状態の画面はこうなっている。左側がビューで、円筒がこれから作成する木のべーすとなる。
この画面での右側の上の方は使わない。
重要なのは、右下の、図の赤枠で囲った部分。ここから、どの幹や枝を編集対象にするかを選択する。
まず、最初は[Plant]が選択されているので、[Branch-1]をクリックし選択する。選択された枝は太文字になる。
枝が選択されると、右上の画面がその枝の編集画面に変わる。ここからその枝の調整をしていく。
ちなみに以下、サムネイル画像を貼り付けたところ本来の画像から上下が切れてしまった。クリックするとフルで見られる。意外と上下を見ないと理解できない画像があったりする。遠い未来に直すかもしれない。
次に、枝を追加してみる。
[Branch-1]を右クリックし、[Append branch]→[New branch]を選択。
くしのように新しい枝が生える。右下に表示されている新しい枝[Branch-1-1]が選択(強調表示)されていることを確認して、右上をもう一度見てみる。
大体は同じで、違いは、主幹一本の時と違い、枝がたくさんあるときは各パラメータが影響を与えるのはその階層の枝全てだと言うこと。例えば、Axis Variationの数字をあげると、横に伸びている枝全てが複雑に折れ曲がる。
Axis Variationは0にしておくとして、この状態で、先ほど飛ばしたVariationとOffset influenceについてみてみる。
② | Variation 枝が複数あるとき、長さにばらつきを与える |
![]() |
③ | Offset influence 枝が複数あるとき、長さを 曲線的に変化させる |
![]() ![]() |
俺がstd::removeを理解できないのはどう考えても仕様が悪い。
というわけで、std::removeの挙動の再確認。
std::removeは、第一引数から第二引数までのコンテナ内の、第三引数で指定された値を削除する(嘘)、STLの関数だ。
今回使うのは以下。removeのためにalgorithm。iteratorはstd::beginを気分で使う。
#include <vector> #include <iostream> #include <algorithm> #include <iterator>
まず配列を用意して初期化する。
std::vector<int> vec = { 1, 2, 3, 2 , 4, 2 , 5 }; for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
結果は、
1
2
3
2
4
2
5
次に、この配列の値2に対してstd::removeをかける
auto end = std::remove(std::begin(vec), std::end(vec), 2); for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
結果は、
1
3
4
5
4
2
5
さあ、もう訳がわからない。消えたはずの2は残っていてその割に個数は減っていてその上位置まで変わっていて、さらにいじったつもりのない4と5が増えている。
Erase-Removeエディオムというのがあり、eraseと併用すれば消える。
vec.erase( std::remove( std::begin(vec), std::end(vec), 2 ), std::end(vec) ); for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
これはつまりこういうことだ。
std::removeは、指定した削除対象の値以外のものを配列の左側に再配置する。
そして戻り値として、必要なデータが入っている領域の次の値へのイテレータを返す。
std::removeが行うのはコンテナの中の値の書き換えだけなので、高速だが、そのままではいわば「破棄推奨領域」が生じてしまう。
(※) end2 = std::remove(begin,end,value);というようにして、以後の処理を begin~end2の範囲に対して行うようにすれば、消えたものとして処理できる。
この破棄推奨領域を削除する行為は、確保されているメモリ領域の変更なので、std::eraseを用いる必要がある。