// 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 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 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 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 ); } } }