Some CLERP (circular linear interpolation) functions

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;
}

CLERP, January 28 2019
1478 views


◀ Back