8月6日の投稿の続編です。
前回はILI9488ディスプレイモジュールをタッチすることで点列を選択し視覚化しました。しかし、それでは選択に限界があるので、複素平面上をスキャンしてどんな点列が見られるか調べてみました。
上の8個の図は、虚部yを固定し実部xをある範囲にわたって変化させて得られたものです。なので単一の点(複素数$z_1=C,z_0=0$)を始まりとする点列ではなく、複数の点を始めとする点列群の重なりとなっています。なお、小さいモジュール画面のスマホ写真を多数掲載するのはちと辛かったので、同様なプログラムをPC上で動かしたもののプリントスクリーンを編集、掲載しました。細かな所は前回(8月6日投稿)を見て頂くことにして、点列生成の種となる$C$の値について次に示します。
$C$の虚部のピクセル | $C$の実部の始点のピクセル | $C$の実部の終点のピクセル |
---|---|---|
105 | 80 | 130 |
70 | 60 | 160 |
54 | 164 | 230 |
50 | 14 | 50 |
50 | 150 | 220 |
-4 | -98 | 2 |
-10 | -100 | 0 |
-49 | -114 | -9 |
for文で扱い易いピクセル単位にしており、この場合1ピクセルに対する数としての変化ステップは $2.4/480.0=0.005$ です。なお、前回同様数学的に正しいかどうかは解りませんので悪しからず。グラフィックスとしての面白さと、マンデルブロー集合自体に現れる模様がここにも現れており、その奥深さ不思議さを感じた次第です。以下に "Arduino IDE 2.1.1" でのスケッチを示します。使用したボードなど、細かい設定は前回と同じです。
Mandelbrot_SerialDots.ino
#include <TFT_eSPI.h>
// 必要なPin設定は libraries/TFT_eSPI/User_Setup.hで行う
// SPI_Speed Up 40MHz <- 20MHz in default
// #define SPI_FREQUENCY 40000000 in about 360th line
TFT_eSPI tft = TFT_eSPI(); // also work with RST pin -> board's RST
int X0 = 240; // tft.width = 480
int Y0 = 160; // tft.height = 320
float view; // viewport width
float di; // real/imaginary calculation step
float idouX = 0.0, idouY = 0.0; // 現在の表示(view)の中心
int k; // 反復回数
int re, gr, bl; // color 0-255
float x, y, zr, zi, R, I; //複素計算変数
float toRad = PI / 180.0; //度をラジアンに変換
//PI=3.1415926535897932384626433832795 defined in Libraly
float absZ = 4.0; // 発散判定
int r;
int inputPin=34;
int val=0;
void setup() {
tft.init();
tft.setRotation(1);
view = 2.4;
// Real and Imaginary step per pixel
di = view / 480.0; // di=0.01 <- 4.8/480.0
idouX = -0.68; // display center
idouY = 0.3;
r = 0;
pinMode(inputPin, INPUT);
}
void Mandelbrot(void) {
for (int j = 320 - Y0; j > -Y0; j--) {
y = j * di + idouY;
for (int i = -X0; i < 480 - X0; i++) {
if (i < -70 && j > 288) {
tft.drawPixel(X0 + i, Y0 - j, TFT_BLACK);
} else {
x = i * di + idouX;
zr = 0.0;
zi = 0.0;
k = -1;
do {
k++;
R = zr * zr - zi * zi + x;
I = 2 * zr * zi + y;
zr = R;
zi = I;
if (k >= 400) break;
} while (R * R + I * I < absZ);
if (k == 400) tft.drawPixel(X0 + i, Y0 - j, TFT_BLACK);
else tft.drawPixel(X0 + i, Y0 - j, ColorSel(k));
}
}
}
tft.setCursor(0, 0);
tft.printf("view width=%1.2f", view);
tft.setCursor(0, 8);
tft.printf("Offset x=%2.4f, y=%2.4f", idouX, idouY);
}
int repeat = 256 * 2;
int yxe[10][3] = {
{105,80,130},
{70,60,160},
{ 54, 164, 230},
{ 50, 14, 50},
{ 50, 150, 220},
{ -4, -98, 2},
{-10,-100, 0},
{-49,-114, -9}
};
void Dots(int n) {
for (int j = -Y0 + 160 + yxe[n][0]; j > -Y0 + 160 + yxe[n][0] - 1; j--) { //259,0
HdotLine(160 - j, TFT_WHITE);
tft.fillRect(0, 312, 480, 319, TFT_BLACK);
y = j * di + idouY;
for (int i = -X0 + 240 + yxe[n][1]; i < -X0 + 240 + yxe[n][2]; i++) { // 20,70
tft.drawLine(i + X0, 312, i + X0, 319, TFT_WHITE);
tft.setCursor(0, 16);
tft.printf("I=%2.4f", y);
x = i * di + idouX;
if(i == -X0 + 240 + yxe[n][1]){
tft.setCursor(0, 24);
tft.printf("R=%2.4f ~ ", x);
}
if( i == -X0 + 240 + yxe[n][2]-1){
tft.setCursor(72, 24);
tft.printf("%2.4f", x);
}
zr = 0.0;
zi = 0.0;
k = -1;
do {
k++;
R = zr * zr - zi * zi + x;
I = 2 * zr * zi + y;
if (k > 0) {
tft.drawPixel(X0 + (int)((R - idouX) / di), 320 - Y0 - (int)((I - idouY) / di), ColorSel(k + 90));
delay(1);
}
if (k >= repeat) break;
zr = R;
zi = I;
} while (R * R + I * I < absZ);
}
}
tft.setCursor(0, 312);
tft.printf("END. Hit Any Key.");
}
void grid(void) {
tft.drawLine(X0, 0, X0, tft.height(), TFT_DARKGREY);
tft.drawLine(X0 + 200, 0, X0 + 200, tft.height(), TFT_DARKGREY);
tft.drawLine(X0 + 100, 0, X0 + 100, tft.height(), TFT_DARKGREY);
tft.drawLine(X0 - 100, 36, X0 - 100, tft.height(), TFT_DARKGREY);
tft.drawLine(X0 - 200, 36, X0 - 200, tft.height(), TFT_DARKGREY);
tft.drawLine(0, Y0, tft.width(), Y0, TFT_DARKGREY);
tft.drawLine(0, Y0 + 100, tft.width(), Y0 + 100, TFT_DARKGREY);
tft.drawLine(0, Y0 - 100, tft.width(), Y0 - 100, TFT_DARKGREY);
tft.drawString("0", X0 + 2, 0);
HdotLine(Y0+idouY/di, TFT_MAGENTA);
VdotLine(X0-idouX/di, TFT_MAGENTA);
}
void HdotLine(int n, uint16_t c) {
for (int i = 0; i < 480; i++) {
if (i % 3 == 0) tft.drawPixel(i, n, c);
}
}
void VdotLine(int n, uint16_t c) {
for (int i = 0; i < 320; i++) {
if (i % 3 == 0) tft.drawPixel(n, i, c);
}
}
uint16_t ColorSel(int c) {
if (c < 9) {
re = 50 + 2 ^ c;
gr = 50 + 2 ^ c;
bl = 50 + 2 ^ c;
return tft.color565(re, gr, bl);
} else {
re = 128 + 126 * cos(0.75*toRad*c );
gr = 128 + 126 * cos(0.75*toRad*(c + 120));
bl = 128 + 126 * cos(0.75*toRad*(c + 240));
return tft.color565(re, gr, bl);
}
}
void loop(void) {
tft.fillScreen(TFT_BLACK);
Mandelbrot();
grid();
Dots(r % 8);
while(digitalRead(inputPin) == LOW){// while connected to GND
}
val = digitalRead(inputPin);
delay(1000);
r++;
}