Signature analyzer class in C/C++ – Implementation   Leave a comment

#include "StdAfx.h"
#include "SignatureAnalyzer.h"

#include <cmath>

#include <iostream>

#include <stdlib.h>
#include <atlbase.h>

#import "msxml4.dll"

using namespace MSXML2;

using namespace std;

CSignatureAnalyzer::CSignatureAnalyzer(void)
{
	m_nNumberOfFailedStrokes = 0;
	m_nNumberOfStrokesInGenuineSig = 0;
	m_bHasErrors = false;
	m_bUseExtremeSchema = false;

		::CoInitialize(0);
}

CSignatureAnalyzer::~CSignatureAnalyzer(void)
{
	purgeSignatures();

	::CoUninitialize();
}

CSignature* CSignatureAnalyzer::AddGenuineSignature(__tistream& _stream) //stringstream _stream(ocx->getSigData());
{
	CSignature* sig_ptr = getSignatureFromStream(_stream);
	if(sig_ptr!=NULL)
	{
		m_sigVerifData.Reset();
		if(m_nNumberOfStrokesInGenuineSig==0)
		{
			//all genuine strokes MUST have the same Number of Strokes...
			m_nNumberOfStrokesInGenuineSig = (int)sig_ptr->m_strokesColl.size();
		}
		else if(m_nNumberOfStrokesInGenuineSig != sig_ptr->m_strokesColl.size())
		{
			m_szLastError = _T("This signature MUST contain the same number of strokes to be a GENUINE signature.");
			m_bHasErrors = true;
			delete sig_ptr;
			return NULL;
		}
		//analyze this signature...
		if(false==sig_ptr->ExtractFeatures(this))
		{
			delete sig_ptr;
			sig_ptr = NULL;
		}

		//finally add it to the collection of template signatures...
		m_sigColl.push_back(sig_ptr);
	}

	return sig_ptr;
}

CSignature* CSignatureAnalyzer::getSignatureFromStream(__tistream& _stream)
{
	CSignature* sig_ptr = NULL;
	if(_stream)
	{
		//the total points in polar coordinates (cartesian plane)
		string line;
		getline(_stream, line);
		int npoint_count = atoi(line.c_str());
		if(npoint_count > 0 && _stream)
		{
			//the number of strokes in this signature...
			getline(_stream, line);
			int nstrokes_count = atoi(line.c_str());

			vector all_points;

			//collect the points...
			while(_stream && npoint_count > 0)
			{
				getline(_stream, line);
				int nws = (int)line.find_first_of(' '); 
				if(string::npos==nws)
					break;

				CStrokePoint point;
				point.LogicalX = atoi(line.substr(0, nws).c_str());
				point.LogicalY = atoi(line.substr(nws + 1).c_str());

				//add this point...
				all_points.push_back(point);
				--npoint_count;
			}

			short* indices_ptr = NULL;

			if(nstrokes_count > 0)
			{
				indices_ptr = new short[nstrokes_count + 1];

				volatile int next_offset = 0;
				while(_stream && next_offset < nstrokes_count)
				{
					getline(_stream, line);
					indices_ptr[next_offset++] = atoi(line.c_str());
				}

				//to read the last block...
				indices_ptr[next_offset] = -1; //the last...

				sig_ptr = new CSignature();
				//classify the points by strokes...
				volatile short next_point = 0;
				next_offset = 0;
				volatile short next_stroke_id = 0;
				while(next_offset m_nStrokeNumber = ++next_stroke_id;
					//
					while(
							(next_point < indices_ptr[next_offset + 1]) || 
							(-1==indices_ptr[next_offset + 1] && next_point m_pointsColl.push_back(point);
					}
					//add the points that belongs to this stroke...
					sig_ptr->m_strokesColl.push_back(stroke_ptr);
					//next block of points...
					++next_offset;
				}
				//
			}

			delete []indices_ptr;
		}
	}else{
		m_bHasErrors	= true;
		m_szLastError	= _T("Signature Stream is not valid or End of Stream reached!");
	}

	return sig_ptr;
}

bool CSignatureAnalyzer::VerifySignature(__tistream& _stream, CSignature** ppSignature)
{
	bool bResult = false;
	if(ppSignature!=NULL)
		*ppSignature = NULL;
	CSignature* sig_ptr = getSignatureFromStream(_stream);
	//verify against the template results...
	if(sig_ptr!=NULL)
	{
		//analyze this signature...
		if(false==sig_ptr->ExtractFeatures(this))
		{
			m_szLastError = _T("Verification failed. This signature could not be extracted the features.");
			m_bHasErrors = true;

			delete sig_ptr;
			return false;
		}

		if(false==m_sigVerifData.m_bIsVerificationDataAvailable)
		{
			//prepare the template set of genuine signatures data...
			PrepareSignatureVerificationData();
		}
		m_nNumberOfFailedStrokes = (int)(m_sigVerifData.m_FullStrokesVerifDataColl.size() - sig_ptr->m_strokesColl.size());
		if(0 == m_nNumberOfFailedStrokes)
		{
			volatile int strokes_in_time = 0;
			//verify this signature using:
			vector success_parts;
			//compare against the prepared signature verification data...		
			for(int stroke = 0; stroke m_strokesColl[stroke];
				//
				//the duration between strokes
				if(stroke_ptr->m_dDuration >= fullStrokeData.m_dMinDuration &&
					stroke_ptr->m_dDuration m_parts.size(), fullStrokeData.m_verifParts.size());
				int nmax_parts = (int)max(stroke_ptr->m_parts.size(), fullStrokeData.m_verifParts.size());

				double parts_ratio = (nstroke_parts / (double)nmax_parts) * 100.0;

				//if the ratio between the number of parts is less than 66.66%, reject it and avoid the comparisons
				if(parts_ratio < m_stStrokeThresHolds.m_dMinRatioValidNumberOfStrokeParts){
					wcout << _T("stroke skipped: ") << stroke << endl;
					continue;
				}

				for(int ipart = 0 ; ipart m_parts[ipart];
					CStrokePartVerificationData& theVerifPart = fullStrokeData.m_verifParts[ipart];
					//
					//Have two authentication schemes: a probed scheme, and my original scheme:
					/*
					AUTHENTIC	: if (Mean(x) - 2*StdDev(x)) <= p(x) <= (Mean(x) + 2*StdDev(x))
					FORGED		: if p(x)  (Mean(x) + 2*StdDev(x))
					*/
					if(m_bUseExtremeSchema)
					{
						double dLeftMean_2StdDev	= (fabs(theVerifPart.m_dMeanValue)	- 
							m_stStrokeThresHolds.m_dStdDevMinThresHold * theVerifPart.m_dMaxStdDev);
						double dRightMean_2StdDev	= (fabs(theVerifPart.m_dMeanValue)	+ 
							(m_stStrokeThresHolds.m_dStdDevMaxThresHold * theVerifPart.m_dMaxStdDev));
						if(
							(dLeftMean_2StdDev <= thePart.m_dStdDev)
							&&
							(thePart.m_dStdDev <= dRightMean_2StdDev)
							)
						{
							//because of floating point rounding issues, this code should be avoided
							//if(thePart.m_dAvgAngle <= theVerifPart.m_dMaxAvgAngle)
							{
								bResult |= true;
								//only the successful parts will be in the array...
								success_parts.push_back(thePart);	
							}
						}
					}else if(thePart.m_dStdDev <= (theVerifPart.m_dMaxStdDev * m_stStrokeThresHolds.m_dStdDevMaxThresHold))
						{
							//because of floating point rounding issues, this code should be avoided
							//if(thePart.m_dAvgAngle getHWRatio() / m_sigVerifData.m_dHWRatio) * 100.0);
			//
			bResult &= (m_stStrokeThresHolds.m_dMinHWRatio <= hw_ratio && 
						hw_ratio = m_stStrokeThresHolds.m_dMinRatioValidSignature);

			double sig_strokes_dur_ratio = (strokes_in_time / (double)m_sigVerifData.m_FullStrokesVerifDataColl.size()) * 100.0;
			//the duration is compared against a threshold also...
			if(sig_strokes_dur_ratio > 0.0 && 
				sig_strokes_dur_ratio = 80.0)
					sig_ptr->m_sttype = GenuineSignature;
				else 
					sig_ptr->m_sttype = SkilledForgery;
			}else{
				if(sig_ratio m_sttype = RandomForgery;
				else if(sig_ratio m_sttype = SimpleForgery;
				else 
					sig_ptr->m_sttype = SkilledForgery;
			}

			m_nNumberOfFailedStrokes = m_sigVerifData.getNumberOfParts() - (int)success_parts.size();
		}else{
			m_szLastError = _T("Verification failed. This signature has different number of strokes compared to genuine data.");
			m_bHasErrors = true;
		}
	}
	//finally, to avoid memory leaks...
	if(ppSignature!=NULL)
		*ppSignature = sig_ptr;
	else
		delete sig_ptr;

	return bResult;
}

void CSignatureAnalyzer::Reset(void)
{
	purgeSignatures();

	m_sigVerifData.Reset(); 

	m_szLastError = _T("");

	m_bHasErrors = false;
}

void CSignatureAnalyzer::purgeSignatures(void)
{
	vector::iterator it = m_sigColl.begin();
	while(it != m_sigColl.end())
	{
		delete *it++;
	}
}

bool CSignature::ExtractFeatures(CSignatureAnalyzer* analyzer)
{
	if(NULL==analyzer)
		return false;

	short nMinX = 10000;
	short nMaxX = 0;
	short nMinY = 10000;
	short nMaxY = 0;

	bool bResult = false;
	//analyze stroke by stroke, calculate the points relative to the first one (normalization of X, Y)
	vector::iterator stroke_it = m_strokesColl.begin();
	while(stroke_it!= m_strokesColl.end())
	{
		CStroke* stroke_ptr = (*stroke_it);

		short _fstX = 0, _fstY = 0;
		short _prevX = 0, _prevY = 0;
		vector::iterator it_point = stroke_ptr->m_pointsColl.begin();
		if(it_point != stroke_ptr->m_pointsColl.end())
		{
			//
			_fstX = it_point->LogicalX, _fstY = it_point->LogicalY;
			it_point++; //skip this point (will always be zero origin 0,0)
		}
		_prevX = _fstX;
		_prevY = _fstY;
		//pre-process all the points in this stroke...
		while(it_point != stroke_ptr->m_pointsColl.end())
		{
			//
			CStrokePoint & point = *it_point;

			//point.X = (_fstX - point.LogicalX) / 100.0;
			point.X = (point.LogicalX - _fstX) / 100.0;
			point.Y	= (_fstY - point.LogicalY) / 100.0;

			//determine the slope...
			double Dy = (_prevY - point.LogicalY);
			double Dx = (_prevX - point.LogicalX);
			if(Dx != 0.0)
				point.Slope =  Dy / Dx;

			//
			_prevX = point.LogicalX;
			_prevY = point.LogicalY;

			nMinX = min(point.LogicalX, nMinX);
			nMaxX = max(point.LogicalX, nMaxX);
			//
			nMinY = min(point.LogicalY, nMinY);
			nMaxY = max(point.LogicalY, nMaxY);
			//
			it_point++;
		}
		//filter the slopes and determine the strokeparts...
		CStrokePart* part_ptr = NULL;
		//
		volatile bool bLeftPart = true; //if values are between -x and 0.0
		it_point = stroke_ptr->m_pointsColl.begin();
		while(it_point != stroke_ptr->m_pointsColl.end())
		{
			if(NULL==part_ptr)
				part_ptr = new CStrokePart();
			//extract the points with zero slope first...
			while(it_point != stroke_ptr->m_pointsColl.end())
			{
				CStrokePoint & point = *it_point;

				//right
				if(point.Slope > 0.0)
				{
					bLeftPart = false;
					break;
				}
				//left
				if(point.Slope m_pointsColl.push_back(point);

				it_point++; //next point
			}
			//negative slope values...
			if(bLeftPart)
			{
				while(it_point != stroke_ptr->m_pointsColl.end())
				{
					CStrokePoint & point = *it_point;

					if(point.Slope >= analyzer->m_stStrokeThresHolds.m_dNegSlopeMinValue && 
						point.Slope m_stStrokeThresHolds.m_dNegSlopeMaxValue)
					{
						if(analyzer->m_stStrokeThresHolds.m_nMaxValidStrokePartPointCount > (int)part_ptr->m_pointsColl.size())
						{
							//add zero to this part...
							part_ptr->m_pointsColl.push_back(point);
							it_point++; //next point
							continue;
						}
					}
					break;
				}
			}else{
				//positive slope values...
				while(it_point != stroke_ptr->m_pointsColl.end())
				{
					CStrokePoint & point = *it_point;

					if(point.Slope >= analyzer->m_stStrokeThresHolds.m_dSlopeMinValue && 
						point.Slope m_stStrokeThresHolds.m_dSlopeMaxValue)
					{
						if(analyzer->m_stStrokeThresHolds.m_nMaxValidStrokePartPointCount > (int)part_ptr->m_pointsColl.size())
						{
							//add zero to this part...
							part_ptr->m_pointsColl.push_back(point);
							it_point++; //next point
							continue;
						}
					}
					break;
				}
			}
			//skip the next m distance points, before continue...
			volatile int m = analyzer->m_stStrokeThresHolds.m_nmDist;
			while(m-- > 0)
			{
				if(it_point != stroke_ptr->m_pointsColl.end())
					it_point++;
			}
			//add this part, only if this container more or equal this reference point count
			if(part_ptr!=NULL && 
				(int)part_ptr->m_pointsColl.size() >= analyzer->m_stStrokeThresHolds.m_nMinValidStrokePartPointCount)
			{
				stroke_ptr->m_parts.push_back(*part_ptr);
			}
			delete []part_ptr;
			part_ptr = NULL;

			//next, skip points not in the valid range...
			while(it_point != stroke_ptr->m_pointsColl.end())
			{
				CStrokePoint & point = *it_point;

				if(point.Slope m_stStrokeThresHolds.m_dNegSlopeMinValue ||
					point.Slope > analyzer->m_stStrokeThresHolds.m_dSlopeMaxValue)
				{
					it_point++;
					continue;
				}
				break;
			}
		}
		//calculate the mean and stddev for each stroke part...
		vector::iterator it_part = stroke_ptr->m_parts.begin();
		while(it_part != stroke_ptr->m_parts.end())
		{
			CStrokePart& part = *it_part;
			//
			part.m_dMean	= 0.0;
			part.m_dStdDev	= 0.0;
			vector::iterator it_point = part.m_pointsColl.begin();
			while(it_point != part.m_pointsColl.end())
			{
				part.m_dMean += it_point->Slope;
				it_point++;
			}
			part.m_dMean /= (double)part.m_pointsColl.size();
			//now, determine the stdev...
			it_point = part.m_pointsColl.begin();
			while(it_point != part.m_pointsColl.end())
			{
				double X = (it_point->Slope - part.m_dMean);
				part.m_dStdDev += X * X;		//(Xi - XM)^2
				//next...
				it_point++;
			}
			//
			bResult |= true;
			//g^2 = (Xi - XM)^2 / (n-1)
			part.m_dStdDev /= (double)((int)part.m_pointsColl.size() - 1);
			part.m_dStdDev =  sqrt(part.m_dStdDev);
			//the average angle formed by the stroke part...
			part.m_dAvgAngle = atan(part.m_dMean);

			//next part...
			it_part++;
		}
		//do the same thing, to the next stroke...
		stroke_it++;
	}

	m_dWidth	= nMaxX - nMinX;			// Xc = [(Xmax - Xmin)/2] + Xmin
	m_dHeight	= nMaxY - nMinY;			// Yc = [(Ymax - Ymin)/2] + Ymin

	//
	return bResult;
}

#define DEGREES_FACTOR (180/3.1415926535897932384626433832795)

bool CSignature::SaveResultantSignature(__tostream& _ostream, bool debug)
{
	if(!_ostream)
		return false;
	bool bResult = false;
	//
	ostringstream ostrk;
	ostringstream ofinal;
	volatile int index = 0;
	volatile int strokes_count = 0;
	vector::iterator it_stroke = m_strokesColl.begin();	
	while(it_stroke != m_strokesColl.end())
	{
		CStroke* stroke_ptr = *it_stroke++;
		if(NULL==stroke_ptr)
			continue;
		//extract all sub-strokes that form this stroke...
		vector::iterator it_sub_stroke = stroke_ptr->m_parts.begin();
		while(it_sub_stroke != stroke_ptr->m_parts.end())
		{
			//add the next index in this signature sub-stroke...
			ofinal << index << endl;
			//
			CStrokePart& sub_strok = *it_sub_stroke++;
			//extract all points for this sub-stroke...
			vector::iterator it_point = sub_strok.m_pointsColl.begin();
			while(it_point != sub_strok.m_pointsColl.end())
			{
				CStrokePoint& point = *it_point++;
				//add each point...
				if(false==debug)
					ostrk << point.LogicalX << " " << point.LogicalY << endl;
				else{
					ostrk << "X = " << point.X 
						  << ", Y = " << point.Y 
						  << ", Slope = " << point.Slope << endl;
				}
				index++;
			}

			if(debug)
			{
				ostrk	<< "Mean = "		<<	sub_strok.m_dMean 
						<< ", StdDev = "	<<	sub_strok.m_dStdDev 
						<< ", Angle = "		<< (sub_strok.m_dAvgAngle * DEGREES_FACTOR) 
						< 0)
	{
		bResult = true;
		if(false==debug)
		{
			_ostream << index << endl 
					 << strokes_count << endl
					 << ostrk.str() //all the points
					 << ofinal.str();
		}else
			_ostream << ostrk.str() << endl 
					 << "Ratio HW = " << getHWRatio() << endl; //all the points			
	}
	//
	return bResult;
}

int CSignature::getNumberOfStrokeParts(void)
{
	int nNumberOfStrokeParts = 0;
	vector::iterator it_stroke = m_strokesColl.begin();	
	while(it_stroke != m_strokesColl.end())
	{
		CStroke* stroke_ptr = *it_stroke++;
		if(NULL==stroke_ptr)
			continue;
		//how many sub-strokes form this stroke?...
		nNumberOfStrokeParts += (int)stroke_ptr->m_parts.size();
	}

	return nNumberOfStrokeParts;
}

bool CSignatureAnalyzer::PrepareSignatureVerificationData(void)
{
	if(m_sigColl.size() m_dWidth	+ sig_ptr2->m_dWidth;
	m_sigVerifData.m_dAvgHeight	= sig_ptr1->m_dHeight	+ sig_ptr2->m_dHeight;

	m_sigVerifData.m_dHWRatio = sig_ptr1->getHWRatio() + sig_ptr2->getHWRatio();

	//
	//X strokes...
	//1. Iterate the strokes for each pair of signatures
	//2. Determine the min between the stroke parts
	//3. Iterate for the min of stroke parts, and determine the max and min of StdDev and Angle...

	int nstrokes_count = (int)sig_ptr1->m_strokesColl.size();
	for(int index = 0; index m_strokesColl[index];
		CStroke* theStrokeRight	= sig_ptr2->m_strokesColl[index];
		//
		if(!(index m_dDuration, theStrokeRight->m_dDuration);
		theFullStrokeData.m_dMaxDuration	=	max(theStrokeLeft->m_dDuration, theStrokeRight->m_dDuration);

		int min_parts = (int)min(theStrokeLeft->m_parts.size(), theStrokeRight->m_parts.size());
		volatile int nleft	= 0;
		volatile int nright = 0;
		//

		if((int)theStrokeLeft->m_parts.size() > min_parts)
			nleft++;
		if((int)theStrokeRight->m_parts.size() > min_parts)
			nright++;

		for(int ipart = 0; ipart m_parts[nleft].m_dStdDev, 
											  theStrokeRight->m_parts[nright].m_dStdDev);

			partVerifData.m_dMaxStdDev	= max(theStrokeLeft->m_parts[nleft].m_dStdDev,
											  theStrokeRight->m_parts[nright].m_dStdDev);

			partVerifData.m_dMinAvgAngle	= min(theStrokeLeft->m_parts[nleft].m_dAvgAngle,
												  theStrokeRight->m_parts[nright].m_dAvgAngle);

			partVerifData.m_dMaxAvgAngle	= max(theStrokeLeft->m_parts[nleft].m_dAvgAngle,
												  theStrokeRight->m_parts[nright].m_dAvgAngle);
			//the mean...
			partVerifData.m_dMeanValue		= (theStrokeLeft->m_parts[nleft].m_dMean + theStrokeRight->m_parts[nright].m_dMean) / 2.0;
			//add it to the stroke part results collection...
			theFullStrokeData.m_verifParts.push_back(partVerifData);
			//the next part...
			nleft++;
			nright++;
		}
		bResult |= true;
	}

	//now that we have information about two genuine signatures, the others are iterated and compared against
	//the sig verif data and the already created CFullStrokeVerificationData objects...

	while(nNextSig m_strokesColl.size();
		for(int index = 0; index m_strokesColl[index];

			//determine the range of duration...
			theFullStrokeData.m_dMinDuration	=	min(theFullStrokeData.m_dMinDuration, theStroke->m_dDuration);
			theFullStrokeData.m_dMaxDuration	=	max(theFullStrokeData.m_dMaxDuration, theStroke->m_dDuration);

			int min_parts = (int)min(theFullStrokeData.m_verifParts.size(), theStroke->m_parts.size());
			volatile int nleft	= 0;
			volatile int nright = 0;
			//
			/*
			if((int)theFullStrokeData.m_verifParts.size() > min_parts)
				nleft++;
				*/		
			if((int)theStroke->m_parts.size() > min_parts)
				nright++;

			for(int ipart = 0; ipart m_parts[nright].m_dStdDev);

				partVerifData.m_dMaxStdDev		= max(partVerifData.m_dMaxStdDev,
													  theStroke->m_parts[nright].m_dStdDev);

				partVerifData.m_dMinAvgAngle	= min(partVerifData.m_dMinAvgAngle,
													  theStroke->m_parts[nright].m_dAvgAngle);

				partVerifData.m_dMaxAvgAngle	= max(partVerifData.m_dMaxAvgAngle,
													  theStroke->m_parts[nright].m_dAvgAngle);

				//the mean...
				partVerifData.m_dMeanValue		= (partVerifData.m_dMeanValue + theStroke->m_parts[nright].m_dMean) / 2.0;

				//the next part...
				nleft++;
				nright++;
			}
		}

		m_sigVerifData.m_dHWRatio += sig_ptr->getHWRatio(); 

		m_sigVerifData.m_dAvgWidth	+= sig_ptr->m_dWidth;
		m_sigVerifData.m_dAvgHeight	+= sig_ptr->m_dHeight;
		//the next genuine signature...
		nNextSig++;
	}

	//calculate the average width...
	m_sigVerifData.m_dAvgWidth /= (double)m_sigColl.size();
	m_sigVerifData.m_dAvgHeight	/= (double)m_sigColl.size();

	m_sigVerifData.m_dHWRatio /= (double)m_sigColl.size();

	// E N D   -   L E A R N I N G   P R O C E S S 

	return bResult;
}

int CFullStrokeVerificationData::getNumberOfParts()
{
	return (int)m_verifParts.size();
}

int CSignatureVerificationData::getNumberOfParts()
{
	int ncount = 0;
	vector::iterator it_stroke = m_FullStrokesVerifDataColl.begin();
	while(it_stroke != m_FullStrokesVerifDataColl.end())
	{
		ncount += it_stroke->getNumberOfParts();
		it_stroke++;
	}

	return ncount;
}
bool CSignature::setStrokeDuration(int stroke, double duration)
{
	CStroke* pStroke = m_strokesColl[stroke];
	if(pStroke!=NULL)
	{
		pStroke->setDuration(duration);
		m_dTotalDuration += duration;
		return true;
	}
	return false;
}

bool CSignatureAnalyzer::SaveFeatures(__tostream& _toStream)
{
	if(!_toStream)
		return false;
	//Persist the following Signature Features Verification data:
	/*
		- Verification Thresholds
		- Full Stroke Collection
		   - Full Stroke Info
		        - Stroke Parts Collection
				      - Metrics

	*/
	_toStream << "MaxHWRatio=" << m_stStrokeThresHolds.m_dMaxHWRatio << endl
			  << "MinHWRatio=" << m_stStrokeThresHolds.m_dMinHWRatio << endl
			  << "MinValidNOS=" << m_stStrokeThresHolds.m_dMinRatioValidNumberOfStrokeParts << endl
			  << "MinValidSig=" << m_stStrokeThresHolds.m_dMinRatioValidSignature << endl
			  << "MinStrokMin=" << m_stStrokeThresHolds.m_dMinStrokeDurationAvg << endl
			  << "NegSlopeMax=" << m_stStrokeThresHolds.m_dNegSlopeMaxValue << endl
			  << "NegSlopeMin=" << m_stStrokeThresHolds.m_dNegSlopeMinValue << endl
			  << "SlopeMax=" << m_stStrokeThresHolds.m_dSlopeMaxValue << endl
			  << "SlopeMin=" << m_stStrokeThresHolds.m_dSlopeMinValue << endl
			  << "THMaxStdDev=" << m_stStrokeThresHolds.m_dStdDevMaxThresHold << endl
			  << "THMinStdDev=" << m_stStrokeThresHolds.m_dStdDevMinThresHold << endl
			  << "MaxPointCnt=" << m_stStrokeThresHolds.m_nMaxValidStrokePartPointCount << endl
			  << "MinPointCnt=" << m_stStrokeThresHolds.m_nMinValidStrokePartPointCount << endl
			  << "mDistance=" << m_stStrokeThresHolds.m_nmDist << endl
			  << "AvgHeight=" << m_sigVerifData.m_dAvgHeight << endl
			  << "AvgWidth=" << m_sigVerifData.m_dAvgWidth << endl
			  << "TotalDuration=" << m_sigVerifData.m_dAvgTotalDuration << endl
			  << "HWRatio=" << m_sigVerifData.m_dHWRatio << endl
			  << "MinExVaStrokes=" << m_sigVerifData.m_nMinNumberOfExpectedValidStrokes << endl
			  << "StrokePartsCnt=" << m_sigVerifData.getNumberOfParts() << endl << endl;

	vector::iterator it_vdata = m_sigVerifData.m_FullStrokesVerifDataColl.begin();
	while(it_vdata != m_sigVerifData.m_FullStrokesVerifDataColl.end())
	{
		CFullStrokeVerificationData& fullStroke = *it_vdata++;
		//
		_toStream << "StrokeNumber=" << fullStroke.m_nStrokeNumber << endl
				  << "Parts=" << fullStroke.getNumberOfParts() << endl
				  << "MaxDuration=" << fullStroke.m_dMaxDuration << endl
				  << "MinDuration=" << fullStroke.m_dMinDuration << endl;

		volatile int npart = 1;
		vector::iterator it_part = fullStroke.m_verifParts.begin();
		while(it_part != fullStroke.m_verifParts.end())
		{
			CStrokePartVerificationData& verifPart = *it_part++;
			//
			_toStream << "Vector=" << npart++ << ", "
					  << "MaxAvgAngle=" << verifPart.m_dMaxAvgAngle << ", "
					  << "MinAvgAngle=" << verifPart.m_dMinAvgAngle << ", "
					  << "MaxStdDev="	<< verifPart.m_dMaxStdDev	<< ", "
					  << "MinStdDev="	<< verifPart.m_dMinStdDev	<< ", "
					  << "MeanValue="	<< verifPart.m_dMeanValue	<< endl;
			//
		}
		_toStream << "-" << endl;
		//
	}

	return true;
}

bool CSignatureAnalyzer::LoadFeatures(__tistream& _fromStream)
{
	m_sigVerifData.m_bIsVerificationDataAvailable = false;
	//
	if(!_fromStream)
		return false;

	string parseLine;
	size_t npos = string::npos;
	getline(_fromStream, parseLine);
	npos = parseLine.find("MaxHWRatio=");
	if(string::npos!=npos)
	{
		npos += strlen("MaxHWRatio=");
		m_stStrokeThresHolds.m_dMaxHWRatio = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MinHWRatio=");
	if(string::npos!=npos)
	{
		npos += strlen("MinHWRatio=");
		m_stStrokeThresHolds.m_dMinHWRatio  = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MinValidNOS=");
	if(string::npos!=npos)
	{
		npos += strlen("MinValidNOS=");
		m_stStrokeThresHolds.m_dMinRatioValidNumberOfStrokeParts = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MinValidSig=");
	if(string::npos!=npos)
	{
		npos += strlen("MinValidSig=");
		m_stStrokeThresHolds.m_dMinRatioValidSignature = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MinStrokMin=");
	if(string::npos!=npos)
	{
		npos += strlen("MinStrokMin=");
		m_stStrokeThresHolds.m_dMinStrokeDurationAvg = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("NegSlopeMax=");
	if(string::npos!=npos)
	{
		npos += strlen("NegSlopeMax=");
		m_stStrokeThresHolds.m_dNegSlopeMaxValue = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("NegSlopeMin=");
	if(string::npos!=npos)
	{
		npos += strlen("NegSlopeMin=");
		m_stStrokeThresHolds.m_dNegSlopeMinValue = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("SlopeMax=");
	if(string::npos!=npos)
	{
		npos += strlen("SlopeMax=");
		m_stStrokeThresHolds.m_dSlopeMaxValue = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("SlopeMin=");
	if(string::npos!=npos)
	{
		npos += strlen("SlopeMin=");
		m_stStrokeThresHolds.m_dSlopeMinValue = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("THMaxStdDev=");
	if(string::npos!=npos)
	{
		npos += strlen("THMaxStdDev=");
		m_stStrokeThresHolds.m_dStdDevMaxThresHold = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("THMinStdDev=");
	if(string::npos!=npos)
	{
		npos += strlen("THMinStdDev=");
		m_stStrokeThresHolds.m_dStdDevMinThresHold = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MaxPointCnt=");
	if(string::npos!=npos)
	{
		npos += strlen("MaxPointCnt=");
		m_stStrokeThresHolds.m_nMaxValidStrokePartPointCount = atoi(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MinPointCnt=");
	if(string::npos!=npos)
	{
		npos += strlen("MinPointCnt=");
		m_stStrokeThresHolds.m_nMinValidStrokePartPointCount = atoi(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("mDistance=");
	if(string::npos!=npos)
	{
		npos += strlen("mDistance=");
		m_stStrokeThresHolds.m_nmDist = atoi(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("AvgHeight=");
	if(string::npos!=npos)
	{
		npos += strlen("AvgHeight=");
		m_sigVerifData.m_dAvgHeight = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("AvgWidth=");
	if(string::npos!=npos)
	{
		npos += strlen("AvgWidth=");
		m_sigVerifData.m_dAvgWidth = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("TotalDuration=");
	if(string::npos!=npos)
	{
		npos += strlen("TotalDuration=");
		m_sigVerifData.m_dAvgTotalDuration = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("HWRatio=");
	if(string::npos!=npos)
	{
		npos += strlen("HWRatio=");
		m_sigVerifData.m_dHWRatio = atof(parseLine.substr(npos).c_str());
	}
	getline(_fromStream, parseLine);
	npos = parseLine.find("MinExVaStrokes=");
	if(string::npos!=npos)
	{
		npos += strlen("MinExVaStrokes=");
		m_sigVerifData.m_nMinNumberOfExpectedValidStrokes = atoi(parseLine.substr(npos).c_str());
	}
	//
	getline(_fromStream, parseLine); //StrokePartsCnt=
	getline(_fromStream, parseLine); //
	while(_fromStream)
	{
		int nPartCount = 0;
		CFullStrokeVerificationData fullStroke;
			//
		getline(_fromStream, parseLine);
		npos = parseLine.find("StrokeNumber=");
		if(string::npos==npos)
			break;
		else
		{
			npos += strlen("StrokeNumber=");
			fullStroke.m_nStrokeNumber = atoi(parseLine.substr(npos).c_str());
		}
		getline(_fromStream, parseLine);
		npos = parseLine.find("Parts=");
		if(string::npos!=npos)
		{
			npos += strlen("Parts=");
			nPartCount = atoi(parseLine.substr(npos).c_str());
		}
		getline(_fromStream, parseLine);
		npos = parseLine.find("MaxDuration=");
		if(string::npos!=npos)
		{
			npos += strlen("MaxDuration=");
			fullStroke.m_dMaxDuration = atof(parseLine.substr(npos).c_str());
		}
		getline(_fromStream, parseLine);
		npos = parseLine.find("MinDuration=");
		if(string::npos!=npos)
		{
			npos += strlen("MinDuration=");
			fullStroke.m_dMinDuration = atof(parseLine.substr(npos).c_str());
		}

		for(int npart=0; (_fromStream && npart createElement(L"SignatureThresHolds");

	xmlDoc->appendChild(root_ptr);

	MSXML2::IXMLDOMElementPtr item_ptr = xmlDoc->createElement(L"MaxHWRatio");
	root_ptr->appendChild(item_ptr);

	_gcvt(m_stStrokeThresHolds.m_dMaxHWRatio, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MinHWRatio");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dMinHWRatio, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MinValidNOS");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dMinRatioValidNumberOfStrokeParts, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MinValidSig");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dMinRatioValidSignature, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MinStrokMin");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dMinStrokeDurationAvg, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"NegSlopeMax");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dNegSlopeMaxValue, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"NegSlopeMin");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dNegSlopeMinValue, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"SlopeMax");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dSlopeMaxValue, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"SlopeMin");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dSlopeMinValue, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"THMaxStdDev");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dStdDevMaxThresHold, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"THMinStdDev");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_stStrokeThresHolds.m_dStdDevMinThresHold, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MaxPointCnt");
	root_ptr->appendChild(item_ptr);
	_itoa(m_stStrokeThresHolds.m_nMaxValidStrokePartPointCount, fpstr, 10);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MinPointCnt");
	root_ptr->appendChild(item_ptr);
	_itoa(m_stStrokeThresHolds.m_nMinValidStrokePartPointCount, fpstr, 10);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"mDistance");
	root_ptr->appendChild(item_ptr);
	_itoa(m_stStrokeThresHolds.m_nmDist, fpstr, 10);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"AvgHeight");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_sigVerifData.m_dAvgHeight, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"AvgWidth");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_sigVerifData.m_dAvgWidth, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"TotalDuration");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_sigVerifData.m_dAvgTotalDuration, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"HWRatio");
	root_ptr->appendChild(item_ptr);
	_gcvt(m_sigVerifData.m_dHWRatio, 6, fpstr);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"MinExVaStrokes");
	root_ptr->appendChild(item_ptr);
	_itoa(m_sigVerifData.m_nMinNumberOfExpectedValidStrokes, fpstr, 10);
	item_ptr->put_text(_bstr_t(fpstr));

	item_ptr = xmlDoc->createElement(L"StrokePartsCnt");
	root_ptr->appendChild(item_ptr);
	_itoa(m_sigVerifData.getNumberOfParts(), fpstr, 10);
	item_ptr->put_text(_bstr_t(fpstr));
	//

	vector::iterator it_vdata = m_sigVerifData.m_FullStrokesVerifDataColl.begin();
	while(it_vdata != m_sigVerifData.m_FullStrokesVerifDataColl.end())
	{
		CFullStrokeVerificationData& fullStroke = *it_vdata++;

		item_ptr = xmlDoc->createElement(L"StrokeInfo");
		root_ptr->appendChild(item_ptr);

		MSXML2::IXMLDOMAttributePtr attrPtr = xmlDoc->createAttribute(L"StrokeNumber");
		item_ptr->attributes->setNamedItem(attrPtr);

		_itoa(fullStroke.m_nStrokeNumber, fpstr, 10);
		attrPtr->put_text(_bstr_t(fpstr));

		attrPtr = xmlDoc->createAttribute(L"Parts");
		item_ptr->attributes->setNamedItem(attrPtr);

		_itoa(fullStroke.getNumberOfParts(), fpstr, 10);
		attrPtr->put_text(_bstr_t(fpstr));

		attrPtr = xmlDoc->createAttribute(L"MaxDuration");
		item_ptr->attributes->setNamedItem(attrPtr);

		_gcvt(fullStroke.m_dMaxDuration, 6, fpstr);
		attrPtr->put_text(_bstr_t(fpstr));

		attrPtr = xmlDoc->createAttribute(L"MinDuration");
		item_ptr->attributes->setNamedItem(attrPtr);

		_gcvt(fullStroke.m_dMinDuration, 6, fpstr);
		attrPtr->put_text(_bstr_t(fpstr));

		//
		volatile int npart = 1;
		vector::iterator it_part = fullStroke.m_verifParts.begin();
		while(it_part != fullStroke.m_verifParts.end())
		{
			CStrokePartVerificationData& verifPart = *it_part++;
			//
			MSXML2::IXMLDOMElementPtr vector_ptr = xmlDoc->createElement(L"Vector");
			item_ptr->appendChild(vector_ptr);

			attrPtr = xmlDoc->createAttribute(L"SeqId");
			vector_ptr->attributes->setNamedItem(attrPtr);

			_itoa(npart++, fpstr, 10);
			attrPtr->put_text(_bstr_t(fpstr));

			attrPtr = xmlDoc->createAttribute(L"MaxAvgAngle");
			vector_ptr->attributes->setNamedItem(attrPtr);

			_gcvt(verifPart.m_dMaxAvgAngle, 6, fpstr);
			attrPtr->put_text(_bstr_t(fpstr));

			attrPtr = xmlDoc->createAttribute(L"MinAvgAngle");
			vector_ptr->attributes->setNamedItem(attrPtr);

			_gcvt(verifPart.m_dMinAvgAngle, 6, fpstr);
			attrPtr->put_text(_bstr_t(fpstr));

			attrPtr = xmlDoc->createAttribute(L"MaxStdDev");
			vector_ptr->attributes->setNamedItem(attrPtr);

			_gcvt(verifPart.m_dMaxStdDev, 6, fpstr);
			attrPtr->put_text(_bstr_t(fpstr));

			attrPtr = xmlDoc->createAttribute(L"MinStdDev");
			vector_ptr->attributes->setNamedItem(attrPtr);

			_gcvt(verifPart.m_dMinStdDev, 6, fpstr);
			attrPtr->put_text(_bstr_t(fpstr));

			attrPtr = xmlDoc->createAttribute(L"MeanValue");
			vector_ptr->attributes->setNamedItem(attrPtr);

			_gcvt(verifPart.m_dMeanValue, 6, fpstr);
			attrPtr->put_text(_bstr_t(fpstr));
			//
		}
	}

	//
	bool bResult = false;
	try{
		bResult = SUCCEEDED(xmlDoc->save(_toXmlFile.c_str()));
	}catch(_com_error& e)
	{
		//should never get here!
	}

	return bResult;
}

bool CSignatureAnalyzer::LoadFeaturesXml(__tstring _fromXmlFile)
{
	m_sigVerifData.m_bIsVerificationDataAvailable = false;
	//
	if(_fromXmlFile.length()==0)
		return false;
	//
	IXMLDOMDocument2Ptr xmlDoc = NULL;
	xmlDoc.CreateInstance(CLSID_DOMDocument);
	if(NULL==xmlDoc)
		return false;

	if(VARIANT_FALSE==xmlDoc->load(_fromXmlFile.c_str()))
		return false;
	//

	MSXML2::IXMLDOMElementPtr item_ptr = xmlDoc->documentElement->selectSingleNode(L"MaxHWRatio");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dMaxHWRatio = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MinHWRatio");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dMinHWRatio = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MinValidNOS");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dMinRatioValidNumberOfStrokeParts = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MinValidSig");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dMinRatioValidSignature = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MinStrokMin");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dMinStrokeDurationAvg = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"NegSlopeMax");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dNegSlopeMaxValue = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"NegSlopeMin");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dNegSlopeMinValue = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"SlopeMax");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dSlopeMaxValue = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"SlopeMin");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dSlopeMinValue = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"THMaxStdDev");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dStdDevMaxThresHold = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"THMinStdDev");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_dStdDevMinThresHold = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MaxPointCnt");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_nMaxValidStrokePartPointCount = atoi((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MinPointCnt");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_nMinValidStrokePartPointCount = atoi((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"mDistance");
if(item_ptr!=NULL)
	m_stStrokeThresHolds.m_nmDist = atoi((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"AvgHeight");
if(item_ptr!=NULL)
	m_sigVerifData.m_dAvgHeight = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"AvgWidth");
if(item_ptr!=NULL)
	m_sigVerifData.m_dAvgWidth = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"HWRatio");
if(item_ptr!=NULL)
	m_sigVerifData.m_dHWRatio = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"TotalDuration");
if(item_ptr!=NULL)
	m_sigVerifData.m_dAvgTotalDuration = atof((const char*)item_ptr->text);

	item_ptr = xmlDoc->documentElement->selectSingleNode(L"MinExVaStrokes");
if(item_ptr!=NULL)
	m_sigVerifData.m_nMinNumberOfExpectedValidStrokes = atoi((const char*)item_ptr->text);

	MSXML2::IXMLDOMNodeListPtr list_ptr = xmlDoc->selectNodes(L"//StrokeInfo");
	for(int stroke=0; stroke length; stroke++)
	{
		CFullStrokeVerificationData fullStroke;
		//
		item_ptr = list_ptr->item[stroke];
		if(item_ptr==NULL)
			continue;

		MSXML2::IXMLDOMAttributePtr attr_ptr = item_ptr->attributes->getNamedItem(L"StrokeNumber");

		fullStroke.m_nStrokeNumber = atoi(attr_ptr->text);

		attr_ptr = item_ptr->attributes->getNamedItem(L"Parts");

		int nPartsCount = atoi(attr_ptr->text);

		attr_ptr = item_ptr->attributes->getNamedItem(L"MaxDuration");
	if(attr_ptr!=NULL)
		fullStroke.m_dMaxDuration = atof(attr_ptr->text);

		attr_ptr = item_ptr->attributes->getNamedItem(L"MinDuration");
	if(attr_ptr!=NULL)
		fullStroke.m_dMinDuration = atof(attr_ptr->text);

		MSXML2::IXMLDOMNodeListPtr vector_list_ptr = item_ptr->selectNodes(L"Vector");

		for(int ipart = 0; ipart length; ipart++)
		{
			CStrokePartVerificationData verifPart;
			//
			attr_ptr = vector_list_ptr->item[ipart]->attributes->getNamedItem(L"MaxAvgAngle");
		if(attr_ptr!=NULL)
			verifPart.m_dMaxAvgAngle = atof(attr_ptr->text);
			attr_ptr = vector_list_ptr->item[ipart]->attributes->getNamedItem(L"MinAvgAngle");
		if(attr_ptr!=NULL)
			verifPart.m_dMinAvgAngle = atof(attr_ptr->text);
			attr_ptr = vector_list_ptr->item[ipart]->attributes->getNamedItem(L"MaxStdDev");
		if(attr_ptr!=NULL)
			verifPart.m_dMaxStdDev	 = atof(attr_ptr->text);
			attr_ptr = vector_list_ptr->item[ipart]->attributes->getNamedItem(L"MinStdDev");
		if(attr_ptr!=NULL)
			verifPart.m_dMinStdDev	 = atof(attr_ptr->text);
			attr_ptr = vector_list_ptr->item[ipart]->attributes->getNamedItem(L"MeanValue");
		if(attr_ptr!=NULL)
			verifPart.m_dMeanValue	= atof(attr_ptr->text);

			fullStroke.m_verifParts.push_back(verifPart);
		}

		//
		m_sigVerifData.m_FullStrokesVerifDataColl.push_back(fullStroke);
	}
	//
	m_sigVerifData.m_bIsVerificationDataAvailable = true;
	return true;
}
Advertisements

Posted February 2, 2013 by hmarzan

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: