I think these work. I trip over this every time I try to do it; this is my most recent code and I think a lot of stuff for me wouldn't work if these were wrong but... I think these work.
#ifndef UTIL_CLERP_H
#define UTIL_CLERP_H
// circular linear interpolation; note: these are all default mod 1.0, not M_2PI
float clerp_direction (float H0, float H1, float Domain);
float clerp_fmod (float H, float Mod);
float clerp_dist (float H0, float H1, float Domain = 1.0);
float clerp_do (float H0, float H1, float Alpha, float Domain = 1.0);
float clerp_closest (float HSrc, float HDst0, float HDst1, float Alpha);
float clerp_max_do (float H0, float H1, float Max, float Domain);
// integer version
int clerp_mod (int H, int Mod);
// modulo x along a circular domain, so that -1 -> Radix - 1
inline int zmod (int X, int Radix) {
if (X < 0) return Radix - ((-X - 1) % Radix) - 1;
return X % Radix;
}
#endif // CLERP_H
#include "clerp.h"
#include <math.h>
#include <algorithm>
using namespace std;
float clerp_fmod (float H, float Mod) {
if (H >= 0) return fmod( H, Mod );
return Mod - fmod (-H, Mod);
};
int clerp_mod (int H, int Mod) {
int HM = H % Mod;
if (HM < 0) HM = Mod + HM;
return HM;
}
float clerp_direction (float H0, float H1, float Domain) {
H0 = fmod (H0, Domain);
H1 = fmod (H1, Domain);
float Factor = 1.0f;
if (H0 > H1) {
float Temp = H1;
H1 = H0;
H0 = Temp;
Factor = Factor * -1.0f;
}
float D1 = fabs (H1 - H0);
float D2 = fabs (H0 + Domain - H1);
if (D1 > D2) {
Factor = Factor * -1.0f;
}
return Factor;
}
float clerp_dist (float H0, float H1, float Domain) {
// we can either rotate H0 to the left, or to the right
H0 = clerp_fmod (H0, Domain);
H1 = clerp_fmod (H1, Domain);
float Dist = 0.0;
if (H0 < H1) {
float LeftDist = H0 + (Domain - H1);
float RightDist = H1 - H0;
Dist = min (LeftDist, RightDist);
}
else {
float LeftDist = H1 + (Domain - H0);
float RightDist = H0 - H1;
Dist = min (LeftDist, RightDist);
}
return Dist;
}
float clerp_do (float H0, float H1, float Alpha, float Domain) {
// which is the closest direction to rotate towards? if it's the other way, then bump it
H0 = clerp_fmod (H0, Domain);
H1 = clerp_fmod (H1, Domain);
if (fabs(H0 - H1) > fabs(H0 + Domain - H1)) {
H0 += Domain;
}
else if (fabs (H1 - H0) > fabs (H1 + Domain - H0)) {
H1 += Domain;
}
float HOut = H0 * (1.0 - Alpha) + H1 * Alpha;
clerp_fmod (HOut, Domain);
return HOut;
};
float clerp_closest (float HSrc, float HDst0, float HDst1, float Alpha) {
if( clerp_dist( HSrc, HDst0 ) < clerp_dist( HSrc, HDst1 ) ) {
return clerp_do( HSrc, HDst0, Alpha );
}
else {
return clerp_do( HSrc, HDst1, Alpha );
}
};
//
// move H0 to H1 up to Max amount, on Domain
//
float clerp_max_do (float H0, float H1, float Max, float Domain) {
// which is the closest direction to rotate towards? if it's the other way, then bump it
H0 = clerp_fmod (H0, Domain);
H1 = clerp_fmod (H1, Domain);
if (fabs (H0 - H1) > fabs (H0 + Domain - H1)) {
H0 = H0 + Domain;
}
else if (fabs (H1 - H0) > fabs (H1 + Domain - H0)) {
H1 = H1 + Domain;
}
float Delta = H1 - H0;
if (Delta > Max) Delta = Max;
else if (Delta < (Max * -1.0f)) Delta = Max * -1.0f;
float HOut = H0 + Delta;
HOut = clerp_fmod (HOut, Domain);
return HOut;
}