Subversion Repositories DIN Is Noise

Rev

Rev 2009 | Rev 2197 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* curve.cc
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/



#include "curve.h"
#include "utils.h"
#include "vector2d.h"
#include "color.h"
#include <list>
#include <fstream>
#include "log.h"

using namespace std;

extern list<crvpt> LIST_OF_POINTS;

curve::curve () {
  x0 = y0 = x1 = y1 = 0;
  tx0 = ty0 = tx1 = ty1 = 0;
  set_limit (0);
}

curve::curve (float _x0, float _y0, float _x1, float _y1, float _tx0, float _ty0, float _tx1, float _ty1, float _d) {

  // 2 vertices
  x0 = _x0; x1 = _x1;
  y0 = _y0; y1 = _y1;

  // 2 tangents
  tx0 = _tx0; tx1 = _tx1;
  ty0 = _ty0; ty1 = _ty1;

  set_limit (_d);

}

void curve::eval (int find_length) {
  /*
   * generates points of the curve using recursive subdivision
   *
  */

 
  LIST_OF_POINTS.clear (); // clear existing points
 
  // add end points to curve
  LIST_OF_POINTS.push_back (crvpt (x0, y0, 0.0f)); // x0, y0, t0
  LIST_OF_POINTS.push_back (crvpt (x1, y1, 1.0f)); // x1, y1, t1
 
  list<crvpt>::iterator i (LIST_OF_POINTS.begin ());
  list<crvpt>::iterator j (i); ++j;
 
  // generate other points of the curve
  crvpt r;

  int inserts = 0;
  while (j != LIST_OF_POINTS.end()) {

    // end points
    crvpt& p = *i;
    crvpt& q = *j;

    // mid point of line joining end points
    float mx = (p.x + q.x) / 2, my = (p.y + q.y) / 2;

    // T of parametric bezier curve equation
    float T = (p.t + q.t) / 2;
   
    // calculate co-efficients of the bezier curve
    float T1 = 1 - T;
    float T1_2 = T1 * T1;
    float T1_3 = T1_2 * T1;
    float T_2 = T * T;
    float T_3 = T_2 * T;
    float BP1 = T1_3;
    float BT1 = 3 * T * T1_2;
    float BT2 = 3 * T_2 * T1;
    float BP2 = T_3;
   
    // calculate point on the bezier curve
    float bx = BP1 * x0 + BT1 * tx0 + BT2 * tx1 + BP2 * x1;
    float by = BP1 * y0 + BT1 * ty0 + BT2 * ty1 + BP2 * y1;
   
    // distance between mid point of line joining end points & point on the bezier curve
    float l2 = (float) magnitude2 (mx, my, bx, by);

    if (l2 >= limit2) { // insert point on bezier curve & further subdivide
     
      inserter:
        r.x = bx;
        r.y = by;
        r.t = T;
       
        r.m = 0;
        r.inf = 0;
       
        j = LIST_OF_POINTS.insert (j, r); // new right hand end point

        ++inserts;
   
    } else { // limit reached. no more inserts

      if (inserts == 0) {
        int zero_tangents = is_zero_vec (ptx0, pty0) && is_zero_vec (ptx1, pty1);
        if (zero_tangents) {
          // a line
        } else {
          float ax = ptx0 + ptx1;
          float ay = pty0 + pty1;
          const float e = 0.005f;
          int flipped_tangents = is_zero_vec (ax, ay, e);
          if (flipped_tangents) goto inserter;
        }
      }
         
      // calc slope of left end point
      calc_slope (p, q);

      i = j; // right end point is now left end point
      ++j; // new right end point
     
      // we find slope of all points except last point (that is OK)
   
    }

  }

  vpts.clear (); for (list<crvpt>::const_iterator p = LIST_OF_POINTS.begin (), q = LIST_OF_POINTS.end (); p != q; ++p) vpts.push_back (*p);

  if (find_length) calc_length ();

  eval_state = EVAL_COMPLETE;

}

void curve::set_limit (float l) {
  limit2 = l * l;
  eval_state = EVAL_REQUIRED;
}

float curve::calc_length () {
  // return length of the curve
  // ie sum of lengths of all line segments that make the curve
  length = 0;
  int last = vpts.size () - 1;
  for (int i = 0; i < last; ++i) {
    crvpt& pi = vpts[i];
    crvpt& pj = vpts[i+1];
    pi.t = magnitude (pi.x, pi.y, pj.x, pj.y); // store length of segment i,j in point i for use in normalise_length (see below)
    length += pi.t;
  }
  vpts[last].t = 0;
  return length;
}

void curve::normalise_length (float& length_now, float total_length) {
  dpts.clear ();
  if (total_length == 0) {
    crvpt& p01 = vpts[0];
    dpts.push_back (crvpt (0, p01.y, 0));
    dpts.push_back (crvpt (1, p01.y, 0));
    return;
  }
  int npts = vpts.size ();
  for (int i = 0; i < npts; ++i) {
    crvpt& pi = vpts[i];
    float norm = length_now * 1.0f / total_length;
    dpts.push_back (crvpt (norm, pi.y, pi.t));
    length_now += pi.t;
  }
  for (int i = 0, last = npts - 1; i < last; ++i) calc_slope (dpts[i], dpts[i+1]);
}

void curve::calc_slope (crvpt& p, crvpt& q) {
  float dx = q.x - p.x;
  if (dx == 0) {
    p.m = 0;
    p.inf = 1; // slope is infinite
  } else {
    float dy = q.y - p.y;
    if (dy == 0) {
      p.m = 0;
      p.inf = 0;
    } else {
      p.m = dy / dx;
      p.inf = -1;
    }
  }
}