macsbug

M5StickC 3D COLOR CUBE

leave a comment »

M5StickC に 3D COLOR CUBE を表示しました。      2019.05.20

M5StickC の表示は 0.96″ 80×160 IPS (In Plane Switching) SPI ST7735S Display です。
色は 65K フルカラー ( 16bit color )です。
上下左右178度の広視野角、どの位置で見ても色の変化は ほとんどありません。

斜めの視野:明るく綺麗で見やすいです。


.
作成:
記事の最下位にある スケッチを Arduino IDE で ボード選択 M5StcicK-C で書き込みます。
_ スケッチ詳細:
_ 1. CUBE のサイズ:9行目の #define U 70 // size of cube で 数値を変更します。
_ 2. 回転速度:251 行目の delay(50); // Rotational speed で 50msecです。
_   回転速度の変更:delay の数値を変更してください。
_ 3. SOLID表示と WIREFRAME表示:選択は 5行目の #define WIREFRAME を
_   コメント(//) を入れるか 入れないかで 表示が変わります。
_ 4. M5Stack 用に 使用する時は // In the case of M5Stack と書かれているコメントを
_   削除してください。#include M5StickC.h は コメントアウトします。
_   237行目は SD_UPDATE 用です。修正は 合計 4箇所です。


.
参考:
M5StickC docs:概要(電源操作、サポート ボーレート、ピンマップ)
github:M5StickC Library:M5StickC ライブラリー
blog:3D CUBE with ESP8266 and OLED:2016.01.16:モノクロの立方体がグイグリ回ります。
blog:I2C OLED 128×64 SSD1306 in ESP8266:2016.03.20:ESP8266 と SSD1306。
blog:Using the WeMos I2C OLED 64×48:2016.04.01:WeMos I2C OLED 64×48 の使用例。
blog:Part 2 of the FM radio in the ESP8266:2016.04.06:cubeをradio displayに使用。
blog:Using the TFT LCD display in the ESP8266:2016.04.16:ESP8266 + TFT SPI ILI9341
blog:ESP32 Adapter Board and TFT Display:2017.02.11:ESP32 + 1.44″ TFT v2.1
LilyGO/TTGO-TS: TTGO-TS/ESP32_1.44/ESP32_1.44.ino:2018.08.11 確認:TTGOに使用される。
blog:Run IPS SPI display on M5Stack:2019.03.24:65K フルカラーです。
Adafruit:Adafruit Mini TFT – 0.96″ 160×80


.
M5Stack Basic:M5Stack Basic用に記載し表示しました。


.
感想:
IPSカラーディスプレーに CUBE を表示したく 作成しました。
M5StickCの動作確認やデモに 良いかと思います。
0.96 inch 128 x64 OLED のデモと同じく CUBE を表示しました。
立方体を2次元表示する為に 3つの四辺形で構成されます。
四辺形は 三角形を3つ抽画しています。

メモ:
_ チラツキ:スケッチに工夫が必要かと思います。
_  CUBE 抽画に M5.Lcd.drawTriangle を 9回(3×3)使用し時間がかかっています。
_  252行目の M5.Lcd.fillRect(x_min,y_min,x_max-x_min+3,y_max-y_min+3,BLACK);
_  は CUBE 全体を消し 時間がかかっています。
_ M5StickC:動作しなくなる時がある。
_  1. 突然動作せず、画面が黒いままになる。
_  2. USBは認識し書込みも出来るが、動作しない。set_up が動作していない。
_   起動ボタンを押したり USB CABLE を抜き差ししたり すると動作する事がある。
_   原因不明ですので 壊れたとか あわてない事です。
_  3. 電源管理チップ AXP192 の設定かと思われます。
_   jimmy Lai氏の情報:
_    There is problem from the setting for axp192, need to set a higher volt level for shutdown.
_    The early version test firmware’s bug.


.
スケッチ:M5StickC 3D COLOR CUBE : 2019.05.20 macsbug

// M5StickC 3D COLOR CUBE : 2019.05.20 macsbug
#include <M5StickC.h>          // In the case of M5StickC
// #include <M5Stack.h>        // In the case of M5Stack
// #include "M5StackUpdater.h" // In the case of M5Stack
//#define WIREFRAME   // WIREFRAME or SOLID Display
#pragma GCC optimize ("O3")
struct p3d{ int16_t x, y, z;};
struct surface{ uint8_t p[4]; int16_t z;};
struct p2d{ int16_t x, y; unsigned is_visible;};
#define U  70         // size of cube
#define ZS U          // eye to screen distance (fixed)

struct p3d cube[8] ={ // cube edge length is 2*U
  { -U, -U,  U },
  {  U, -U,  U },
  {  U, -U, -U },
  { -U, -U, -U },
  { -U,  U,  U },
  {  U,  U,  U },
  {  U,  U, -U },
  { -U,  U, -U },
};

struct surface s[6] = {// define the surfaces
  { {0, 1, 2, 3}, 0 }, // bottom
  { {4, 5, 6, 7}, 0 }, // top
  { {0, 1, 5, 4}, 0 }, // back
  { {3, 7, 6, 2}, 0 }, // front
  { {1, 2, 6, 5}, 0 }, // right
  { {0, 3, 7, 4}, 0 }, // left
};

struct p3d cube2[8];   // Define the structure of the cube,
struct p2d cube_[8];   // calculation will be done there

int16_t x_min, x_max;  // will contain a rectangle border
int16_t y_min, y_max;  //  of the box projection into 2D plane.

const int16_t sin_tbl[65] = {
       0,  1606,  3196,  4756,  6270,  7723,  9102, 10394, 
   11585, 12665, 13623, 14449, 15137, 15679, 16069, 16305,
   16384, 16305, 16069, 15679, 15137, 14449, 13623, 12665,
   11585, 10394,  9102,  7723,  6270,  4756,  3196,  1606, 
       0, -1605, -3195, -4755, -6269, -7722, -9101,-10393,
  -11584,-12664,-13622,-14448,-15136,-15678,-16068,-16304,
  -16383,-16304,-16068,-15678,-15136,-14448,-13622,-12664,
  -11584,-10393, -9101, -7722, -6269, -4755, -3195, -1605, 
       0
};

const int16_t cos_tbl[65] = {
   16384, 16305, 16069, 15679, 15137, 14449, 13623, 12665, 
   11585, 10394,  9102,  7723,  6270,  4756,  3196,  1606,
       0, -1605, -3195, -4755, -6269, -7722, -9101,-10393,
  -11584,-12664,-13622,-14448,-15136,-15678,-16068,-16304,
  -16383,-16304,-16068,-15678,-15136,-14448,-13622,-12664,
  -11584,-10393, -9101, -7722, -6269, -4755, -3195, -1605,
       0,  1606,  3196,  4756,  6270,  7723,  9102, 10394,
   11585, 12665, 13623, 14449, 15137, 15679, 16069, 16305,
   16384
};

void copy_cube(void){
  uint8_t i;
  for (i = 0; i < 8; i++){
    cube2[i] = cube[i];
  }
}

void rotate_cube_x(uint16_t w){
  uint8_t i;
  int16_t y, z;
  for (i = 0; i < 8; i++){
    y = ( (int32_t)cube2[i].y * (int32_t)cos_tbl[w] + 
          (int32_t)cube2[i].z * (int32_t)sin_tbl[w]) >> 14;
    z = (-(int32_t)cube2[i].y * (int32_t)sin_tbl[w] + 
          (int32_t)cube2[i].z * (int32_t)cos_tbl[w]) >> 14;
    cube2[i].y = y;
    cube2[i].z = z;
  }
}

void rotate_cube_y(uint16_t w){
  uint8_t i;
  int16_t x, z;
  for (i = 0; i < 8; i++){
    x = ( (int32_t)cube2[i].x * (int32_t)cos_tbl[w] +
          (int32_t)cube2[i].z * (int32_t)sin_tbl[w]) >> 14;
    z = (-(int32_t)cube2[i].x * (int32_t)sin_tbl[w] + 
          (int32_t)cube2[i].z * (int32_t)cos_tbl[w]) >> 14;
    //printf("%d: %d %d --> %d %d\n",i,cube2[i].x,cube2[i].z,x,z);
    cube2[i].x = x;
    cube2[i].z = z;
  }
}

void trans_cube(uint16_t z){
  uint8_t i;
  for (i = 0; i < 8; i++){
    cube2[i].z += z;
  }
}

void reset_min_max(void){
  x_min =  0x07fff;
  y_min =  0x07fff;
  x_max = -0x07fff;
  y_max = -0x07fff;
}

// calculate xs and ys from a 3d value
void convert_3d_to_2d(struct p3d *p3, struct p2d *p2){
  int ws = M5.Lcd.width();
  int hs = M5.Lcd.height();
  int32_t t;
  p2->is_visible = 1;
  if (p3->z >= ZS){
    t = ZS;
    t *= p3->x;
    t <<= 1;
    t /= p3->z;
    if (t >= -(ws/2) && t <= (ws/2) - 1){
      t += (ws/2);
      p2->x = t;
      if (x_min > t) x_min = t;
      if (x_max < t) x_max = t;
      t = ZS;
      t *= p3->y;
      t <<= 1;
      t /= p3->z;
      if (t >= -(hs/2) && t <= (hs/2) - 1){
        t += (hs/2);
        p2->y = t;
        if (y_min > t) y_min = t;
        if (y_max < t) y_max = t;
      }else{
        p2->is_visible = 0;
      }
    }else{
      p2->is_visible = 0;
    }
  }else{
    p2->is_visible = 0;
  }
}

void convert_cube(void){
  uint8_t i;
  reset_min_max();
  for (i = 0; i < 8; i++){
    convert_3d_to_2d(cube2 + i, cube_ + i);
  }
}

void calculate_z(void){
  uint8_t  i, j;
  uint16_t z;
  for (i = 0; i < 6; i++){
    z = 0;
    for (j = 0; j < 4; j++){
      z += cube2[s[i].p[j]].z;
    }
    z /= 4;
    s[i].z = z;
    //printf("%d: z=%d\n", i, z);
  }
}

void draw_cube(void){
  uint8_t i, ii;
  uint8_t skip_cnt = 3; // first 3 surfaces are invisible
  int16_t z, z_upper;
  uint16_t color;
  z_upper = 32767;
  for (;;){
    ii = 6;
    z = -32767;
    for (i = 0; i < 6; i++){
      if (s[i].z <= z_upper){
        if (z < s[i].z){
          z = s[i].z;
          ii = i;
        }
      }
    }
    if (ii >= 6) break;
    z_upper = s[ii].z;
    s[ii].z++;
    if (skip_cnt > 0){
      skip_cnt--;
    }else{
      color = M5.Lcd.color565((uint8_t)((( ii + 1) &  1)      * 255),
                              (uint8_t)((((ii + 1) >> 1) & 1) * 255),
                              (uint8_t)((((ii + 1) >> 2) & 1) * 255));
      #if defined(WIREFRAME) // WIREFRAME
        M5.Lcd.drawTriangle(cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            color);
        M5.Lcd.drawTriangle(cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            color);
        M5.Lcd.drawTriangle(cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            color);
      #else // SOLID
        M5.Lcd.fillTriangle(cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            color);
        M5.Lcd.fillTriangle(cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            color);
        M5.Lcd.fillTriangle(cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            color);
      #endif
    }
  }
}

void calc_and_draw(int16_t w, int16_t v){
  copy_cube();
  rotate_cube_y(w);
  rotate_cube_x(v);
  trans_cube(U * 8);
  convert_cube();
  calculate_z();
  draw_cube();
}

void setup(void){ 
  M5.begin();
//Wire.begin();if(digitalRead(39)==0){updateFromFS(SD);ESP.restart();}//SD UPdate
  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setRotation(3);
}

int16_t w = 0;
int16_t v = 0;

void loop(void){
  calc_and_draw(w, v >> 3);
  v += 3;
  v &= 511;
  w++;
  w &= 63;
  delay(50); // Rotational speed
  M5.Lcd.fillRect(x_min,y_min,x_max-x_min+3,y_max-y_min+3,BLACK);
}

———————————————————————————————–
M5StickC , 3D , COLOR , CUBE , IPS ,

Written by macsbug

5月 20, 2019 @ 10:30 pm

カテゴリー: M5STACK

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。