#include <U8g2lib.h>  // Biblioteca U8g2

// Inicialização do display (I2C)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

int points[8][2]; // Oito pontos 2D para o cubo

// Oito pontos 3D definidos para o cubo
int orig_points[8][3] = {
  { -1, -1,  1 },
  {  1, -1,  1 },
  {  1,  1,  1 },
  { -1,  1,  1 },
  { -1, -1, -1 },
  {  1, -1, -1 },
  {  1,  1, -1 },
  { -1,  1, -1 }
};

float rotated_3d_points[8][3]; // Oito pontos 3D rotacionados em torno do eixo Y
float angle_deg = 0.0;         // Rotação em torno do eixo Y
float z_offset = -4.0;         // Deslocamento no eixo Z
float cube_size = 32.0;        // Tamanho do cubo (multiplicador)
float time_frame = 0;          // Valor de tempo sempre crescente

// Variáveis para movimentação do cubo
float pos_x = 64;              // Posição X inicial do cubo (meio da tela)
float pos_y = 32;              // Posição Y inicial do cubo (meio da tela)
float vel_x = 1.5;             // Velocidade X do cubo
float vel_y = 1.0;             // Velocidade Y do cubo

void setup() {
  u8g2.begin();                // Inicializa o display
  u8g2.setDrawColor(1);        // Define a cor para branco
}

void loop() {
  time_frame++;                // Aumenta o valor do tempo em 1 frame
  angle_deg += 5;              // Aumenta o ângulo de rotação
  if (angle_deg >= 360) {
    angle_deg = 0;
  }

  // Movimentação do cubo
  pos_x += vel_x;
  pos_y += vel_y;

  // Verifica colisão com as bordas e corrige a posição se necessário
  if (pos_x - (cube_size / 2) + 4 <= 0 || pos_x + (cube_size / 2) + 4 >= 128) {
    vel_x = -vel_x; // Inverte a direção no eixo X
    pos_x = constrain(pos_x, (cube_size / 2), 128 - (cube_size / 2));
  }

  if (pos_y - (cube_size / 2) + 4 <= 0 || pos_y + (cube_size / 2) >= 64) {
    vel_y = -vel_y; // Inverte a direção no eixo Y
    pos_y = constrain(pos_y, cube_size / 2, 64 - (cube_size / 2));
  }

  // Calcula os pontos
  for (int i = 0; i < 8; i++) {
    // Rotaciona os pontos 3D em torno do eixo Y (rotacionando as posições X e Z)
    rotated_3d_points[i][0] = orig_points[i][0]
                            * cos(radians(angle_deg))
                            - orig_points[i][2]
                            * sin(radians(angle_deg));
    rotated_3d_points[i][1] = orig_points[i][1];
    rotated_3d_points[i][2] = orig_points[i][0]
                            * sin(radians(angle_deg))
                            + orig_points[i][2]
                            * cos(radians(angle_deg))
                            + z_offset;

    // Projeta os pontos 3D no espaço 2D com divisão de perspectiva - 2D x = x/z, 2D y = y/z
    points[i][0] = round(pos_x + rotated_3d_points[i][0] / rotated_3d_points[i][2] * cube_size);
    points[i][1] = round(pos_y + rotated_3d_points[i][1] / rotated_3d_points[i][2] * cube_size);
  }

  u8g2.clearBuffer(); // Limpa o buffer do display

  // Conecta as linhas entre os pontos individuais para formar o cubo
  u8g2.drawLine(points[0][0], points[0][1], points[1][0], points[1][1]);  // Conecta pontos 0-1
  u8g2.drawLine(points[1][0], points[1][1], points[2][0], points[2][1]);  // Conecta pontos 1-2
  u8g2.drawLine(points[2][0], points[2][1], points[3][0], points[3][1]);  // Conecta pontos 2-3
  u8g2.drawLine(points[3][0], points[3][1], points[0][0], points[0][1]);  // Conecta pontos 3-0

  u8g2.drawLine(points[4][0], points[4][1], points[5][0], points[5][1]);  // Conecta pontos 4-5
  u8g2.drawLine(points[5][0], points[5][1], points[6][0], points[6][1]);  // Conecta pontos 5-6
  u8g2.drawLine(points[6][0], points[6][1], points[7][0], points[7][1]);  // Conecta pontos 6-7
  u8g2.drawLine(points[7][0], points[7][1], points[4][0], points[4][1]);  // Conecta pontos 7-4

  u8g2.drawLine(points[0][0], points[0][1], points[4][0], points[4][1]);  // Conecta pontos 0-4
  u8g2.drawLine(points[1][0], points[1][1], points[5][0], points[5][1]);  // Conecta pontos 1-5
  u8g2.drawLine(points[2][0], points[2][1], points[6][0], points[6][1]);  // Conecta pontos 2-6
  u8g2.drawLine(points[3][0], points[3][1], points[7][0], points[7][1]);  // Conecta pontos 3-7

  u8g2.sendBuffer(); // Envia o buffer para o display
  delay(10);         // Pequeno atraso para suavizar a animação
}