0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マンデルブロー集合を生成するときの点列を視覚化してみた(続編)

Last updated at Posted at 2023-08-16

 8月6日の投稿の続編です。

前回はILI9488ディスプレイモジュールをタッチすることで点列を選択し視覚化しました。しかし、それでは選択に限界があるので、複素平面上をスキャンしてどんな点列が見られるか調べてみました。
dots00.jpg
 上の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++;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?