🌌 Π§Π°ΡΡ‚ΡŒ 43: 4D-визуализация Π½Π° LED-ΠΊΡƒΠ±Π΅

ΠœΡ‹ создадим Ρ‡Π΅Ρ‚Ρ‹Ρ€Π΅Ρ…ΠΌΠ΅Ρ€Π½Ρ‹ΠΉ LED-ΠΊΡƒΠ± (4D-Π³ΠΈΠΏΠ΅Ρ€ΠΊΡƒΠ±), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ проСцируСтся Π² 3D-пространство!

4D-Π³ΠΈΠΏΠ΅Ρ€ΠΊΡƒΠ± (ВСссСракт):

#include <avr/pgmspace.h>

#define CUBE_SIZE 4
#define LED_COUNT (CUBE_SIZE * CUBE_SIZE * CUBE_SIZE)
#define FRAME_BUFFER_SIZE (LED_COUNT / 8)

// ΠšΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ 4D-Π³ΠΈΠΏΠ΅Ρ€ΠΊΡƒΠ±Π° (16 Π²Π΅Ρ€ΡˆΠΈΠ½)
const int8_t tesseract[16][4] PROGMEM = {
    {-1, -1, -1, -1},
    { 1, -1, -1, -1},
    {-1,  1, -1, -1},
    { 1,  1, -1, -1},
    {-1, -1,  1, -1},
    { 1, -1,  1, -1},
    {-1,  1,  1, -1},
    { 1,  1,  1, -1},
    {-1, -1, -1,  1},
    { 1, -1, -1,  1},
    {-1,  1, -1,  1},
    { 1,  1, -1,  1},
    {-1, -1,  1,  1},
    { 1, -1,  1,  1},
    {-1,  1,  1,  1},
    { 1,  1,  1,  1}
};

// Π Π΅Π±Ρ€Π° Π³ΠΈΠΏΠ΅Ρ€ΠΊΡƒΠ±Π° (32 Ρ€Π΅Π±Ρ€Π°)
const uint8_t edges[32][2] PROGMEM = {
    {0,1}, {0,2}, {0,4}, {0,8},
    {1,3}, {1,5}, {1,9},
    {2,3}, {2,6}, {2,10},
    {3,7}, {3,11},
    {4,5}, {4,6}, {4,12},
    {5,7}, {5,13},
    {6,7}, {6,14},
    {7,15},
    {8,9}, {8,10}, {8,12},
    {9,11}, {9,13},
    {10,11}, {10,14},
    {11,15},
    {12,13}, {12,14},
    {13,15},
    {14,15}
};

// Π‘ΡƒΡ„Π΅Ρ€ ΠΊΠ°Π΄Ρ€Π°
uint8_t frameBuffer[FRAME_BUFFER_SIZE];

// 4D -> 3D проСкция (с использованиСм ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Ρ‹ вращСния)
void project4Dto3D(int8_t x, int8_t y, int8_t z, int8_t w, 
                   uint16_t angleXY, uint16_t angleZW,
                   int8_t* outX, int8_t* outY, int8_t* outZ) {
    // Π’Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ Π² плоскости XY
    int16_t cosA = pgm_read_word(&cosTable[angleXY]);
    int16_t sinA = pgm_read_word(&sinTable[angleXY]);
    int16_t rx = (x * cosA - y * sinA) / 256;
    int16_t ry = (x * sinA + y * cosA) / 256;
    
    // Π’Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ Π² плоскости ZW
    int16_t cosB = pgm_read_word(&cosTable[angleZW]);
    int16_t sinB = pgm_read_word(&sinTable[angleZW]);
    int16_t rz = (z * cosB - w * sinB) / 256;
    int16_t rw = (z * sinB + w * cosB) / 256;
    
    // ΠŸΠ΅Ρ€ΡΠΏΠ΅ΠΊΡ‚ΠΈΠ²Π½Π°Ρ проСкция 4D -> 3D
    int16_t scale = 64 / (rw + 64 + 1);
    *outX = 2 + (rx * scale) / 32;
    *outY = 2 + (ry * scale) / 32;
    *outZ = 2 + (rz * scale) / 32;
}

// Π Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³ Π³ΠΈΠΏΠ΅Ρ€ΠΊΡƒΠ±Π°
void renderTesseract(uint16_t angleXY, uint16_t angleZW) {
    // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ Π±ΡƒΡ„Π΅Ρ€
    memset(frameBuffer, 0, sizeof(frameBuffer));
    
    // РисуСм Ρ€Π΅Π±Ρ€Π°
    for (uint8_t i = 0; i < 32; i++) {
        uint8_t idx1 = pgm_read_byte(&edges[i][0]);
        uint8_t idx2 = pgm_read_byte(&edges[i][1]);
        
        int8_t x1 = pgm_read_byte(&tesseract[idx1][0]);
        int8_t y1 = pgm_read_byte(&tesseract[idx1][1]);
        int8_t z1 = pgm_read_byte(&tesseract[idx1][2]);
        int8_t w1 = pgm_read_byte(&tesseract[idx1][3]);
        
        int8_t x2 = pgm_read_byte(&tesseract[idx2][0]);
        int8_t y2 = pgm_read_byte(&tesseract[idx2][1]);
        int8_t z2 = pgm_read_byte(&tesseract[idx2][2]);
        int8_t w2 = pgm_read_byte(&tesseract[idx2][3]);
        
        int8_t px1, py1, pz1, px2, py2, pz2;
        project4Dto3D(x1, y1, z1, w1, angleXY, angleZW, &px1, &py1, &pz1);
        project4Dto3D(x2, y2, z2, w2, angleXY, angleZW, &px2, &py2, &pz2);
        
        // Алгоритм Π‘Ρ€Π΅Π·Π΅Π½Ρ…Π΅ΠΌΠ° для рисования Π»ΠΈΠ½ΠΈΠΈ Π² 3D
        drawLine3D(px1, py1, pz1, px2, py2, pz2);
    }
}

// Алгоритм Π‘Ρ€Π΅Π·Π΅Π½Ρ…Π΅ΠΌΠ° Π² 3D
void drawLine3D(int8_t x1, int8_t y1, int8_t z1, int8_t x2, int8_t y2, int8_t z2) {
    int8_t dx = abs(x2 - x1);
    int8_t dy = abs(y2 - y1);
    int8_t dz = abs(z2 - z1);
    
    int8_t sx = (x1 < x2) ? 1 : -1;
    int8_t sy = (y1 < y2) ? 1 : -1;
    int8_t sz = (z1 < z2) ? 1 : -1;
    
    int8_t err = dx - dy - dz;
    
    while (true) {
        // УстанавливаСм пиксСль
        if (x1 >= 0 && x1 < CUBE_SIZE && 
            y1 >= 0 && y1 < CUBE_SIZE && 
            z1 >= 0 && z1 < CUBE_SIZE) {
            uint16_t idx = (z1 * CUBE_SIZE * CUBE_SIZE + y1 * CUBE_SIZE + x1) / 8;
            uint8_t bit = x1 % 8;
            frameBuffer[idx] |= (1 << bit);
        }
        
        if (x1 == x2 && y1 == y2 && z1 == z2) break;
        
        int8_t e2 = 2 * err;
        if (e2 > -dz) {
            err -= dy + dz;
            x1 += sx;
        }
        if (e2 < dx) {
            err += dx + dy;
            z1 += sz;
        }
        if (e2 > -dx) {
            err += dx - dz;
            y1 += sy;
        }
    }
}

// ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° ΠΊΠ°Π΄Ρ€Π° Π½Π° LED-ΠΊΡƒΠ±
void sendFrame() {
    for (uint16_t i = 0; i < FRAME_BUFFER_SIZE; i++) {
        // ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ Ρ‡Π΅Ρ€Π΅Π· SPI Π½Π° MAX7219
        SPI.transfer(frameBuffer[i]);
    }
}

void setup() {
    SPI.begin();
    SPI.setClockDivider(SPI_CLOCK_DIV2);
    
    // Настройка Ρ‚Π°ΠΉΠΌΠ΅Ρ€Π° для Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠΈ
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS10);
    OCR1A = 15999;  // 1 ΠΊΠ“Ρ†
    TIMSK1 = (1 << OCIE1A);
    
    sei();
}

volatile uint16_t angleXY = 0;
volatile uint16_t angleZW = 0;

ISR(TIMER1_COMPA_vect) {
    angleXY = (angleXY + 1) % 360;
    angleZW = (angleZW + 2) % 360;
    
    renderTesseract(angleXY, angleZW);
    sendFrame();
}

void loop() {
    // Π’Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ Π³ΠΈΠΏΠ΅Ρ€ΠΊΡƒΠ±Π° Π² 4D
    // ВсС дСлаСтся Π² прСрываниях
    delay(1000);
}

Π’Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΡ‚ΡŒΡΡ

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’Π°Ρˆ адрСс email Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Ρ‹ *