速さの錯視
対象間の間隔によって速さの知覚が異なる(Scientific American: MIND, MAY/JUNE 2014, p. 69. “More legs move faster?”)ことのデモンストレーションプログラムをVisual C++/.NET 2013で作成してみた。
プログラムillusionSpeed.exeをダウンロード・実行すると図1のフォームが表示される。

図1
うまくダウンロード・実行できないときは
<WindowsにおけるEXEファイルのダウンロード・実行>
を参照されたい。
図1のフォームで「GO」ボタンをクリックすると図2の画面になり、2つの大きい円の周りを小さい円が回転する。

図2
左右、同じ速さで回転しているが、小円の数の多い右側の方が速く回転しているように見える。回転の速さは「Step Size」の値によって変わる。値を大きくすると速くなる。
もし、小円の動きがあまり滑らかでないときは、図2のフォームを小さくして画像の処理時間を短くしたプログラムillusionSpeed_S.exeも用意したので試すことができる。
プログラムのソースコードファイルなどは圧縮ファイルillusionSpeed.zipにまとめた。クリックしてダウンロード解凍すれば、Visual Studio 2013 for Windows Desktopで開くことができる。
フォームを小さくしたプログラムのソースコードファイルなどをまとめた圧縮ファイルillusionSpeed_S.zipも用意した。クリックしてダウンロード解凍してVisual Studio 2013 for Windows Desktopで開くことができる。
プログラムのメインの部分は以下のようになっている。
#pragma
once
namespace
illusionSpeed {
                ・
                ・
                ・
        この部分は、Visual
Studioによって勝手に作成される。
                ・
                ・
                ・
#pragma
endregion
        private:
System::Void buttonClose_Click(System::Object^  sender, System::EventArgs^  e) {
                Close();
        }
                         int pw, ph;
                         double StepSize,
StepPos;
        private:
System::Void buttonGO_Click(System::Object^  sender, System::EventArgs^  e) {
                //
                //                      フォームの準備
                //
                timer1->Enabled
= false;
                this->WindowState = FormWindowState::Maximized;
                this->Update();
                pictureBox1->Left
= 0;
                pictureBox1->Top
= buttonClose->Height + 1;
                pictureBox1->Width
= this->ClientSize.Width;
                pictureBox1->Height
= this->ClientSize.Height - pictureBox1->Top;
                pw
= pictureBox1->Width;
                ph = pictureBox1->Height;
                pictureBox1->Image
= gcnew Bitmap(pw, ph);
                Graphics
^g = Graphics::FromImage(pictureBox1->Image);
                Brush
^ BshWt = gcnew SolidBrush(Color::White);
                g->FillRectangle(BshWt, 0, 0, pw, ph);
                StepSize = double::Parse(textBox1->Text);
                StepPos = 0.0;
                timer1->Interval
= 1;
                timer1->Enabled
= true;
        }
private:
System::Void timer1_Tick(System::Object^ 
sender, System::EventArgs^  e) {
        timer1->Enabled
= false;
        Bitmap
^ bm = gcnew Bitmap(pw, ph);
        Graphics
^g = Graphics::FromImage(bm);
        //
        //              全体を白で塗り潰す
        //
        Brush
^ BshWt = gcnew SolidBrush(Color::White);
        g->FillRectangle(BshWt, 0, 0, pw, ph);
        //
        //                      大きな2つの円の描画
        //
        double
C_L_left = pw * (0.5 - 0.25);
        double
C_L_right = pw * (0.5 + 0.25);
        double
r_L = pw / 8.0;
        double
r_S = pw / 48.0;
        Brush
^ BshBl = gcnew SolidBrush(Color::Blue);
        g->FillEllipse(BshBl, int(C_L_left - r_L), int(ph
/ 2 - r_L), int(2 * r_L), int(2 * r_L));
        g->FillEllipse(BshBl, int(C_L_right - r_L), int(ph
/ 2 - r_L), int(2 * r_L), int(2 * r_L));
        //
        //                      小さい円の描画
        //
        Brush
^ BshOr = gcnew SolidBrush(Color::Orange);
        for (int i = 0; i
< 2; i++){
                double
theta = Math::PI * (StepPos + i);
                double
x_pos = C_L_left + (r_L + r_S * 1.1) *
Math::Cos(theta);
                double
y_pos = ph / 2.0 - (r_L + r_S * 1.1) *
Math::Sin(theta);
                g->FillEllipse(BshOr, int(x_pos - r_S),
int(y_pos - r_S), int(2.0 * r_S), int(2.0 * r_S));
        }
        for (int i = 0; i
< 16; i++){
                double
theta = Math::PI * (StepPos + i
/ 8.0);
                double
x_pos = C_L_right + (r_L + r_S * 1.1) *
Math::Cos(theta);
                double
y_pos = ph / 2.0 - (r_L + r_S * 1.1) *
Math::Sin(theta);
                g->FillEllipse(BshOr, int(x_pos - r_S),
int(y_pos - r_S), int(2.0 * r_S), int(2.0 * r_S));
        }
        delete
g;
        StepPos += StepSize;
        if (StepPos > 2.0) StepPos -= 2.0;
        Graphics
^g1 = Graphics::FromImage(pictureBox1->Image);
        g1->DrawImage(bm, 0, 0);                                        //              ビットマップに描いた画像の転送
        pictureBox1->Image
= pictureBox1->Image;                        //             描画結果を表示する。
        delete
bm;
        delete
BshBl;
        delete
BshWt;
        delete
BshOr;
        delete
g1;
        timer1->Enabled
= true;
}
private:
System::Void textBox1_TextChanged(System::Object^  sender, System::EventArgs^  e) {
        timer1->Enabled
= false;
}
};
}
Visual C++/.NETプログラミングについては<岡本安晴「大学生のための心理学VC++プログラミング入門」勁草書房>で説明している。