You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
840 lines
16 KiB
840 lines
16 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* mesh.cxx
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Implementation of spline meshes
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 01/18/1999 davidx
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
|
|
//
|
|
// Mesh constructor
|
|
//
|
|
|
|
Mesh::Mesh(
|
|
INT gridRows,
|
|
INT gridCols
|
|
)
|
|
|
|
{
|
|
this->gridRows = gridRows;
|
|
this->gridCols = gridCols;
|
|
ptTemp = NULL;
|
|
ptxTemp1 = ptxTemp2 = NULL;
|
|
dstSize.cx = dstSize.cy = 2;
|
|
|
|
mesh = new PointX[gridRows*gridCols];
|
|
|
|
if (mesh == NULL)
|
|
Error("Couldn't allocate memory to hold mesh grids\n");
|
|
|
|
initMesh();
|
|
}
|
|
|
|
//
|
|
// Mesh destructor
|
|
//
|
|
|
|
Mesh::~Mesh()
|
|
{
|
|
delete[] mesh;
|
|
delete[] ptTemp;
|
|
delete[] ptxTemp1;
|
|
delete[] ptxTemp2;
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize mesh configuration
|
|
//
|
|
|
|
VOID
|
|
Mesh::initMesh()
|
|
{
|
|
PointX* p = mesh;
|
|
double sx = 1.0 / (gridCols-1);
|
|
double sy = 1.0 / (gridRows-1);
|
|
|
|
for (INT row=0; row < gridRows; row++)
|
|
{
|
|
double y = row * sy;
|
|
|
|
for (INT col=0; col < gridCols; col++)
|
|
{
|
|
p->x = col * sx;
|
|
p->y = y;
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate temporary memory to hold
|
|
//
|
|
|
|
VOID
|
|
Mesh::allocTempPoints()
|
|
{
|
|
// Check to see if we have already allocated
|
|
// temporary memory for holding spline and bezier points
|
|
|
|
if (ptTemp)
|
|
return;
|
|
|
|
INT count = max(gridRows, gridCols);
|
|
|
|
ptxTemp1 = new PointX[count];
|
|
ptxTemp2 = new PointX[3*count+1];
|
|
ptTemp = new POINT[3*count+1];
|
|
|
|
if (!ptxTemp1 || !ptxTemp2 || !ptTemp)
|
|
Error("Out of memory\n");
|
|
}
|
|
|
|
|
|
//
|
|
// Convert a spline curve to Bezier segments
|
|
//
|
|
|
|
VOID
|
|
Mesh::spline2Bezier(
|
|
const PointX* srcPts,
|
|
PointX* dstPts,
|
|
INT count
|
|
)
|
|
|
|
{
|
|
const PointX* p;
|
|
PointX tempPts[4];
|
|
|
|
// We use the default tension of 0.5
|
|
|
|
double a3 = 0.5 / 3;
|
|
|
|
*dstPts = *srcPts;
|
|
|
|
for (INT i=0; i < count; i++)
|
|
{
|
|
if (i > 1 && i < count-1)
|
|
p = srcPts + (i-1);
|
|
else
|
|
{
|
|
tempPts[0] = srcPts[(i > 0) ? (i-1) : 0];
|
|
tempPts[1] = srcPts[i];
|
|
tempPts[2] = srcPts[(i+1 < count) ? (i+1) : count];
|
|
tempPts[3] = srcPts[(i+2 < count) ? (i+2) : count];
|
|
p = tempPts;
|
|
}
|
|
|
|
dstPts[1].x = -a3*p[0].x + p[1].x + a3*p[2].x;
|
|
dstPts[1].y = -a3*p[0].y + p[1].y + a3*p[2].y;
|
|
dstPts[2].x = a3*p[1].x + p[2].x - a3*p[3].x;
|
|
dstPts[2].y = a3*p[1].y + p[2].y - a3*p[3].y;
|
|
dstPts[3] = p[2];
|
|
|
|
dstPts += 3;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Return the beizer control points corresponding to
|
|
// the specified row of the mesh.
|
|
//
|
|
|
|
PointX*
|
|
Mesh::getMeshRowBeziers(
|
|
INT row,
|
|
INT* pointCount,
|
|
double sx,
|
|
double sy
|
|
)
|
|
{
|
|
allocTempPoints();
|
|
|
|
PointX* ptx = mesh + indexOf(row);
|
|
|
|
for (INT i=0; i < gridCols; i++)
|
|
{
|
|
ptxTemp1[i].x = ptx[i].x * sx;
|
|
ptxTemp1[i].y = ptx[i].y * sy;
|
|
}
|
|
|
|
INT segments = gridCols-1;
|
|
spline2Bezier(ptxTemp1, ptxTemp2, segments);
|
|
|
|
*pointCount = 3*segments + 1;
|
|
return ptxTemp2;
|
|
}
|
|
|
|
POINT*
|
|
Mesh::getMeshRowBeziers(
|
|
INT row,
|
|
INT* pointCount
|
|
)
|
|
{
|
|
PointX* ptx;
|
|
|
|
ptx = getMeshRowBeziers(row, pointCount, dstSize.cx - 1, dstSize.cy - 1);
|
|
PointX::convertToPOINT(ptx, ptTemp, *pointCount);
|
|
return ptTemp;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the beizer control points corresponding to
|
|
// the specified column of the mesh.
|
|
//
|
|
|
|
PointX*
|
|
Mesh::getMeshColumnBeziers(
|
|
INT col,
|
|
INT* pointCount,
|
|
double sx,
|
|
double sy
|
|
)
|
|
{
|
|
allocTempPoints();
|
|
|
|
INT i, j;
|
|
|
|
for (i=0, j=col; i < gridRows; i++, j+=gridCols)
|
|
{
|
|
ptxTemp1[i].x = mesh[j].x * sx;
|
|
ptxTemp1[i].y = mesh[j].y * sy;
|
|
}
|
|
|
|
INT segments = gridRows-1;
|
|
spline2Bezier(ptxTemp1, ptxTemp2, segments);
|
|
|
|
*pointCount = 3*segments + 1;
|
|
return ptxTemp2;
|
|
}
|
|
|
|
POINT*
|
|
Mesh::getMeshColumnBeziers(
|
|
INT col,
|
|
INT* pointCount
|
|
)
|
|
{
|
|
PointX* ptx;
|
|
|
|
ptx = getMeshColumnBeziers(col, pointCount, dstSize.cx - 1, dstSize.cy - 1);
|
|
PointX::convertToPOINT(ptxTemp2, ptTemp, *pointCount);
|
|
return ptTemp;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the mesh control points for the specified row
|
|
//
|
|
|
|
POINT*
|
|
Mesh::getMeshRowPoints(
|
|
INT row,
|
|
INT* pointCount
|
|
)
|
|
{
|
|
allocTempPoints();
|
|
|
|
POINT* pt = ptTemp;
|
|
PointX* ptx = mesh + indexOf(row);
|
|
|
|
double sx = dstSize.cx - 1;
|
|
double sy = dstSize.cy - 1;
|
|
|
|
for (INT j=0; j < gridCols; j++)
|
|
{
|
|
pt[j].x = ROUND2INT(ptx[j].x * sx);
|
|
pt[j].y = ROUND2INT(ptx[j].y * sy);
|
|
}
|
|
|
|
return pt;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the specified mesh control point (given row & column)
|
|
//
|
|
|
|
VOID
|
|
Mesh::getMeshPoint(
|
|
INT row,
|
|
INT col,
|
|
POINT* point
|
|
)
|
|
{
|
|
INT index = indexOf(row, col);
|
|
|
|
point->x = ROUND2INT(mesh[index].x * (dstSize.cx - 1));
|
|
point->y = ROUND2INT(mesh[index].y * (dstSize.cy - 1));
|
|
}
|
|
|
|
|
|
//
|
|
// Set a mesh control point to the specified values
|
|
//
|
|
|
|
BOOL
|
|
Mesh::setMeshPoint(
|
|
INT row,
|
|
INT col,
|
|
INT x,
|
|
INT y
|
|
)
|
|
{
|
|
// special case for mesh control points along the border
|
|
|
|
if ((row == 0 && y != 0) ||
|
|
(row == gridRows-1 && y != dstSize.cy-1) ||
|
|
(col == 0 && x != 0) ||
|
|
(col == gridCols-1 && x != dstSize.cx-1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
double tx, ty;
|
|
|
|
tx = (double) x / (dstSize.cx - 1);
|
|
ty = (double) y / (dstSize.cy - 1);
|
|
|
|
// quick test to ensure the mesh control point
|
|
// is well-ordered relative to its four neighbors
|
|
|
|
if (col > 0 && tx <= mesh[indexOf(row, col-1)].x ||
|
|
col < gridCols-1 && tx >= mesh[indexOf(row, col+1)].x ||
|
|
row > 0 && ty <= mesh[indexOf(row-1, col)].y ||
|
|
row < gridRows-1 && ty >= mesh[indexOf(row+1, col)].y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
INT index = indexOf(row, col);
|
|
PointX ptx = mesh[index];
|
|
|
|
mesh[index].x = tx;
|
|
mesh[index].y = ty;
|
|
|
|
// verify the mesh row and mesh column is single-valued
|
|
|
|
if (verifyRow(row) && verifyColumn(col))
|
|
return TRUE;
|
|
|
|
// if not, reject the mesh control point
|
|
|
|
mesh[index] = ptx;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify that the specified mesh row is well-ordered
|
|
//
|
|
|
|
BOOL
|
|
Mesh::verifyRow(INT row)
|
|
{
|
|
INT count;
|
|
PointX* points = getMeshRowBeziers(row, &count, dstSize.cx, dstSize.cy);
|
|
|
|
while (count > 3)
|
|
{
|
|
if (!verifyBezierX(points))
|
|
return FALSE;
|
|
|
|
points += 3;
|
|
count -= 3;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Verify that the specified mesh column is well-ordered
|
|
//
|
|
|
|
BOOL
|
|
Mesh::verifyColumn(INT col)
|
|
{
|
|
INT count;
|
|
PointX* points = getMeshColumnBeziers(col, &count, dstSize.cx, dstSize.cy);
|
|
|
|
while (count > 3)
|
|
{
|
|
if (!verifyBezierY(points))
|
|
return FALSE;
|
|
|
|
points += 3;
|
|
count -= 3;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if a Bezier segment is well-ordered in the x-direction
|
|
//
|
|
|
|
BOOL
|
|
Mesh::verifyBezierX(
|
|
PointX* pts
|
|
)
|
|
{
|
|
double a, b, c, d;
|
|
|
|
// get the quadratic equation for x'(t)
|
|
|
|
a = 3.0 * (3*pts[1].x + pts[3].x - pts[0].x - 3*pts[2].x);
|
|
b = 6.0 * (pts[0].x - 2*pts[1].x + pts[2].x);
|
|
c = 3.0 * (pts[1].x - pts[0].x);
|
|
|
|
// solve t for x'(t) = 0
|
|
|
|
d = b*b - 4*a*c;
|
|
|
|
if (d <= 0 || a == 0)
|
|
return TRUE;
|
|
|
|
// if both solution are between 0 <= t <= 1
|
|
// then the Bezier curve is not well-ordered in x-direction
|
|
|
|
double t1, t2;
|
|
|
|
d = sqrt(d);
|
|
a = 0.5 / a;
|
|
|
|
t1 = (d - b) * a;
|
|
t2 = -(d + b) * a;
|
|
|
|
return t1 < 0 || t1 > 1 ||
|
|
t2 < 0 || t2 > 1;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if a Bezier segment is well-ordered in the y-direction
|
|
//
|
|
|
|
BOOL
|
|
Mesh::verifyBezierY(
|
|
PointX* pts
|
|
)
|
|
{
|
|
double a, b, c, d;
|
|
|
|
// get the quadratic equation for y'(t)
|
|
|
|
a = 3.0 * (3*pts[1].y + pts[3].y - pts[0].y - 3*pts[2].y);
|
|
b = 6.0 * (pts[0].y - 2*pts[1].y + pts[2].y);
|
|
c = 3.0 * (pts[1].y - pts[0].y);
|
|
|
|
// solve t for y'(t) = 0
|
|
|
|
d = b*b - 4*a*c;
|
|
|
|
if (d <= 0 || a == 0)
|
|
return TRUE;
|
|
|
|
// if both solution are between 0 <= t <= 1
|
|
// then the Bezier curve is not well-ordered in y-direction
|
|
|
|
double t1, t2;
|
|
|
|
d = sqrt(d);
|
|
a = 0.5 / a;
|
|
|
|
t1 = (d - b) * a;
|
|
t2 = -(d + b) * a;
|
|
|
|
return t1 < 0 || t1 > 1 ||
|
|
t2 < 0 || t2 > 1;
|
|
}
|
|
|
|
|
|
//
|
|
// Return a new MeshIterator object so that we can
|
|
// step along the y-direction and get the scale factors
|
|
// for each scanline.
|
|
//
|
|
|
|
MeshIterator*
|
|
Mesh::getYIterator(
|
|
INT srcWidth,
|
|
INT dstWidth,
|
|
INT ySteps
|
|
)
|
|
{
|
|
MeshIterator* iterator;
|
|
|
|
iterator = new MeshIterator(srcWidth, dstWidth, ySteps, gridCols);
|
|
|
|
if (iterator == NULL)
|
|
Error("Out of memory\n");
|
|
|
|
PointX* ptx;
|
|
INT count;
|
|
|
|
for (INT col=0; col < gridCols; col++)
|
|
{
|
|
ptx = getMeshColumnBeziers(col, &count, dstWidth-1, ySteps-1);
|
|
|
|
// swap x and y coordinates
|
|
|
|
for (INT i=0; i < count; i++)
|
|
{
|
|
double t = ptx[i].x;
|
|
ptx[i].x = ptx[i].y;
|
|
ptx[i].y = t;
|
|
}
|
|
|
|
iterator->addCurve(ptx, count);
|
|
}
|
|
|
|
return iterator;
|
|
}
|
|
|
|
|
|
//
|
|
// Return a new MeshIterator object so that we can
|
|
// step along the y-direction and get the scale factors
|
|
// for each vertical column of pixels.
|
|
//
|
|
|
|
MeshIterator*
|
|
Mesh::getXIterator(
|
|
INT srcHeight,
|
|
INT dstHeight,
|
|
INT xSteps
|
|
)
|
|
{
|
|
MeshIterator* iterator;
|
|
|
|
iterator = new MeshIterator(srcHeight, dstHeight, xSteps, gridRows);
|
|
|
|
if (iterator == NULL)
|
|
Error("Out of memory\n");
|
|
|
|
PointX* ptx;
|
|
INT count;
|
|
|
|
for (INT row=0; row < gridRows; row++)
|
|
{
|
|
ptx = getMeshRowBeziers(row, &count, xSteps-1, dstHeight-1);
|
|
iterator->addCurve(ptx, count);
|
|
}
|
|
|
|
return iterator;
|
|
}
|
|
|
|
|
|
//
|
|
// MeshIterator constructor
|
|
//
|
|
|
|
MeshIterator::MeshIterator(
|
|
INT srcLen,
|
|
INT dstLen,
|
|
INT steps,
|
|
INT maxCurves
|
|
)
|
|
{
|
|
if (maxCurves > MAXCURVES)
|
|
Error("Too many curves in MeshIterator constructor\n");
|
|
|
|
this->srcLen = srcLen;
|
|
this->dstLen = dstLen;
|
|
this->steps = steps;
|
|
this->maxCurves = maxCurves;
|
|
curveCount = 0;
|
|
|
|
for (INT i=0; i < MAXCURVES; i++)
|
|
curves[i] = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// MeshIterator - destructor
|
|
//
|
|
|
|
MeshIterator::~MeshIterator()
|
|
{
|
|
for (INT i=0; i < MAXCURVES; i++)
|
|
delete curves[i];
|
|
}
|
|
|
|
|
|
//
|
|
// MeshIterator - add another curve
|
|
//
|
|
|
|
VOID
|
|
MeshIterator::addCurve(
|
|
const PointX* pts,
|
|
INT count
|
|
)
|
|
{
|
|
if (curveCount == maxCurves)
|
|
Error("Program error in MeshIterator::addCurve\n");
|
|
|
|
FlatCurve* curve = new FlatCurve(pts, count);
|
|
|
|
if (curve == NULL)
|
|
Error("Out of memory\n");
|
|
|
|
curves[curveCount++] = curve;
|
|
}
|
|
|
|
|
|
//
|
|
// MeshIterator - get out positions
|
|
//
|
|
|
|
VOID
|
|
MeshIterator::getOutPos(
|
|
INT index,
|
|
double* outpos
|
|
)
|
|
{
|
|
if (curveCount != maxCurves ||
|
|
index < 0 || index >= steps)
|
|
{
|
|
Error("Program error in MeshIterator::getOutPos\n");
|
|
}
|
|
|
|
INT i, j;
|
|
double scale;
|
|
double x = index;
|
|
|
|
for (i=0; i < maxCurves; i++)
|
|
stops[i] = curves[i]->getPos(x);
|
|
|
|
scale = 1.0 / (srcLen-1);
|
|
|
|
for (i=0; i < srcLen; i++)
|
|
{
|
|
j = i * (maxCurves-1) / (srcLen-1);
|
|
|
|
INT i0 = (srcLen-1) * j / (maxCurves-1);
|
|
|
|
if (i == i0)
|
|
outpos[i] = stops[j];
|
|
else
|
|
{
|
|
INT i1 = (srcLen-1) * (j+1) / (maxCurves-1);
|
|
|
|
outpos[i] = stops[j] + (stops[j+1] - stops[j]) * (i-i0) / (i1-i0);
|
|
}
|
|
}
|
|
|
|
outpos[srcLen] = dstLen;
|
|
}
|
|
|
|
|
|
//
|
|
// FlatCurve constructor
|
|
//
|
|
|
|
FlatCurve::FlatCurve(
|
|
const PointX* pts,
|
|
INT count
|
|
)
|
|
{
|
|
capacity = elementCount = 0;
|
|
allocIncr = count * 3;
|
|
allocIncr = (allocIncr + 31) & ~31;
|
|
pointArray = NULL;
|
|
lastIndex = 0;
|
|
|
|
while (count > 3)
|
|
{
|
|
addBezierFlatten(pts);
|
|
count -= 3;
|
|
pts += 3;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FlatCurve destructor
|
|
//
|
|
|
|
FlatCurve::~FlatCurve()
|
|
{
|
|
free(pointArray);
|
|
}
|
|
|
|
|
|
//
|
|
// FlatCurve - add a flattened bezier segment
|
|
//
|
|
|
|
VOID
|
|
FlatCurve::addBezierFlatten(
|
|
const PointX* pts
|
|
)
|
|
{
|
|
BOOL flatEnough;
|
|
double dx, dy, distSq;
|
|
PointX tempPts[4];
|
|
|
|
static const double epsilon = 0.00001;
|
|
static const double flatness = 1;
|
|
static const double flatness2 = flatness*flatness;
|
|
|
|
//
|
|
// Determine if the Bezier curve is flat enough
|
|
//
|
|
// Algorithm
|
|
// Given 3 points (Ax, Ay), (Bx, By), and (Cx, Cy),
|
|
// the distance from C to line AB is:
|
|
// dx = Bx - Ax
|
|
// dy = By - Ay
|
|
// L = sqrt(dx*dx + dy*dy)
|
|
// dist = (dy * (Cx - Ax) - dx * (Cy - Ay))/ L
|
|
//
|
|
|
|
dx = pts[3].x - pts[0].x;
|
|
dy = pts[3].y - pts[0].y;
|
|
distSq = dx*dx + dy*dy;
|
|
|
|
if (distSq < epsilon)
|
|
{
|
|
// if P0 and P3 are too close
|
|
|
|
flatEnough = PointX::getSquareDist(pts[0], pts[1]) <= flatness2 &&
|
|
PointX::getSquareDist(pts[2], pts[3]) <= flatness2;
|
|
}
|
|
else
|
|
{
|
|
// check if P1 is close enough to line P0-P3
|
|
|
|
double s;
|
|
|
|
s = dy*(pts[1].x - pts[0].x) - dx*(pts[1].y - pts[0].y);
|
|
s *= s;
|
|
distSq *= flatness2;
|
|
|
|
if (s > distSq)
|
|
flatEnough = FALSE;
|
|
else
|
|
{
|
|
// check if P2 is close enough to line P0-P3
|
|
|
|
s = dy*(pts[2].x - pts[0].x) - dx*(pts[2].y - pts[0].y);
|
|
flatEnough = (s*s <= distSq);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If Bezier segment is already flat enough, we're done
|
|
//
|
|
|
|
if (flatEnough)
|
|
{
|
|
addLine(pts[0], pts[3]);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Otherwise, we need to subdivide
|
|
//
|
|
|
|
tempPts[0] = pts[0];
|
|
tempPts[1].x = (pts[0].x + pts[1].x) * 0.5;
|
|
tempPts[1].y = (pts[0].y + pts[1].y) * 0.5;
|
|
tempPts[2].x = (pts[0].x + pts[2].x) * 0.25 + pts[1].x * 0.5;
|
|
tempPts[2].y = (pts[0].y + pts[2].y) * 0.25 + pts[1].y * 0.5;
|
|
tempPts[3].x = (pts[0].x + pts[3].x) * 0.125 +
|
|
(pts[1].x + pts[2].x) * 0.375;
|
|
tempPts[3].y = (pts[0].y + pts[3].y) * 0.125 +
|
|
(pts[1].y + pts[2].y) * 0.375;
|
|
|
|
addBezierFlatten(tempPts);
|
|
|
|
tempPts[0] = tempPts[3];
|
|
tempPts[1].x = (pts[1].x + pts[3].x) * 0.25 + pts[2].x * 0.5;
|
|
tempPts[1].y = (pts[1].y + pts[3].y) * 0.25 + pts[2].y * 0.5;
|
|
tempPts[2].x = (pts[2].x + pts[3].x) * 0.5;
|
|
tempPts[2].y = (pts[2].y + pts[3].y) * 0.5;
|
|
tempPts[3] = pts[3];
|
|
|
|
addBezierFlatten(tempPts);
|
|
}
|
|
|
|
|
|
//
|
|
// FlatCurve - add a line segment
|
|
//
|
|
|
|
VOID
|
|
FlatCurve::addLine(
|
|
const PointX& p1,
|
|
const PointX& p2
|
|
)
|
|
{
|
|
// make sure we have enough space
|
|
|
|
if (capacity < elementCount+2)
|
|
{
|
|
capacity += allocIncr;
|
|
pointArray = (PointX*) realloc(pointArray, capacity*sizeof(PointX));
|
|
|
|
if (pointArray == NULL)
|
|
Error("Out of memory\n");
|
|
}
|
|
|
|
// add the first end point of the line, if necessary
|
|
|
|
if (elementCount == 0 ||
|
|
p1.x != pointArray[elementCount-1].x ||
|
|
p1.y != pointArray[elementCount-1].y)
|
|
{
|
|
pointArray[elementCount++] = p1;
|
|
}
|
|
|
|
// add the second end point
|
|
|
|
pointArray[elementCount++] = p2;
|
|
}
|
|
|
|
|
|
//
|
|
// FlatCurve - calculate the value of the curve at a given position
|
|
//
|
|
|
|
double
|
|
FlatCurve::getPos(
|
|
double x
|
|
)
|
|
{
|
|
while (lastIndex < elementCount && pointArray[lastIndex].x < x)
|
|
lastIndex++;
|
|
|
|
if (lastIndex == elementCount)
|
|
return pointArray[elementCount-1].y;
|
|
|
|
if (pointArray[lastIndex].x == x)
|
|
return pointArray[lastIndex].y;
|
|
|
|
double x0 = pointArray[lastIndex-1].x;
|
|
double y0 = pointArray[lastIndex-1].y;
|
|
|
|
return y0 + (x - x0) * (pointArray[lastIndex].y - y0) /
|
|
(pointArray[lastIndex].x - x0);
|
|
}
|
|
|