PixelFlow

PixelFlow is a series of advanced particle simulation applications that demonstrate fluid dynamics, physics-based particle systems, and sensor integration. The PixelFlow family consists of three variants, each showcasing different levels of complexity and platform integration.

Overview

The PixelFlow series includes:

  • PixelFlow - IMU-integrated fluid simulation for Raspberry Pi with hardware sensor input

  • PixelFlow2 - Enhanced IMU-based particle system with improved physics and lifetime management

  • PixelFlow3 - Cross-platform particle system with joystick controls (no IMU dependency)

All variants feature:

  • 3D Particle Physics - Realistic particle movement with velocity and acceleration

  • Surface Dynamics - Particles that flow along cube surfaces with edge transitions

  • Dynamic Color Systems - Automatic color cycling with randomized variations

  • Fade Effects - Configurable particle persistence and trail rendering

  • Efficient Memory Management - Smart pointer-based particle lifecycle

Application Variants

PixelFlow (Original) - IMU Integration

Platform: Raspberry Pi only (requires MPU6050 IMU sensor)

Key Features: * IMU Sensor Integration - Uses MPU6050 accelerometer for motion-responsive particles * Surface-Aware Spawning - Particles spawn on cube faces based on IMU orientation * Gravity Simulation - Real-world gravity affects particle movement via IMU input * High Particle Density - Up to 60 particles per frame for dense fluid effects

class PixelFlow : public CubeApplication {
private:
    Mpu6050 Imu;                    // MPU6050 IMU sensor integration

public:
    PixelFlow() : CubeApplication(40) {}

    bool loop() override {
        fade(0.85);                 // Particle persistence

        // Spawn 60 particles per frame
        for (int i = 0; i < 60; i++) {
            // Determine spawn surface from IMU
            auto imuPoint = Imu.getCubeAccIntersect();

            // Set initial velocity based on surface orientation
            Vector3f startSpeed = calculateSurfaceVelocity(imuPoint);

            rdrops.push_back(std::make_shared<Drop>(
                maxPos, imuPoint.cast<float>(), startSpeed,
                Vector3f(0,0,0), currentColor));
        }

        // Apply IMU-based acceleration to existing particles
        for(auto drop : rdrops) {
            drop->acceleration(Imu.getAcceleration() *
                (-0.1f + random_variation));
            drop->step();
            setPixel3D(drop->iPosition(), drop->color());
        }

        render();
        return true;
    }
};

PixelFlow2 - Enhanced IMU System

Platform: Raspberry Pi only (requires MPU6050 IMU sensor)

Key Features: * Improved Particle Lifetime - Optimized step count management (180 steps vs 260) * Enhanced Physics Model - Refined acceleration and velocity calculations * Better Surface Dynamics - Improved particle flow along cube surfaces * Optimized Performance - More efficient particle spawning and management

class PixelFlow2 : public CubeApplication {
private:
    class Drop : public SurfaceParticle {
        int stepCount;                // Particle lifetime counter
        bool rdyDelete_;              // Deletion flag

    public:
        void step() override {
            SurfaceParticle::step();  // Advanced surface physics

            if (stepCount > 180) {    // Optimized lifetime
                rdyDelete_ = true;
            }
            stepCount++;
        }
    };
};

PixelFlow3 - Cross-Platform Interactive

Platform: All platforms (desktop and Raspberry Pi)

Key Features: * Joystick Integration - Interactive controls without IMU dependency * Cross-Platform Compatibility - Runs on any system with matrixserver * Simplified Physics - No IMU dependency, uses programmed particle behavior * Interactive Controls - Real-time parameter adjustment via gamepad

class PixelFlow : public CubeApplication {
private:
    std::vector<Joystick*> joysticks;

public:
    PixelFlow() : CubeApplication(40) {
        // Initialize up to 4 joysticks for interaction
        for(int i = 0; i < 4; i++) {
            joysticks.push_back(new Joystick(i));
        }
    }

    bool loop() override {
        static bool isPaused = false;

        // Process joystick input
        for (auto joystick : joysticks) {
            if (joystick->getButtonPress(0)) {
                counterColChange++;    // Change color scheme
            }
            if (joystick->getButtonPress(3)) {
                isPaused = !isPaused; // Toggle pause
            }
            joystick->clearAllButtonPresses();
        }

        if (isPaused) return true;

        fade(0.85);

        // Create 30 particles with random motion
        for (int i = 0; i < 30; i++) {
            float randAngle = rand() % 360;
            float vx = 0.5 * cos(randAngle * PI / 180);
            float vy = 0.5 * sin(randAngle * PI / 180);

            rdrops.push_back(std::make_shared<Drop>(
                maxPos, centerPosition, Vector3f(vx, vy, 0),
                Vector3f(0,0,0), currentColor));
        }

        // Update particles
        for(auto drop : rdrops) {
            drop->step();
            setPixel3D(drop->iPosition(), drop->color());
        }

        cleanupParticles();
        render();
        return true;
    }
};

Particle System Architecture

All PixelFlow variants share a sophisticated particle system hierarchy:

Particle Base Class

The foundation class providing basic physics:

class PixelFlow::Particle {
protected:
    Vector3f position_;             // Current position
    Vector3f velocity_;             // Current velocity
    Vector3f acceleration_;         // Current acceleration
    Color color_;                   // Particle color

public:
    virtual void step() {
        accelerate();               // Apply acceleration
        move();                     // Update position
    }

    void move() {
        position_ += velocity_;     // Basic Euler integration
    }

    void accelerate() {
        velocity_ += acceleration_; // Update velocity
    }

    Vector3i iPosition() {
        return Vector3i(round(position_[0]),
                       round(position_[1]),
                       round(position_[2]));
    }
};

SurfaceParticle Class

Specialized particle that flows along cube surfaces:

class PixelFlow::SurfaceParticle : public Particle {
protected:
    Vector3i maxPosition;           // Cube boundaries
    EdgeNumber lastEdge;            // Last cube edge
    Vector3i lastIPosition;         // Previous position

public:
    void step() override {
        accelerateOnSurface();      // Surface-aware acceleration
        move();                     // Update position
        warp();                     // Handle edge transitions
    }

    void accelerateOnSurface() {
        // Apply acceleration based on current surface
        switch(getScreenNumber(iPosition())) {
            case top: case bottom:
                velocity_[0] += acceleration_[0];
                velocity_[1] += acceleration_[1];
                break;
            case front: case back:
                velocity_[0] += acceleration_[0];
                velocity_[2] += acceleration_[2];
                break;
            case left: case right:
                velocity_[1] += acceleration_[1];
                velocity_[2] += acceleration_[2];
                break;
        }
    }

    void warp() {
        // Handle edge transitions and velocity mapping
        Vector3i currentPosition = iPosition();
        EdgeNumber currentEdge = getEdgeNumber(currentPosition);

        if(currentEdge != anyEdge && currentEdge != lastEdge) {
            // Map velocity components across edges
            switch (currentEdge) {
                case topLeft: case topRight:
                case bottomRight: case bottomLeft:
                    std::swap(velocity_[2], velocity_[0]);
                    break;
                case topFront: case topBack:
                case bottomBack: case bottomFront:
                    std::swap(velocity_[2], velocity_[1]);
                    break;
                case frontRight: case backLeft:
                case leftFront: case rightBack:
                    std::swap(velocity_[0], velocity_[1]);
                    break;
            }

            // Reflect at boundaries if needed
            handleBoundaryReflection(currentPosition);
        }

        lastEdge = currentEdge;
        lastIPosition = currentPosition;
    }
};

Drop Class Implementation

The final particle implementation with lifecycle management:

class PixelFlow::Drop : public SurfaceParticle {
private:
    float vxOld_, vyOld_;           // Stored velocities for surface sliding
    int stepCount;                 // Lifetime counter
    bool rdyDelete_;               // Cleanup flag

public:
    Drop(Vector3i maxPos, Vector3f pos, Vector3f vel,
         Vector3f accel, Color col)
        : SurfaceParticle(maxPos, pos, vel, accel, col) {
        stepCount = 0;
        rdyDelete_ = false;
        vxOld_ = vyOld_ = 0.0f;
    }

    void step() override {
        SurfaceParticle::step();    // Advanced surface physics

        // Lifetime management varies by variant:
        // PixelFlow: 260 steps
        // PixelFlow2: 180 steps
        // PixelFlow3: Similar to MatrixRain
        if (stepCount > getMaxLifetime()) {
            rdyDelete_ = true;
        }
        stepCount++;
    }

    bool getRdyDelete() { return rdyDelete_; }
};

Color System

Dynamic Color Cycling

All PixelFlow variants feature sophisticated color cycling:

void updateColorScheme(int colorIndex) {
    switch (colorIndex % 6) {
        case 0:  // Blue-green spectrum
            col1.r(0);
            col1.g(255 - rand() % 100);
            col1.b(255 - rand() % 200);
            break;

        case 1:  // Red-blue spectrum
            col1.g(0);
            col1.b(255 - rand() % 100);
            col1.r(255 - rand() % 200);
            break;

        case 2:  // Green-red spectrum
            col1.b(0);
            col1.r(255 - rand() % 100);
            col1.g(255 - rand() % 200);
            break;

        case 3:  // Pure blue variations
            col1.r(0); col1.g(0);
            col1.b(255 - rand() % 200);
            break;

        case 4:  // Pure red variations
            col1.g(0); col1.b(0);
            col1.r(255 - rand() % 200);
            break;

        case 5:  // Pure green variations
            col1.r(0); col1.b(0);
            col1.g(255 - rand() % 200);
            break;
    }
}

PixelFlow3 Special Case: PixelFlow3 uses a simplified 2-mode system:

switch (counterColChange % 2) {
    case 0:  // Green with intensity variation
        col1.r(0);
        col1.g(255);
        col1.b(150);
        col1 *= (float)(rand() % 100) / 100.0f;
        break;

    case 1:  // Blue-red combination
        col1.g(0);
        col1.b(255 - rand() % 100);
        col1.r(255 - rand() % 200);
        break;
}

IMU Integration (PixelFlow & PixelFlow2)

MPU6050 Sensor Usage

The original PixelFlow variants demonstrate advanced IMU integration:

class PixelFlow : public CubeApplication {
private:
    Mpu6050 Imu;                    // MPU6050 IMU sensor

public:
    void spawnParticles() {
        // Get intersection point of gravity vector with cube
        auto imuPoint = Imu.getCubeAccIntersect();

        // Determine spawn surface and initial velocity
        Vector3f startSpeed(0, 0, 0);
        switch(getScreenNumber(imuPoint)) {
            case top: case bottom:
                startSpeed[0] = vx;   // X-Y plane movement
                startSpeed[1] = vy;
                break;
            case front: case back:
                startSpeed[0] = vx;   // X-Z plane movement
                startSpeed[2] = vy;
                break;
            case left: case right:
                startSpeed[1] = vx;   // Y-Z plane movement
                startSpeed[2] = vy;
                break;
        }

        // Create particle at IMU-determined surface point
        createParticle(imuPoint.cast<float>(), startSpeed);
    }

    void updateParticles() {
        for(auto particle : particles) {
            // Apply real-world acceleration from IMU
            Vector3f imuAccel = Imu.getAcceleration();
            Vector3f randomVariation(-0.1f + rand_variation);

            particle->acceleration(imuAccel * randomVariation);
            particle->step();
        }
    }
};

Real-World Physics

IMU integration enables real-world physics simulation:

  • Tilt Response: Particles flow toward the “down” direction based on cube orientation

  • Motion Sensitivity: Shaking the cube increases particle activity

  • Gravity Simulation: Particles respond to real gravity direction

  • Surface Selection: Spawn points determined by which face is “up”

Interactive Controls

PixelFlow3 Controls

Button A: Cycle Color Schemes

Changes between green and blue-red color modes

Button Y: Pause/Unpause

Freezes particle simulation while preserving state

No IMU Dependency: Runs on any platform with matrixserver support

void handleInput() {
    for (auto joystick : joysticks) {
        if (joystick->getButtonPress(0)) {
            counterColChange++;        // Color scheme change
        }
        if (joystick->getButtonPress(3)) {
            isPaused = !isPaused;     // Pause toggle
        }
        joystick->clearAllButtonPresses();
    }
}

Performance Characteristics

Particle Density Comparison

  • PixelFlow: 60 particles/frame - High density for rich fluid effects

  • PixelFlow2: 60 particles/frame - Same density with optimized management

  • PixelFlow3: 30 particles/frame - Balanced for cross-platform performance

Frame Rate and Optimization

All variants run at 40 FPS with different optimization strategies:

// PixelFlow/PixelFlow2: High particle count with IMU processing
for (int i = 0; i < 60; i++) {
    // IMU-based particle creation (expensive)
    createIMUParticle();
}

// PixelFlow3: Lower particle count for broader compatibility
for (int i = 0; i < 30; i++) {
    // Simple algorithmic particle creation (efficient)
    createAlgorithmicParticle();
}

Memory Management

All variants use efficient smart pointer management:

// Automatic cleanup with STL algorithms
particles.erase(std::remove_if(particles.begin(), particles.end(),
    [](std::shared_ptr<Drop> p) { return p->getRdyDelete(); }),
    particles.end());

Building and Platform Requirements

Build Configuration

# PixelFlow and PixelFlow2 (Raspberry Pi only)
# Requires MPU6050 IMU sensor and appropriate I2C setup
make pixelflow    # Original IMU version
make pixelflow2   # Enhanced IMU version

# PixelFlow3 (All platforms)
make pixelflow3   # Cross-platform version

Platform Requirements

PixelFlow & PixelFlow2:
  • Raspberry Pi with I2C enabled

  • MPU6050 IMU sensor connected via I2C

  • matrixserver running on Raspberry Pi

PixelFlow3:
  • Any platform supporting matrixserver

  • Optional USB gamepad/joystick for interaction

  • No sensor hardware requirements

Hardware Setup (PixelFlow/PixelFlow2)

MPU6050 I2C connection: * VCC → 3.3V * GND → Ground * SDA → GPIO 2 (Pin 3) * SCL → GPIO 3 (Pin 5)

Enable I2C on Raspberry Pi: .. code-block:: bash

sudo raspi-config # Navigate to Interface Options → I2C → Enable

Advanced Usage

Customization Options

Particle Density: .. code-block:: cpp

// Increase particle count for denser effects for (int i = 0; i < 120; i++) { // Double density

createParticle();

}

Fade Persistence: .. code-block:: cpp

fade(0.95); // Longer trails fade(0.70); // Shorter trails

Color Cycling Speed: .. code-block:: cpp

if (counter % 25 == 0) { // Faster color changes

counterColChange++;

}

Physics Tuning

IMU Sensitivity (PixelFlow/PixelFlow2): .. code-block:: cpp

// More sensitive to IMU input particle->acceleration(Imu.getAcceleration() * -0.2f);

// Less sensitive to IMU input particle->acceleration(Imu.getAcceleration() * -0.05f);

Particle Lifetime: .. code-block:: cpp

// Longer-lived particles if (stepCount > 300) { rdyDelete_ = true; }

// Shorter-lived particles if (stepCount > 100) { rdyDelete_ = true; }

Development Workflow

Testing Sequence

  1. Start with PixelFlow3 for initial development (no hardware dependencies)

  2. Verify particle physics and visual effects work correctly

  3. Deploy to Raspberry Pi with PixelFlow/PixelFlow2 for IMU integration

  4. Fine-tune IMU sensitivity and surface dynamics

The PixelFlow series demonstrates the evolution from simple particle systems to sophisticated sensor-integrated fluid dynamics, providing excellent examples of both cross-platform development and hardware-specific optimization in LED cube applications.