Rev 274 |
Rev 964 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* curve.cc
* DIN Is Noise is copyright (c) 2006-2017 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/
#include "curve.h"
#include "vector2d.h"
#include "utils.h"
#include "color.h"
#include <list>
#include <fstream>
using namespace std;
extern list<crvpt> LIST_OF_POINTS;
curve::curve () {
for (int i = 0; i < 2; ++i) x[i] = y[i] = tx[i] = ty[i] = 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
x[0] = x0; x[1] = x1;
y[0] = y0; y[1] = y1;
// 2 tangents
tx[0] = tx0; tx[1] = tx1;
ty[0] = ty0; ty[1] = 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 (x[0], y[0], 0.0f)); // x0, y0, t0
LIST_OF_POINTS.push_back (crvpt (x[1], y[1], 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;
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 * x[0] + BT1 * tx[0] + BT2 * tx[1] + BP2 * x[1];
float by = BP1 * y[0] + BT1 * ty[0] + BT2 * ty[1] + BP2 * y[1];
// 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
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
} else { // limit reached. no more inserts
// 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;
}
}
}