/* See voxm_bezier.h */
/* Last edited on 2016-04-04 00:07:18 by stolfilocal */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

#include <bool.h>
#include <r3.h>
#include <r3_path.h>
#include <affirm.h>

#include <voxm_path.h>
#include <voxm_bezier.h>

void voxm_bezier_from_path_states(voxm_path_state_t *S, voxm_path_state_t *T, r3_t *p1, r3_t *p2)
  { 
    bool_t debug = TRUE;
    if (debug) { fprintf(stderr, "enter %s\n", __FUNCTION__); }

    if (debug) 
      { voxm_path_state_debug(stderr, S, "  ", "initial"); 
        voxm_path_state_debug(stderr, T, "  ", "final"); 
      }
    
    r3_t *p0 = &(S->p);
    r3_t *p3 = &(T->p);
    double dt = T->t - S->t;  /* Duration of path. */
    r3_mix(1.0, p0, +dt/3.0, &(S->v), p1);
    r3_mix(1.0, p3, -dt/3.0, &(T->v), p2);
    
    if (debug) { fprintf(stderr, "exit %s\n", __FUNCTION__); }
  }

double voxm_bezier_length_estimate(r3_t *p0, r3_t *p1, r3_t *p2, r3_t *p3, int order)
  { 
    if (order <= 0)
      { return r3_dist(p0,p3); }
    else
      { 
        r3_t p01, p12, p23, p012, p123, p0123; /* Intermediate points. */
        voxm_bezier_split(0.0, 1.0, p0, p1, p2, p3, 0.5, &p01, &p12, &p23, &p012, &p123, &p0123, NULL);      
        double d0 = voxm_bezier_length_estimate(p0, &p01, &p012, &p0123, order-1);
        double d1 = voxm_bezier_length_estimate(&p0123, &p123, &p23, p3, order-1);
        return d0 + d1;
      }
  }

void voxm_bezier_split
  ( double t0, 
    double t1,
    r3_t *p0, 
    r3_t *p1, 
    r3_t *p2, 
    r3_t *p3, 
    double t,
    r3_t *p01, 
    r3_t *p12,
    r3_t *p23, 
    r3_t *p012, 
    r3_t *p123,
    r3_t *p0123,
    r3_t *v
  )
  {
    /* The DeCasteljau algorithm: */
    double dt = t1 - t0;
    demand(dt != 0, "path duration is zero");
    double b = (t - t0)/dt;
    double a = 1 - b;
    
    r3_mix(a, p0, b, p1, p01);
    r3_mix(a, p1, b, p2, p12);
    r3_mix(a, p2, b, p3, p23);

    r3_mix(a, p01, b, p12, p012);
    r3_mix(a, p12, b, p23, p123);

    r3_mix(a, p012, b, p123, p0123);

    if (v != NULL) 
      { r3_sub(p123, p012, v);
        r3_scale(3/dt, v, v);
      }
  }

