/*
 * Three-dimensional pattern in C++ based on the "Rings" Processing example.
 *
 * Uses noise functions modulated by sinusoidal rings, which themselves
 * wander and shift according to some noise functions.
 *
 * 2014 Micah Elizabeth Scott
 */

#include <math.h>
#include "color.h"
#include "effect.h"
#include "noise.h"

class Rings : public Effect
{
public:
    Rings()
        : d(0, 0, 0, 0) {}

    static const float xyzSpeed = 0.006;
    static const float xyzScale = 0.08;
    static const float wSpeed = 0.002;
    static const float wRate = 0.0001;
    static const float ringScale = 1.5;
    static const float ringScaleRate = 0.01;
    static const float ringDepth = 0.2;
    static const float wanderSpeed = 0.04;
    static const float wanderSize = 1.8;
    static const float hueScale = 5.0;
    static const float hueRate = 0.001;

    // State variables
    Vec4 d;

    // Calculated once per frame
    float hue, saturation;
    float spacing;
    Vec3 center;

    virtual void beginFrame(const FrameInfo &f)
    {
        hue = noise2(f.time * hueRate, 20.5) * hueScale;

        saturation = sq(std::min(std::max(0.7f * (0.5f + noise2(f.time * 0.01, 0.5)), 0.0f), 1.0f));
        spacing = sq(0.5 + noise2(f.time * ringScaleRate, 1.5)) * ringScale;

        // Rotate movement in the XZ plane
        float angle = noise2(f.time * 0.01, 30.5) * 10.0;
        float speed = pow(fabsf(noise2(f.time * 0.01, 40.5)), 2.5) * xyzSpeed;
        d[0] += cosf(angle) * speed;
        d[2] += sinf(angle) * speed;

        // Random wander along the W axis
        d[3] += noise2(f.time * wRate, 3.5) * wSpeed;

        // Update center position
        center = Vec3(noise2(f.time * wanderSpeed, 50.9),
                      noise2(f.time * wanderSpeed, 51.4),
                      noise2(f.time * wanderSpeed, 51.7)) * wanderSize;
    }

    virtual void calculatePixel(Vec3& rgb, const PixelInfo &p)
    {
        float dist = len(p.point - center);
        Vec4 pulse = Vec4(sinf(d[2] + dist * spacing) * ringDepth, 0, 0, 0);
        Vec4 s = Vec4(p.point * xyzScale, 0) + d;
        Vec4 chromaOffset = Vec4(0, 0, 0, 10);

        float n = fbm_noise4(s + pulse, 4) * 2.5f + 0.1f;
        float m = fbm_noise4(s + chromaOffset, 4);

        hsv2rgb(rgb,
            hue + 0.5 * m,
            saturation,
            std::min(0.9f, sq(std::max(0.0f, n)))
        );
    }
};

int main(int argc, char **argv)
{
    EffectRunner r;
    Rings e;
    r.setEffect(&e);
    r.setLayout("../layouts/grid32x16z.json");
    return r.main(argc, argv);
}