home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DOS/V Power Report 1997 March
/
VPR9703A.ISO
/
VPR_DATA
/
DOGA
/
SOURCES
/
MEDIT.LZH
/
BEZIER.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1995-09-26
|
11KB
|
471 lines
#include <stdio.h>
#include "matrix.h"
#include "bezier.h"
#include "log.h"
static const int AllocUnit = 32;
static const int BezierPoints = 64;
static const double minimumlength = 100.0;
class BezierPoint {
public:
double (*bezierrate)[4];
BezierPoint();
~BezierPoint() {delete bezierrate;}
};
BezierPoint::BezierPoint()
{
bezierrate = new double[BezierPoints+1][4];
double t = 0.0;
for (int i = 0; i < BezierPoints+1; ++i) {
bezierrate[i][0] = (1-t)*(1-t)*(1-t);
bezierrate[i][1] = 3* t *(1-t)*(1-t);
bezierrate[i][2] = 3* t * t *(1-t);
bezierrate[i][3] = t * t * t ;
t += (1.0 / BezierPoints);
}
}
BezierPoint bp;
#if 0
double bezierrate[17][4] = {
{4096.0 / 4096.0, 0.0 / 4096.0, 0.0 / 4096.0, 0.0 / 4096.0},
{3375.0 / 4096.0, 675.0 / 4096.0, 45.0 / 4096.0, 1.0 / 4096.0},
{2744.0 / 4096.0, 1176.0 / 4096.0, 168.0 / 4096.0, 8.0 / 4096.0},
{2197.0 / 4096.0, 1521.0 / 4096.0, 351.0 / 4096.0, 27.0 / 4096.0},
{1728.0 / 4096.0, 1728.0 / 4096.0, 576.0 / 4096.0, 64.0 / 4096.0},
{1331.0 / 4096.0, 1815.0 / 4096.0, 750.0 / 4096.0, 125.0 / 4096.0},
{1000.0 / 4096.0, 1800.0 / 4096.0, 1080.0 / 4096.0, 216.0 / 4096.0},
{ 729.0 / 4096.0, 1701.0 / 4096.0, 1323.0 / 4096.0, 343.0 / 4096.0},
{ 512.0 / 4096.0, 1536.0 / 4096.0, 1536.0 / 4096.0, 512.0 / 4096.0},
{ 343.0 / 4096.0, 1323.0 / 4096.0, 1701.0 / 4096.0, 729.0 / 4096.0},
{ 216.0 / 4096.0, 1080.0 / 4096.0, 1800.0 / 4096.0, 1000.0 / 4096.0},
{ 125.0 / 4096.0, 750.0 / 4096.0, 1815.0 / 4096.0, 1331.0 / 4096.0},
{ 64.0 / 4096.0, 576.0 / 4096.0, 1728.0 / 4096.0, 1728.0 / 4096.0},
{ 27.0 / 4096.0, 351.0 / 4096.0, 1521.0 / 4096.0, 2197.0 / 4096.0},
{ 8.0 / 4096.0, 168.0 / 4096.0, 1176.0 / 4096.0, 2744.0 / 4096.0},
{ 1.0 / 4096.0, 45.0 / 4096.0, 675.0 / 4096.0, 3375.0 / 4096.0},
{ 0.0 / 4096.0, 0.0 / 4096.0, 0.0 / 4096.0, 4096.0 / 4096.0},
};
#endif
Bezier::Bezier()
{
points = -1;
allocpoints = AllocUnit;
point = new Vector[allocpoints*3+2];
length = new double[allocpoints];
point[0] = Vector(0,0,0);
lengthvalid = FALSE;
}
Bezier::Bezier(Bezier& b)
{
memcpy(this, &b, sizeof(Bezier));
point = new Vector[allocpoints*3+2];
length = new double[allocpoints];
memcpy(point, b.point, sizeof(Vector) * (points*3+2));
memcpy(length, b.length, sizeof(double) * points);
}
Bezier::Bezier(Vector &v1)
{
points = 0;
allocpoints = AllocUnit;
point = new Vector[allocpoints*3+2];
length = new double[allocpoints];
point[0] = point[1] = v1;
lengthvalid = FALSE;
}
Bezier::Bezier(Vector &v1, Vector &v2)
{
points = 0;
allocpoints = AllocUnit;
point = new Vector[allocpoints*3+2];
length = new double[allocpoints];
point[0] = v1;
point[1] = v2;
lengthvalid = FALSE;
}
Bezier::~Bezier()
{
delete point;
delete length;
}
Vector Bezier::GetPoint(double t)
{
if (points == 0) {
return point[0];
} else if (t <= 0) {
return point[0];
} else if (t >= (double)points) {
return point[3 * points];
} else {
int begin = (int)t;
t -= begin;
Vector *p = point + 3 * begin;
Vector v = ( (1-t)*(1-t)*(1-t))*p[0]
+ (3* t *(1-t)*(1-t))*p[1]
+ (3* t * t *(1-t))*p[2]
+ ( t * t * t )*p[3];
return v;
}
}
Vector Bezier::GetVector(double t)
{
if (points == 0) {
return Vector(0,0,0);
}
if (t <= 0) {
if (point[1] == point[0]) {
return 3.0 * (point[2] - point[0]);
} else {
return 3.0 * (point[1] - point[0]);
}
} else if (t >= (double)points) {
if (point[3*points] == point[3*points-1]) {
return 3.0 * (point[3*points] - point[3*points-2]);
} else {
return 3.0 * (point[3*points] - point[3*points-1]);
}
} else {
int begin = (int)t;
t -= begin;
Vector *p = point + 3 * begin;
if (t == 0.0 && p[0] == p[1]) {
return p[2] - p[0];
} else {
return (-3 * (1 - t) * (1-t)) * p[0]
+ ( 3 * (1 - 3*t) * (1-t)) * p[1]
+ ( 3 * (2 - 3*t) * t ) * p[2]
+ ( 3 * t * t ) * p[3];
}
}
}
double Bezier::GetRate(double l)
{
if (l <= 0.0) {
return 0.0;
}
for (int i = 0; i < points; ++i) {
if (l < length[i]) {
break;
}
l -= length[i];
}
if (i == points) {
return (double)points;
}
Vector *p = point + i * 3;
for (int j = 0; j < BezierPoints; ++j) {
Vector v = (bp.bezierrate[j+1][0]-bp.bezierrate[j][0]) * p[0]
+ (bp.bezierrate[j+1][1]-bp.bezierrate[j][1]) * p[1]
+ (bp.bezierrate[j+1][2]-bp.bezierrate[j][2]) * p[2]
+ (bp.bezierrate[j+1][3]-bp.bezierrate[j][3]) * p[3];
double vl = v.length();
if (l < vl) {
return (double)i + ((double)j + l / vl) / (double)BezierPoints;
}
l -= vl;
}
return (double)(i+1);
}
double Bezier::TotalLength(void)
{
if (points <= 0) {
return 0.0;
}
if (lengthvalid == FALSE) {
UpdateLength();
}
double l = 0;
for (int i = 0; i < points; ++i) {
l += length[i];
}
return l;
}
void Bezier::UpdateLength(int i)
{
Vector *p = point + 3 * i;
double l = 0;
for (int j = 0; j < BezierPoints; ++j) {
Vector v = (bp.bezierrate[j+1][0]-bp.bezierrate[j][0]) * p[0]
+ (bp.bezierrate[j+1][1]-bp.bezierrate[j][1]) * p[1]
+ (bp.bezierrate[j+1][2]-bp.bezierrate[j][2]) * p[2]
+ (bp.bezierrate[j+1][3]-bp.bezierrate[j][3]) * p[3];
l += v.length();
}
if (l < minimumlength) {
l = minimumlength;
}
length[i] = l;
}
void Bezier::UpdateLength(void)
{
for (int i = 0; i < points; ++i) {
UpdateLength(i);
}
lengthvalid = TRUE;
}
void Bezier::AddPoint(Vector& v1, Vector& v2, Vector& v3)
{
if (points < 0) {
point[0] = point[1] = v3;
points = 0;
} else {
AllocPoint(points+1);
point[points*3+1] = v1;
point[points*3+2] = v2;
point[points*3+3] = v3;
points++;
}
lengthvalid = FALSE;
}
void Bezier::AddPoint(Vector& p1, Vector& p2)
{
if (points < 0) {
point[0] = p1;
point[1] = p2;
points = 0;
} else {
AllocPoint(points+1);
point[points*3+2] = 2 * p1 - p2;
point[points*3+3] = p1;
point[points*3+4] = p2;
points++;
}
lengthvalid = FALSE;
}
void Bezier::MovePoint(int pos, Vector& p, BezierControl flag)
{
if (pos < 0 || pos > points * 3 + 1) {
return;
}
if (flag == BCdep) {
if (pos == 0) {
point[1] += p - point[0];
} else if (pos > 1) {
Vector v;
double l1, l2;
switch (pos % 3) {
case 0:
point[pos-1] += p - point[pos];
point[pos+1] += p - point[pos];
break;
case 1:
v = p - point[pos-1];
l1 = (point[pos-1] - point[pos-2]).length();
l2 = v.length();
if (l2 > 0.0) {
point[pos-2] = point[pos-1] - (l1/l2) * v;
}
break;
case 2:
v = p - point[pos+1];
l1 = (point[pos+1] - point[pos+2]).length();
l2 = v.length();
if (l2 > 0.0) {
point[pos+2] = point[pos+1] - (l1/l2) * v;
}
break;
}
}
} else if (flag == BCsame) {
if (pos == 0) {
point[1] += p - point[0];
} else if (pos > 1) {
switch (pos % 3) {
case 0:
point[pos-1] += p - point[pos];
point[pos+1] += p - point[pos];
break;
case 1:
point[pos-2] = 2.0 * point[pos-1] - p;
break;
case 2:
point[pos+2] = 2.0 * point[pos+1] - p;
break;
}
}
}
point[pos] = p;
lengthvalid = FALSE;
}
void Bezier::InsertPoint(int pos)
{
if (pos < 0 || pos >= points) {
return;
}
AllocPoint(points+1);
for (int i = points*3+1; i >= pos*3+2; --i) {
point[i+3] = point[i];
}
for (i = points; i > pos; --i) {
length[i] = length[i-1];
}
points++;
i = pos*3;
// Vector& v0 = point[i];
// Vector& v3 = point[i+6];
Vector v1 = point[i+1];
Vector v2 = point[i+2];
// point[i+1] = 0.5 * (v0 + v1);
// point[i+5] = 0.5 * (v2 + v3);
Vector v12 = 0.5 * (v1 + v2);
point[i+2] = 0.5 * (point[i+1] + v12);
point[i+4] = 0.5 * (v12 + point[i+5]);
point[i+3] = 0.5 * (point[i+2] + point[i+4]);
length[pos] = length[pos+1] = length[pos]/2;
lengthvalid = FALSE;
}
void Bezier::InsertPoint(int pos, Vector& v1, Vector& v2)
{
if (pos < 0 || pos > points+1) {
return;
}
AllocPoint(points+1);
for (int i = points*3+1; i >= pos*3-1 && i >= 0; --i) {
point[i+3] = point[i];
}
for (i = points; i > pos; --i) {
length[i] = length[i-1];
}
points++;
i = pos*3;
if (pos == 0) {
point[i ] = v1;
point[i+1] = v2;
point[i+2] = 2.0 * point[i+3] - point[i+4];
length[0] = 0.0;
} else {
point[i-1] = 2.0 * v1 - v2;
point[i ] = v1;
point[i+1] = v2;
double l0 = length[pos-1];
UpdateLength(pos-1);
UpdateLength(pos);
if (length[pos-1] == 0.0) {
length[pos] = l0;
} else if (length[pos] == 0.0) {
length[pos-1] = l0;
} else {
double l1 = length[pos-1] + length[pos];
length[pos-1] *= l0/l1;
length[pos ] *= l0/l1;
}
}
lengthvalid = FALSE;
}
void Bezier::DeletePoint(int pos)
{
if (pos < 0 || pos > points) {
return;
}
int i;
if (pos == 0) {
for (i = 0; i < points*3-1; ++i) {
point[i] = point[i+3];
}
} else {
length[pos-1] += length[pos];
if (pos < points){
for (i = pos*3-1; i < points*3-1; ++i) {
point[i] = point[i+3];
}
}
}
for (i = pos; i < points; ++i) {
length[i] = length[i+1];
}
points--;
lengthvalid = FALSE;
}
void Bezier::AllocPoint(int num)
{
if (allocpoints < num) {
for (int nalloc = allocpoints; nalloc < num; nalloc += AllocUnit)
;
Vector *v = new Vector[nalloc*3+2];
double *d = new double[nalloc];
int i;
for (i = 0; i < allocpoints * 3 + 2; ++i) {
v[i] = point[i];
}
// for (i = 0; i < allocpoints; ++i) {
// d[i] = length[i];
// }
allocpoints = nalloc;
delete[] point;
delete[] length;
point = v;
length = d;
lengthvalid = FALSE;
}
}
void Bezier::AddSpline(Vector* p, int num)
{
AllocPoint(points+num);
for (int i = 0; i < num; ++i) {
AddPoint(p[i], p[i]);
}
#if 0
Vector d[num], z[num];
double dd[num];
if (points > 0) {
z[0] = point[3*points-2] - 2 * point[3*points-1] + point[3*points];
} else {
z[0] = Vector(0,0,0);
}
z[num-1] = Vector(0,0,0);
int i;
for (i = 0; i < num-1; i++) {
d[i+1] = p[i+1] - p[i];
}
z[1] = d[2] - d[1] - z[0];
dd[1] = 4;
for (i = 1; i < num-2; i++) {
double t = 1.0 / dd[i];
z[i+1] = d[i+2] - d[i+1] - t * z[i];
dd[i+1] = 4 - t;
}
z[num-2] -= z[num-1];
for (i = num-2; i > 0; i--) {
z[i] = (z[i] - z[i+1]) / dd[i];
}
#endif
}