Files
2026-06-01 12:46:52 +02:00

350 lines
11 KiB
C++

// Name: IdvSpline.cpp
//
// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION ***
//
// Copyright (c) 2001-2004 IDV, Inc.
// All Rights Reserved.
//
// IDV, Inc.
// 1233 Washington St. Suite 610
// Columbia, SC 29201
// Voice: (803) 799-1699
// Fax: (803) 931-0320
// Web: http://www.idvinc.com
//
// This software is supplied under the terms of a license agreement or
// nondisclosure agreement with Interactive Data Visualization and may not
// be copied or disclosed except in accordance with the terms of that
// agreement.
#pragma warning (disable : 4786)
#include "../Debug.h"
#include "IdvSpline.h"
#include "../LibRandom_Source/IdvRandom.h"
#include <map>
#include <algorithm>
using namespace std;
static map<string, CIdvBezierSpline*> g_mSplineCache;
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::CIdvBezierSpline
CIdvBezierSpline::CIdvBezierSpline( ) :
m_fMin(0.0f),
m_fMax(1.0f),
m_fVariance(0.0f)
{
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::CIdvBezierSpline
CIdvBezierSpline::CIdvBezierSpline(const string& strInput)
{
CIdvBezierSpline* pLookup = g_mSplineCache[strInput];
if (!pLookup)
{
Parse(strInput);
CreateEvenlySpacedPoints(c_nNumEvalPoints);
CIdvBezierSpline* pCachedCopy = new CIdvBezierSpline(*this);
g_mSplineCache[strInput] = pCachedCopy;
}
else
*this = *pLookup;
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::CIdvBezierSpline
CIdvBezierSpline::CIdvBezierSpline(const CIdvBezierSpline& cRight)
{
*this = cRight;
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::CIdvBezierSpline
CIdvBezierSpline::~CIdvBezierSpline( )
{
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::ClearCache
void CIdvBezierSpline::ClearCache(void)
{
for (map<string, CIdvBezierSpline*>::iterator i = g_mSplineCache.begin( ); i != g_mSplineCache.end( ); ++i)
{
delete i->second;
i->second = NULL;
}
g_mSplineCache.clear( );
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::Evaluate
float CIdvBezierSpline::Evaluate(float fPercent) const
{
float fValue = 0.0f;
if (m_vEvenlySpacedPoints.size( ) == c_nNumEvalPoints)
{
// new way, interpolated
int nClosest = int(fPercent * float(c_nNumEvalPoints - 1));
if (nClosest == c_nNumEvalPoints - 1)
{
fValue = m_vEvenlySpacedPoints[nClosest][1];
}
else
{
float fInterpPercent = (fPercent - (nClosest * c_fEvalPointSpacing)) / c_fEvalPointSpacing;
fValue = VecInterpolate(m_vEvenlySpacedPoints[nClosest][1], m_vEvenlySpacedPoints[nClosest + 1][1], fInterpPercent);
}
fValue = fValue * (m_fMax - m_fMin) + m_fMin;
static CIdvRandom cRandom;
fValue += static_cast<float>(cRandom.GetUniform(-m_fVariance, m_fVariance));
}
return fValue;
}
///////////////////////////////////////////////////////////////////////
// NextToken
static const char* NextToken(const char* pInput, char* pToken)
{
while (isspace(*pInput))
pInput++;
sscanf(pInput, "%s", pToken);
return pInput + strlen(pToken);
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::Parse
void CIdvBezierSpline::Parse(const string& strInput)
{
const char* pInput = strInput.c_str( );
char szToken[256];
pInput = NextToken(pInput, szToken);
if (strcmp(szToken, "BezierSpline") == 0)
{
// get min, max, variance
pInput = NextToken(pInput, szToken);
m_fMin = static_cast<float>(atof(szToken));
pInput = NextToken(pInput, szToken);
m_fMax = static_cast<float>(atof(szToken));
pInput = NextToken(pInput, szToken);
m_fVariance = static_cast<float>(atof(szToken));
// match '{'
pInput = NextToken(pInput, szToken);
if (szToken[0] == '{')
{
// for each control point, get x,y control point, x,y tangent, and
// tangent length
pInput = NextToken(pInput, szToken);
int nNumControlPoints = atoi(szToken);
for (int i = 0; i < nNumControlPoints; ++i)
{
float afControlPoint[2];
pInput = NextToken(pInput, szToken);
afControlPoint[0] = static_cast<float>(atof(szToken));
pInput = NextToken(pInput, szToken);
afControlPoint[1] = static_cast<float>(atof(szToken));
float afTangent[2];
pInput = NextToken(pInput, szToken);
afTangent[0] = static_cast<float>(atof(szToken));
pInput = NextToken(pInput, szToken);
afTangent[1] = static_cast<float>(atof(szToken));
float fTangentLength;
pInput = NextToken(pInput, szToken);
fTangentLength = static_cast<float>(atof(szToken));
AddControlPoint(afControlPoint, afTangent, fTangentLength);
}
// the last token should be '}', but we ignore it
}
}
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::Save
string CIdvBezierSpline::Save(void) const
{
string strOutput;
strOutput = "BezierSpline ";
strOutput += IdvFormatString("%g %g %g\n", m_fMin, m_fMax, m_fVariance);
strOutput += "{\n";
strOutput += IdvFormatString("\t%d\n", m_vControlPoints.size( ));
for (unsigned int i = 0; i < m_vControlPoints.size( ); ++i)
strOutput += IdvFormatString("\t%g %g %g %g %g\n", m_vControlPoints[i][0], m_vControlPoints[i][1], m_vControlPointTangents[i][0], m_vControlPointTangents[i][1], m_vControlPointTangentLengths[i]);
strOutput += "\n";
strOutput += "}\n";
return strOutput;
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::ScaledVariance
float CIdvBezierSpline::ScaledVariance(float fPercent) const
{
float fValue = 0.0f;
if (m_vEvenlySpacedPoints.size( ) == c_nNumEvalPoints)
{
int nClosest = int(fPercent * float(c_nNumEvalPoints - 1) + 0.5f);
fValue = m_vEvenlySpacedPoints[nClosest][1];
static CIdvRandom cRandom;
fValue = static_cast<float>(cRandom.GetUniform(-m_fVariance * fValue, m_fVariance * fValue));
}
return fValue;
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::AddControlPoint
void CIdvBezierSpline::AddControlPoint(float afPoint[2], float afTangent[2], float fTangentLength)
{
CVec cPoint(afPoint[0], afPoint[1]);
CVec cTangent(afTangent[0], afTangent[1]);
cTangent.Normalize( );
if (!m_vControlPoints.empty( ))
{
unsigned int nLastEntry = m_vControlPoints.size( ) - 1;
m_vSplinePoints.push_back(m_vControlPoints[nLastEntry] + (m_vControlPointTangents[nLastEntry] * m_vControlPointTangentLengths[nLastEntry]));
m_vSplinePoints.push_back(cPoint - (cTangent * fTangentLength));
}
m_vControlPoints.push_back(cPoint);
m_vSplinePoints.push_back(cPoint);
m_vControlPointTangents.push_back(cTangent);
m_vControlPointTangentLengths.push_back(fTangentLength);
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::CreateEvenlySpacedPoints
void CIdvBezierSpline::CreateEvenlySpacedPoints(unsigned int nNumPoints)
{
vector<CVec> vRawPoints;
vRawPoints.resize(nNumPoints);
unsigned int i, j;
for (i = 0; i < nNumPoints; ++i)
vRawPoints[i] = EvaluateRawPoint(float(i) / float(nNumPoints));
m_vEvenlySpacedPoints.clear( );
m_vEvenlySpacedPoints.resize(nNumPoints);
unsigned int nIndex = 0;
m_vEvenlySpacedPoints[0] = m_vControlPoints[0];
//m_vEvenlySpacedPoints.push_back(m_vControlPoints[0]);
for (i = 1; i < nNumPoints - 1; ++i)
{
float fPercent = float(i) / float(nNumPoints);
for (j = nIndex; j < nNumPoints - 1; ++j)
{
if (fPercent >= vRawPoints[j][0] &&
fPercent < vRawPoints[j + 1][0])
{
nIndex = j;
break;
}
}
CVec cOut(fPercent, 0.0f, 0.0f, 0.0f, 0.0f);
fPercent = (fPercent - vRawPoints[nIndex][0]) / (vRawPoints[nIndex + 1][0] - vRawPoints[nIndex][0]);
cOut[1] = VecInterpolate(vRawPoints[nIndex][1], vRawPoints[nIndex + 1][1], fPercent);
//m_vEvenlySpacedPoints.push_back(cOut);
m_vEvenlySpacedPoints[i] = cOut;
}
//m_vEvenlySpacedPoints.push_back(m_vControlPoints[m_vControlPoints.size( ) - 1]);
m_vEvenlySpacedPoints[nNumPoints - 1] = m_vControlPoints[m_vControlPoints.size( ) - 1];
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::EvaluateRawPoint
inline CVec CIdvBezierSpline::EvaluateRawPoint(float fPercent)
{
CVec cValue(2);
int nSize = m_vControlPoints.size( );
if (nSize > 1)
{
// clip domain
fPercent = __max(0.0f, fPercent);
fPercent = __min(1.0f, fPercent);
// scale percent to span number of control points
fPercent *= nSize - 1;
// fractional amount between control points
float fFraction = fPercent - float(int(fPercent));
bool bDetermined = false;
for (int i = 0; i < nSize && !bDetermined; ++i)
{
if (i == nSize - 1)
{
cValue = m_vControlPoints[i];
bDetermined = true;
}
else if (fPercent >= float(i) && fPercent < float(i + 1))
{
cValue = SplineInterpolate(m_vSplinePoints[i * 3],
m_vSplinePoints[i * 3 + 1],
m_vSplinePoints[i * 3 + 2],
m_vSplinePoints[i * 3 + 3],
fFraction);
bDetermined = true;
}
}
}
return cValue;
}
///////////////////////////////////////////////////////////////////////
// CIdvBezierSpline::SplineInterpolate
inline CVec CIdvBezierSpline::SplineInterpolate(const CVec& cVec1, const CVec& cVec2, const CVec& cVec3, const CVec& cVec4, float x)
{
float c12[2] = { VecInterpolate(cVec1[0], cVec2[0], x), VecInterpolate(cVec1[1], cVec2[1], x) };
float c23[2] = { VecInterpolate(cVec2[0], cVec3[0], x), VecInterpolate(cVec2[1], cVec3[1], x) };
float c34[2] = { VecInterpolate(cVec3[0], cVec4[0], x), VecInterpolate(cVec3[1], cVec4[1], x) };
float c1223[2] = { VecInterpolate(c12[0], c23[0], x), VecInterpolate(c12[1], c23[1], x) };
float c2334[2] = { VecInterpolate(c23[0], c34[0], x), VecInterpolate(c23[1], c34[1], x) };
return CVec(VecInterpolate(c1223[0], c2334[0], x), VecInterpolate(c1223[1], c2334[1], x));
}