typedef struct
{
	// variables
    double ripple_frequency,
    	   ripple_velocity,
    	   ripple_amplitude,
    	   ripple_centerx,
    	   ripple_centery,
    	   ripple_phase,
    	   ripple_scale;
    	   
	// private stuff
    double f, a, p, s, is,
		   vxp, pxa, pixa;
} Variables;

#define _USE_MATH_DEFINES
#include "apoplugin.h"

APO_PLUGIN("ripple");
APO_VARIABLES(
	// wave function frequency
    VAR_REAL(ripple_frequency, 2.0),
    
    // wave velocity
    VAR_REAL(ripple_velocity, 1.0),
    
    // wave amplitude
    VAR_REAL(ripple_amplitude, 0.5),
    
    // origin of the ripple (x,y)
    VAR_REAL(ripple_centerx, 0.0),
    VAR_REAL(ripple_centery, 0.0),
    
    // wave phase / interpolation coefficient
    VAR_REAL(ripple_phase, 0.0),
    
    // ripple scale
    VAR_REAL(ripple_scale, 1.0)
);

int PluginVarPrepare(Variation* vp)
{
	// some variables are settled in another range for edit comfort
	// - transform them
	VAR(f) = VAR(ripple_frequency) * 5;
	VAR(a) = VAR(ripple_amplitude) * 0.01;
	VAR(p) = VAR(ripple_phase) * M_2PI - M_PI; 
	
	// scale must not be zero
	VAR(s) = VAR(ripple_scale) == 0 ? EPS : VAR(ripple_scale);
	
	// we will need the inverse scale
	VAR(is) = 1 / VAR(s);
	
	// pre-multiply velocity+phase, phase+amplitude and (PI-phase)+amplitude
	VAR(vxp) = VAR(ripple_velocity) * VAR(p);
	VAR(pxa) = VAR(p) * VAR(a);
	VAR(pixa) = (M_PI - VAR(p)) * VAR(a);
	
	// done
    return TRUE; 
}

inline double lerp(double a, double b, double p) { return a + (b - a) * p; }

int PluginVarCalc(Variation* vp)
{
	//align input x, y to given center and multiply with scale
    double x = (FTx * VAR(s)) - VAR(ripple_centerx),
           y = (FTy * VAR(s)) + VAR(ripple_centery);
           
    // calculate distance from center but constrain it to EPS
    double d = MAX(EPS, sqrt(sqr(x) * sqr(y)));
    
    // normalize (x,y)
    double nx = x / d, 
	       ny = y / d;
	       
	// calculate cosine wave with given frequency, velocity 
	// and phase based on the distance to center
    double wave = cos(VAR(f) * d - VAR(vxp));
    
    // calculate the wave offsets
    double d1 = wave * VAR(pxa) + d,
   		   d2 = wave * VAR(pixa) + d;
   		   
   	// we got two offsets, so we also got two new positions (u,v)
    double u1 = (VAR(ripple_centerx) + nx * d1),
    	   v1 = (-VAR(ripple_centery) + ny * d1);
    double u2 = (VAR(ripple_centerx) + nx * d2),
    	   v2 = (-VAR(ripple_centery) + ny * d2);
    
    // interpolate the two positions by the given phase and
    // invert the multiplication with scale from before
    FPx = VVAR * (lerp(u1, u2, VAR(p))) * VAR(is);
    FPy = VVAR * (lerp(v1, v2, VAR(p))) * VAR(is);
    
    // done
    return TRUE;
}
