Files
FC1/Editor/CurveObject.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

617 lines
14 KiB
C++

// CCurveObject : implementaion file
//
// CurveObject Class, data representaion of the curve.
// Functionality :
// 1. Setting, retrieving and moving knots.
// 2. Calculation the curve point
//
// Copyright Johan Janssens, 2001 (jjanssens@mail.ru)
// Feel free to use and distribute. May not be sold for profit.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
// If the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage of buiness that this
// product may cause
//
// Please use and enjoy. Please let me know of any bugs/mods/improvements
// that you have found/implemented and I will fix/incorporate them into
// this file
#include "stdafx.h"
#include "CurveObject.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//CCurveDllImpl CCurveObject::m_dllImpl; //Initialise CCurveDllImpl
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
IMPLEMENT_SERIAL(CCurveObject, CObject, 1)
CCurveObject::CCurveObject()
{
m_bIsValid = false; //Curve Object not yet created
//Initialise members
m_bParabolic = false;
m_bAveraging = false;
m_bDiscreetY = false;
m_nSmoothing = 1;
}
CCurveObject::~CCurveObject()
{
//clean up
RemoveAllKnots();
}
//////////////////////////////////////////////////////////////////////
//Curve Funtions
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//Curve Creation
BOOL CCurveObject::CreateCurve(LPCTSTR strName, CRect rcClipRect, UINT nSmoothing, DWORD dwFlags)
{
//Normalise ViewPort Rect
rcClipRect.NormalizeRect();
if(!rcClipRect.IsRectNull())
{
m_strName = strName; //Set Curve Name
m_rcClipRect = rcClipRect; //Set Cliprect
//Add Head Knot
CPoint ptHead;
ptHead.x = rcClipRect.left;
ptHead.y = rcClipRect.bottom;
CKnot* pKnotHead = new CKnot;
pKnotHead->SetPoint(ptHead);
m_arrKnots.Add(pKnotHead);
//Add Tail Knot
CPoint ptTail;
ptTail.x = rcClipRect.right;
ptTail.y = rcClipRect.top;
CKnot* pKnotTail = new CKnot;
pKnotTail->SetPoint(ptTail);
m_arrKnots.Add(pKnotTail);
m_bIsValid = true;
}
else
m_bIsValid = false;
return m_bIsValid;
}
//////////////////////////////////////////////////////////////////////
//Curve Setting
/*void CCurveObject::SetCurveInfo(CURVEINFO tagInfo)
{
}
void CCurveObject::GetCurveInfo(LPCURVEINFO tagInfo)
{
}*/
//////////////////////////////////////////////////////////////////////
//Curve Calculation
UINT CCurveObject::GetCurveY(UINT ptX)
{
ASSERT(IsValid()); //Verify Curve Object
std::vector<CPoint> ptArray;
for(int pos = 0; pos < m_arrKnots.GetSize(); pos++)
{
CKnot* Knot = GetKnot(pos);
CPoint pt;
pt.x = Knot->x;
pt.y = Knot->y;
ptArray.push_back(pt);
}
double iY = 0;//CCurveDllImpl::Interpolate(&ptArray, ptX, m_bParabolic, m_bAveraging, m_nSmoothing);
//Clip To Tail Knot
CKnot* pTail = GetTailKnot();
iY = __max(iY, (double)pTail->y);
CKnot* pHead = GetHeadKnot();
iY = __min(iY, (double)(pHead->y - 1));
if(m_bDiscreetY) {
//Clip Curve To Next Knot
CKnot* pKnotNear =
FindNearKnot(CPoint(ptX, iY), KNOT_RIGHT);
iY = __max(iY, pKnotNear->y);
}
return iY;
}
//////////////////////////////////////////////////////////////////////
//Curve Hit Testing
void CCurveObject::HitTest(CPoint point, LPHITINFO pHitInfo)
{
UINT iIndex; //Index of Knot HTKNOT
POINT ptCurve; //Exact point on curve HTCURVE
pHitInfo->ptHit = point; //Set cursor position
//if(PtOnKnot(point, 3, &iIndex))
if(PtOnKnot(point, 1000, &iIndex))
{
pHitInfo->wHitCode = HTKNOT;
pHitInfo->nKnotIndex = iIndex;
}
//else if(PtOnCurve(point, 0, &ptCurve))
else if(PtOnCurve(point, 1000, &ptCurve))
{
pHitInfo->wHitCode = HTCURVE;
pHitInfo->ptCurve = ptCurve;
}
else pHitInfo->wHitCode = HTCANVAS;
}
BOOL CCurveObject::PtOnCurve(CPoint ptHit, UINT nInterval, POINT* pt)
{
ASSERT(IsValid()); //Verify Curve Object
if(GetKnotCount() == -1)
return FALSE; //Curve Has No Knots
int iY, iX;
BOOL b = FALSE;
for(int i = 0; i<5; i++)
{
int iXdiff;
if(i != 0)
(i%2 != 0) ? iXdiff = (+1)*((i+1)/2) : iXdiff = (-1)*(i/2);
else iXdiff = 0;
iX = ptHit.x + iXdiff;
iY = GetCurveY(iX);
if((iY > ptHit.y - 2) && (iY < ptHit.y + 2)) {
b = TRUE;
break;
}
}
//Return curve exact point
if(b) {
pt->x = iX;
pt->y = iY;
}
return b;
}
BOOL CCurveObject::PtOnKnot(CPoint ptHit, UINT nInterval, UINT* nIndex)
{
ASSERT(IsValid()); //Verify Curve Object
if(GetKnotCount() == -1)
return FALSE; //Curve Has No Knots
int iIndex = - 1;
for(int pos = 0; pos <= GetKnotCount(); pos++)
{
CKnot* pKnot = GetKnot(pos);
CRect rcKnot;
rcKnot.left = pKnot->x - nInterval;
rcKnot.right = pKnot->x + nInterval;
rcKnot.top = pKnot->y - nInterval;
rcKnot.bottom = pKnot->y + nInterval;
if(rcKnot.PtInRect(ptHit)) {
iIndex = pos;
break;
}
}
if(iIndex != -1) {
*nIndex = iIndex;
return TRUE;
}
else return FALSE;
}
//////////////////////////////////////////////////////////////////////
//Knot Functions
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//Operations
UINT CCurveObject::InsertKnot(CPoint ptCntKnot)
{
ASSERT(IsValid()); //Verify Curve Object
//Create new Knot object
CKnot* pKnot = new CKnot;
pKnot->SetPoint(ptCntKnot);
int iIndex = 0;
std::vector<double> arrValues;
double dValueToSeek = ptCntKnot.x;
for(int pos = 0; pos <= GetKnotCount(); pos++)
{
CKnot* pKnot = GetKnot(pos);
arrValues.push_back(pKnot->x);
}
//CCurveDllImpl dllCurve;
double dIndex = 0;//CCurveDllImpl::IndexOfClosestValue(&arrValues, dValueToSeek) - 1;
double dValue = arrValues[(int)dIndex];
if(dValue < dValueToSeek)
dIndex += 1;
iIndex = (int) dIndex;
m_arrKnots.InsertAt(iIndex, pKnot);
return iIndex;
}
BOOL CCurveObject::MoveKnot(CPoint ptMoveTo, UINT nIndex)
{
VERIFY(IsValid()); //Verify Curve Object
CPoint ptNext, ptPrev;
CKnot* pKnotNext = GetNextKnot(nIndex);
CKnot* pKnotPrev = GetPrevKnot(nIndex);
//Restrict x movemnt to Viewport boundaries
ptMoveTo.x = __min((int)ptMoveTo.x, (int) m_rcClipRect.right);
ptMoveTo.x = __max((int)ptMoveTo.x, (int) m_rcClipRect.left);
//Restrict x movement to next/prev knot
ptMoveTo.x = __min((int)ptMoveTo.x, (int)(pKnotNext->x) - 1);
ptMoveTo.x = __max((int)ptMoveTo.x, (int)(pKnotPrev->x) + 1);
//Restrict y movement to Viewport boundaries
ptMoveTo.y = __min((int)ptMoveTo.y, (int) m_rcClipRect.bottom);
ptMoveTo.y = __max((int)ptMoveTo.y, (int) m_rcClipRect.top);
//Restrict y movement (discreet y)
if(m_bDiscreetY) {
ptMoveTo.y = __max((int) ptMoveTo.y, (int)pKnotNext->y);
ptMoveTo.y = __min((int) ptMoveTo.y, (int)pKnotPrev->y);
}
CKnot* pKnot = GetKnot(nIndex);
BOOL b = (*pKnot != ptMoveTo);
*pKnot = ptMoveTo;
return b;
}
BOOL CCurveObject::RemoveKnot(UINT nIndex)
{
ASSERT(IsValid()); //Verify Curve Object
BOOL b = TRUE;
if(nIndex > GetKnotCount())
b = FALSE;
else
{
CKnot *pKnot = (CKnot*) m_arrKnots.GetAt(nIndex);
delete pKnot;
m_arrKnots.RemoveAt(nIndex);
}
return b;
}
BOOL CCurveObject::RemoveAllKnots()
{
ASSERT(IsValid()); //Verify Curve Object
BOOL b = TRUE;
for(int pos = 0; pos < m_arrKnots.GetSize(); pos++)
{
CKnot* pKnot = (CKnot*) m_arrKnots.GetAt(pos);
delete pKnot;
}
m_arrKnots.RemoveAll();
return b;
}
///////////////////////////////////////////////////////////////////////
//Searching
CKnot* CCurveObject::FindNearKnot(CPoint pt, UINT nDirection)
{
ASSERT(IsValid()); //Verify Curve Object
std::vector<double> arrValues;
double dValueToSeek = pt.x;
for(int pos = 0; pos < m_arrKnots.GetSize(); pos++)
{
CKnot* pKnot = (CKnot*) m_arrKnots.GetAt(pos);
arrValues.push_back(pKnot->x);
}
//CCurveDllImpl dllCurve;
double dIndex = 0;//dllCurve.IndexOfClosestValue(&arrValues, dValueToSeek) - 1;
double dValue = arrValues[(int)dIndex];
if((dValue <= dValueToSeek) && (nDirection == KNOT_RIGHT))
dIndex += 1;
if((dValue >= dValueToSeek) && (nDirection == KNOT_LEFT))
dIndex -= 1;
if(dIndex > GetKnotCount())
dIndex = GetKnotCount();
CKnot* pKnotNear = GetKnot((int) dIndex);
return pKnotNear;
}
///////////////////////////////////////////////////////////////////////
//Retrieval/Iteration
CKnot* CCurveObject::GetHeadKnot()
{
ASSERT(IsValid()); //Verify Curve Object
CKnot* pKnot; //Knot Object
int iIndex = m_arrKnots.GetUpperBound();
if(iIndex != -1)
{
pKnot = (CKnot*) m_arrKnots.GetAt(0);
ASSERT(pKnot != NULL);
}
else pKnot = NULL;
return pKnot;
}
CKnot* CCurveObject::GetTailKnot()
{
ASSERT(IsValid()); //Verify Curve Object
CKnot* pKnot; //Knot Object
int iIndex = m_arrKnots.GetUpperBound();
if(iIndex != -1)
{
pKnot = (CKnot*) m_arrKnots.GetAt(iIndex);
ASSERT(pKnot != NULL);
}
else pKnot = NULL;
return pKnot;
}
CKnot* CCurveObject::GetKnot(UINT nIndex)
{
ASSERT(IsValid()); //Verify Curve Object
CKnot* pKnot; //Knot Object
int iIndex = m_arrKnots.GetUpperBound();
if(iIndex != -1)
{
pKnot = (CKnot*) m_arrKnots.GetAt(nIndex);
ASSERT(pKnot != NULL);
}
else pKnot = NULL;
return pKnot;
}
CKnot* CCurveObject::GetNextKnot(UINT nIndex)
{
ASSERT(IsValid()); //Verify Curve Object
CKnot* pKnot; //Knot pointer
int iNextIndex = nIndex + 1;
if(iNextIndex > m_arrKnots.GetUpperBound())
pKnot = NULL;
else pKnot = (CKnot*) m_arrKnots.GetAt(iNextIndex);
return pKnot;
}
CKnot* CCurveObject::GetPrevKnot(UINT nIndex)
{
ASSERT(IsValid()); //Verify Curve Object
CKnot* pKnot; //Knot pointer
int iPrevIndex = nIndex - 1;
if(iPrevIndex < 0)
pKnot = NULL;
else pKnot = (CKnot*) m_arrKnots.GetAt(iPrevIndex);
return pKnot;
}
///////////////////////////////////////////////////////////////////////
//Staus
UINT CCurveObject::GetKnotCount()
{
ASSERT(IsValid()); //Verify Curve Object
int iKnots = m_arrKnots.GetUpperBound();
return iKnots;
}
///////////////////////////////////////////////////////////////////////
//CCurveObject Serialisation //
///////////////////////////////////////////////////////////////////////
void CCurveObject::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
int iSize = m_arrKnots.GetSize();
ar << iSize;
for(int pos = 0; pos < iSize; pos++)
{
CKnot* pKnot = (CKnot* )m_arrKnots.GetAt(pos);
pKnot->Serialize(ar);
}
CRect rc = m_rcClipRect;
ar << rc.left << rc.bottom << rc.right << rc.top;
ar << m_strName;
ar << m_bParabolic;
ar << m_bAveraging;
ar << m_nSmoothing;
}
else
{
int iSize;
ar >> iSize;
//RemoveAllKnots();
for(int pos = 0; pos < iSize; pos++)
{
CKnot* pKnot = new CKnot;
pKnot->Serialize(ar);
m_arrKnots.Add(pKnot);
}
CRect rc;
ar >> rc.left >> rc.bottom >> rc.right >> rc.top;
m_rcClipRect = rc;
ar >> m_strName;
ar >> m_bParabolic;
ar >> m_bAveraging;
ar >> m_nSmoothing;
m_bIsValid = true; //Set Valid Automaticaly
}
}
void CCurveObject::Serialize( CXmlArchive &xmlAr )
{
if (xmlAr.bLoading)
{
// Loading
CLogFile::WriteLine("Loading Curve settings...");
XmlNodeRef curve = xmlAr.root->findChild( "Curve" );
if (!curve)
return;
curve->getAttr( "Name",m_strName );
curve->getAttr( "Parabolic",m_bParabolic );
curve->getAttr( "Averaging",m_bAveraging );
curve->getAttr( "Smoothing",m_nSmoothing );
curve->getAttr( "ClipLeft",m_rcClipRect.left );
curve->getAttr( "ClipRight",m_rcClipRect.right );
curve->getAttr( "ClipTop",m_rcClipRect.top );
curve->getAttr( "ClipBottom",m_rcClipRect.bottom );
for(int pos = 0; pos < curve->getChildCount(); pos++)
{
CKnot* pKnot = new CKnot;
XmlNodeRef knot = curve->getChild(pos);
knot->getAttr( "X",pKnot->x );
knot->getAttr( "Y",pKnot->y );
knot->getAttr( "Val",pKnot->dwData );
m_arrKnots.Add(pKnot);
}
}
else
{
// Storing
CLogFile::WriteLine("Storing Curve settings...");
XmlNodeRef curve = xmlAr.root->newChild( "Curve" );
curve->setAttr( "Name",m_strName );
curve->setAttr( "Parabolic",m_bParabolic );
curve->setAttr( "Averaging",m_bAveraging );
curve->setAttr( "Smoothing",m_nSmoothing );
curve->setAttr( "ClipLeft",m_rcClipRect.left );
curve->setAttr( "ClipRight",m_rcClipRect.right );
curve->setAttr( "ClipTop",m_rcClipRect.top );
curve->setAttr( "ClipBottom",m_rcClipRect.bottom );
for(int pos = 0; pos < m_arrKnots.GetSize(); pos++)
{
CKnot* pKnot = (CKnot* )m_arrKnots.GetAt(pos);
XmlNodeRef knot = curve->newChild( "Knot" );
knot->setAttr( "X",pKnot->x );
knot->setAttr( "Y",pKnot->y );
knot->setAttr( "Val",pKnot->dwData );
}
}
}