1 #include "../Common/Common.h"
4 #include "UserLogTrial.h"
6 // Track memory leaks on Windows to the line that new'd the memory
9 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
12 static char THIS_FILE
[] = __FILE__
;
16 CUserLogTrial::CUserLogTrial(const string
& strCurrentTrialFilename
)
18 //CFunctionLogger f1("CUserLogTrial::CUserLogTrial", g_pLogger);
22 m_strCurrentTrialFilename
= strCurrentTrialFilename
;
25 CUserLogTrial::~CUserLogTrial()
27 //CFunctionLogger f1("CUserLogTrial::~CUserLogTrial", g_pLogger);
29 for (unsigned int i
= 0; i
< m_vpParams
.size(); i
++)
31 CUserLogParam
* pParam
= (CUserLogParam
*) m_vpParams
[i
];
40 for (unsigned int i
= 0; i
< m_vpNavCycles
.size(); i
++)
42 NavCycle
* pCycle
= (NavCycle
*) m_vpNavCycles
[i
];
46 if (pCycle
->pSpan
!= NULL
)
52 for (unsigned int i
= 0; i
< pCycle
->vectorButtons
.size(); i
++)
54 CUserButton
* pButton
= (CUserButton
*) pCycle
->vectorButtons
[i
];
63 for (unsigned int i
= 0; i
< pCycle
->vectorMouseLocations
.size(); i
++)
65 CUserLocation
* pLocation
= (CUserLocation
*) pCycle
->vectorMouseLocations
[i
];
67 if (pLocation
!= NULL
)
74 for (unsigned int i
= 0; i
< pCycle
->vectorNavLocations
.size(); i
++)
76 NavLocation
* pLocation
= (NavLocation
*) pCycle
->vectorNavLocations
[i
];
77 if (pLocation
!= NULL
)
79 CTimeSpan
* pSpan
= pLocation
->span
;
87 Dasher::VECTOR_SYMBOL_PROB
* pVectorAdded
= pLocation
->pVectorAdded
;
88 if (pVectorAdded
!= NULL
)
114 // Returns an XML version of all our information
115 string
CUserLogTrial::GetXML(const string
& strPrefix
)
117 //CFunctionLogger f1("CUserLogTrial::GetXML", g_pLogger);
119 string strResult
= "";
121 string strPrefixTab
= strPrefix
;
122 strPrefixTab
+= "\t";
124 string strPrefixTabTab
= strPrefixTab
;
125 strPrefixTabTab
+= "\t";
127 strResult
+= strPrefix
;
128 strResult
+= "<Trial>\n";
130 // Summarize what happened at the beginning of the block
131 strResult
+= GetSummaryXML(strPrefix
);
133 // Parameters that we want tracked on a per trial basis
134 strResult
+= GetParamsXML(strPrefix
);
136 // Size of the window and the canvas in screen coordniates
137 strResult
+= GetWindowCanvasXML(strPrefix
);
139 strResult
+= m_strCurrentTrial
;
141 // All the start and stop navigation events
142 strResult
+= GetNavCyclesXML(strPrefix
);
144 strResult
+= strPrefix
;
145 strResult
+= "</Trial>\n";
150 // See if any navigation has occured during this trial yet
151 bool CUserLogTrial::HasWritingOccured()
153 //CFunctionLogger f1("CUserLogTrial::HasWritingOccured", g_pLogger);
155 if (m_vpNavCycles
.size() > 0)
162 void CUserLogTrial::StartWriting()
164 //CFunctionLogger f1("CUserLogTrial::StartWriting", g_pLogger);
168 g_pLogger
->Log("CUserLogTrial::StartWriting, nav already marked as started!", logNORMAL
);
172 // Make sure our trial time span is running
176 // Start the task timer if we haven't already done so
178 m_pSpan
= new CTimeSpan("Time", false);
182 g_pLogger
->Log("CUserLogTrial::StartWriting, m_pSpan was NULL!", logNORMAL
);
186 m_bWritingStart
= true;
188 // If we have already done some navigation, then the previous NavStop() would have stopped
189 // the timer in the last NavLocation object. We want to tell it to continue since the
190 // trial is not in fact over.
191 if (m_vpNavCycles
.size() > 0)
193 NavLocation
* pLastLocation
= GetCurrentNavLocation();
194 if ((pLastLocation
!= NULL
) && (pLastLocation
->span
!= NULL
))
195 pLastLocation
->span
->Continue();
198 // NavCycle* newCycle = AddNavCycle();
202 void CUserLogTrial::StopWriting(double dBits
)
204 //CFunctionLogger f1("CUserLogTrial::StopWriting", g_pLogger);
206 if (!m_bWritingStart
)
208 g_pLogger
->Log("CUserLogTrial::StopWriting, nav already marked as stopped!", logNORMAL
);
212 if (m_vpNavCycles
.size() <= 0)
214 g_pLogger
->Log("CUserLogTrial::StopWriting, vector was empty!", logNORMAL
);
218 NavCycle
* pCycle
= GetCurrentNavCycle();
221 g_pLogger
->Log("CUserLogTrial::StopWriting, current cycle was NULL!", logNORMAL
);
225 CTimeSpan
* pSpan
= (CTimeSpan
*) pCycle
->pSpan
;
228 g_pLogger
->Log("CUserLogTrial::StopWriting, span was NULL!", logNORMAL
);
232 pCycle
->dBits
= dBits
;
235 m_bWritingStart
= false;
237 // Make sure the last location timer is stopped since this could be the end of the trial and
238 // we want the timestamps in the location elements to match the total trial time.
239 NavLocation
* pLastLocation
= GetCurrentNavLocation();
240 if ((pLastLocation
!= NULL
) && (pLastLocation
->span
!= NULL
))
241 pLastLocation
->span
->Continue();
243 // Could be the last event of the trial
247 // We want to use the UserTrial info from the navigation period in Dasher. We'll update
248 // this everytime the user stops, this should make sure we get the right bit.
252 // The user has entered one or more new symbols. UserLog object will
253 // pass us the pointer to the current alphabet that is being used.
254 void CUserLogTrial::AddSymbols(Dasher::VECTOR_SYMBOL_PROB
* vpNewSymbolProbs
,
255 eUserLogEventType iEvent
)
257 // Add the symbols to our running total of symbols.
259 // ACL: Old comment said "We track the symbols and not the display text
260 // since we may need to delete symbols later and
261 // a given symbol might take up multiple chars."
262 // - yet stored the display text????
264 // We also keep the probability around so we can
265 // calculate the average bits of the history.
266 m_vHistory
.insert(m_vHistory
.end(), vpNewSymbolProbs
->begin(), vpNewSymbolProbs
->end());
270 // Create the new NavLocation struct that record the data about this addition
271 NavLocation
* pLocation
= NULL
;
272 pLocation
= new NavLocation
;
274 if (pLocation
== NULL
)
276 g_pLogger
->Log("CUserLogTrial::AddSymbols, failed to create location!", logNORMAL
);
280 pLocation
->strHistory
= GetHistoryDisplay();
281 pLocation
->span
= new CTimeSpan("Time", false);
282 pLocation
->avgBits
= GetHistoryAvgBits();
283 pLocation
->event
= iEvent
;
284 pLocation
->numDeleted
= 0;
285 pLocation
->pVectorAdded
= new std::vector
<Dasher::SymbolProb
>(*vpNewSymbolProbs
);
287 NavCycle
* pCycle
= GetCurrentNavCycle();
289 pCycle
->vectorNavLocations
.push_back(pLocation
);
291 g_pLogger
->Log("CUserLogTrial::AddSymbols, cycle was NULL!", logNORMAL
);
295 void CUserLogTrial::DeleteSymbols(int iNumToDelete
, eUserLogEventType iEvent
)
297 //CFunctionLogger f1("CUserLogTrial::DeleteSymbols", g_pLogger);
299 if (iNumToDelete
<= 0)
302 // Be careful not to pop more things than we have (this will hork the
303 // memory up on linux but not windows).
304 int iActualNumToDelete
= min((int) m_vHistory
.size(), iNumToDelete
);
306 for (int i
= 0; i
< iActualNumToDelete
; i
++)
308 // Remove the request number of symbols from our
310 m_vHistory
.pop_back();
315 // Create the new NavLocation struct that record the data about this addition
316 NavLocation
* pLocation
= NULL
;
317 pLocation
= new NavLocation
;
319 if (pLocation
== NULL
)
321 g_pLogger
->Log("CUserLogTrial::DeleteSymbols, failed to create location!", logNORMAL
);
325 pLocation
->strHistory
= GetHistoryDisplay();
326 pLocation
->span
= new CTimeSpan("Time", false);
327 pLocation
->avgBits
= GetHistoryAvgBits();
328 pLocation
->event
= iEvent
;
329 pLocation
->numDeleted
= iNumToDelete
;
330 pLocation
->pVectorAdded
= NULL
;
332 NavCycle
* pCycle
= GetCurrentNavCycle();
334 pCycle
->vectorNavLocations
.push_back(pLocation
);
336 g_pLogger
->Log("CUserLogTrial::DeleteSymbols, cycle was NULL!", logNORMAL
);
341 // Called by UserLog object whenever we move on to the next trial. This lets
342 // our trial object finalize any timers.
343 void CUserLogTrial::Done()
345 //CFunctionLogger f1("CUserLogTrial::Done", g_pLogger);
349 // Stop the time span that tracks the total trial time (if not already stopped)
350 if ((m_pSpan
!= NULL
) && (!m_pSpan
->IsStopped()))
356 void CUserLogTrial::AddMouseLocation(int iX
, int iY
, float dNats
)
358 //CFunctionLogger f1("CUserLogTrial::AddMouseLocation", g_pLogger);
360 CUserLocation
* pLocation
= NULL
;
362 pLocation
= new CUserLocation(iX
, iY
, dNats
);
364 if (pLocation
!= NULL
)
366 // m_vectorMouseLocations.push_back(location);
368 NavCycle
* pCycle
= GetCurrentNavCycle();
371 pCycle
->vectorMouseLocations
.push_back(pLocation
);
373 g_pLogger
->Log("CUserLogTrial::AddLocation, cycle was NULL!", logNORMAL
);
376 g_pLogger
->Log("CUserLogTrial::AddLocation, location was NULL!", logNORMAL
);
380 // Adds a normalized version of our mouse coordinates based on the size
381 // of the window. Can optionally be told to store both representations.
382 void CUserLogTrial::AddMouseLocationNormalized(int iX
, int iY
, bool bStoreIntegerRep
, float dNats
)
384 //CFunctionLogger f1("CUserLogTrial::AddMouseLocationNormalized", g_pLogger);
386 CUserLocation
* pLocation
= NULL
;
388 if ((m_sCanvasCoordinates
.bottom
== 0) &&
389 (m_sCanvasCoordinates
.left
== 0) &&
390 (m_sCanvasCoordinates
.right
== 0) &&
391 (m_sCanvasCoordinates
.top
== 0))
392 g_pLogger
->Log("CUserLogTrial::AddMouseLocationNormalized, called before AddCanvasSize()?", logNORMAL
);
394 pLocation
= new CUserLocation(iX
,
396 m_sCanvasCoordinates
.top
,
397 m_sCanvasCoordinates
.left
,
398 m_sCanvasCoordinates
.bottom
,
399 m_sCanvasCoordinates
.right
,
403 if (pLocation
!= NULL
)
405 // m_vectorMouseLocations.push_back(location);
406 NavCycle
* pCycle
= GetCurrentNavCycle();
409 pCycle
->vectorMouseLocations
.push_back(pLocation
);
411 g_pLogger
->Log("CUserLogTrial::AddMouseLocationNormalized, cycle was NULL!", logNORMAL
);
414 g_pLogger
->Log("CUserLogTrial::AddLocation, location was NULL!", logNORMAL
);
417 void CUserLogTrial::AddKeyDown(int iId
, int iType
, int iEffect
) {
418 CUserButton
* pButton
= new CUserButton(iId
, iType
, iEffect
);
421 NavCycle
* pCycle
= GetCurrentNavCycle();
424 pCycle
->vectorButtons
.push_back(pButton
);
426 g_pLogger
->Log("CUserLogTrial::AddLocation, cycle was NULL!", logNORMAL
);
429 g_pLogger
->Log("CUserLogTrial::AddLocation, location was NULL!", logNORMAL
);
432 // Sets the current window size, this includes area for the menu bar,
433 // sliders, canvas, etc.
434 void CUserLogTrial::AddWindowSize(int iTop
, int iLeft
, int iBottom
, int iRight
)
436 //CFunctionLogger f1("CUserLogTrial::AddWindowSize", g_pLogger);
438 m_sWindowCoordinates
.top
= iTop
;
439 m_sWindowCoordinates
.left
= iLeft
;
440 m_sWindowCoordinates
.bottom
= iBottom
;
441 m_sWindowCoordinates
.right
= iRight
;
444 // Sets the current canvas size
445 void CUserLogTrial::AddCanvasSize(int iTop
, int iLeft
, int iBottom
, int iRight
)
447 //CFunctionLogger f1("CUserLogTrial::AddCanvasSize", g_pLogger);
449 m_sCanvasCoordinates
.top
= iTop
;
450 m_sCanvasCoordinates
.left
= iLeft
;
451 m_sCanvasCoordinates
.bottom
= iBottom
;
452 m_sCanvasCoordinates
.right
= iRight
;
455 // Are we currently navigating?
456 bool CUserLogTrial::IsWriting()
458 return m_bWritingStart
;
461 ////////////////////////////////////////// private methods ////////////////////////////////////////////////
463 void CUserLogTrial::InitMemberVars()
465 //CFunctionLogger f1("CUserLogTrial::InitMemberVars", g_pLogger);
467 m_bWritingStart
= false;
469 m_strCurrentTrial
= "";
471 m_sWindowCoordinates
.bottom
= 0;
472 m_sWindowCoordinates
.top
= 0;
473 m_sWindowCoordinates
.left
= 0;
474 m_sWindowCoordinates
.right
= 0;
476 m_sCanvasCoordinates
.bottom
= 0;
477 m_sCanvasCoordinates
.top
= 0;
478 m_sCanvasCoordinates
.left
= 0;
479 m_sCanvasCoordinates
.right
= 0;
482 // Obtain information that is being passed in from the UserTrial standalone application.
483 // This information tell us what the user is actually trying to enter.
484 void CUserLogTrial::GetUserTrialInfo()
486 //CFunctionLogger f1("CUserLogTrial::GetUserTrialInfo", g_pLogger);
488 m_strCurrentTrial
= "";
490 if (m_strCurrentTrialFilename
.length() > 0)
492 // We want ios::nocreate, but not available in .NET 2003, arrgh
493 fstream
fin(m_strCurrentTrialFilename
.c_str(), ios::in
);
495 // Make sure we successfully opened before we start reading it
500 fin
.getline(m_szTempBuffer
, TEMP_BUFFER_SIZE
);
501 if (strlen(m_szTempBuffer
) > 0)
503 m_strCurrentTrial
+= "\t\t\t";
504 m_strCurrentTrial
+= m_szTempBuffer
;
505 m_strCurrentTrial
+= "\n";
513 // Returns the concatenation of all our symbol history using
514 // the display text that the alphabet at the time of the
515 // symbol being added gave us.
516 string
CUserLogTrial::GetHistoryDisplay()
518 //CFunctionLogger f1("CUserLogTrial::GetHistoryDisplay", g_pLogger);
520 string strResult
= "";
522 for (unsigned int i
= 0; i
< m_vHistory
.size(); i
++)
524 Dasher::SymbolProb sItem
= (Dasher::SymbolProb
) m_vHistory
[i
];
525 strResult
+= sItem
.strDisplay
;
531 double CUserLogTrial::GetHistoryAvgBits()
533 //CFunctionLogger f1("CUserLogTrial::GetHistoryAvgBits", g_pLogger);
535 double dResult
= 0.0;
537 if (m_vHistory
.size() > 0)
539 for (unsigned int i
= 0; i
< m_vHistory
.size(); i
++)
541 Dasher::SymbolProb sItem
= (Dasher::SymbolProb
) m_vHistory
[i
];
543 dResult
+= log(sItem
.prob
);
545 dResult
= dResult
* -1.0;
546 dResult
= dResult
/ log(2.0);
547 dResult
= dResult
/ m_vHistory
.size();
553 void CUserLogTrial::StopPreviousTimer()
555 //CFunctionLogger f1("CUserLogTrial::StopPreviousTimer", g_pLogger);
557 // Make sure the previous time span (if any) has had its timer stopped
558 if (m_vpNavCycles
.size() > 0)
560 NavLocation
* pLastLocation
= GetCurrentNavLocation();
561 if ((pLastLocation
!= NULL
) && (pLastLocation
->span
!= NULL
))
562 pLastLocation
->span
->Stop();
567 // Gets XML string for a given NavLocation struct
568 string
CUserLogTrial::GetLocationXML(NavLocation
* pLocation
, const string
& strPrefix
)
570 //CFunctionLogger f1("CUserLogTrial::GetLocationXML", g_pLogger);
572 string strResult
= "";
573 if (pLocation
== NULL
)
575 g_pLogger
->Log("CUserLogTrial::GetLocationXML, location was NULL!", logNORMAL
);
579 strResult
+= strPrefix
;
580 strResult
+= "<Location>\n";
582 strResult
+= strPrefix
;
583 strResult
+= "\t<History>";
584 strResult
+= pLocation
->strHistory
;
585 strResult
+= "</History>\n";
587 strResult
+= strPrefix
;
588 strResult
+= "\t<AvgBits>";
589 sprintf(m_szTempBuffer
, "%0.6f", pLocation
->avgBits
);
590 strResult
+= m_szTempBuffer
;
591 strResult
+= "</AvgBits>\n";
593 // Only output the event if it is interesting type, not normal mouse navigation
594 if (pLocation
->event
!= userLogEventMouse
)
596 strResult
+= strPrefix
;
597 strResult
+= "\t\t<Event>";
598 sprintf(m_szTempBuffer
, "%d", (int) pLocation
->event
);
599 strResult
+= m_szTempBuffer
;
600 strResult
+= "</Event>\n";
603 if ((pLocation
->pVectorAdded
!= NULL
) && (pLocation
->pVectorAdded
->size() > 0))
605 strResult
+= strPrefix
;
606 strResult
+= "\t<NumAdded>";
607 sprintf(m_szTempBuffer
, "%zu", pLocation
->pVectorAdded
->size());
608 strResult
+= m_szTempBuffer
;
609 strResult
+= "</NumAdded>\n";
611 Dasher::VECTOR_SYMBOL_PROB
* pVectorAdded
= pLocation
->pVectorAdded
;
613 if (pVectorAdded
!= NULL
)
615 // Output the details of each add
616 for (unsigned int j
= 0; j
< pVectorAdded
->size(); j
++)
618 Dasher::SymbolProb sItem
= (Dasher::SymbolProb
) (*pVectorAdded
)[j
];
620 strResult
+= strPrefix
;
621 strResult
+= "\t<Add>\n";
623 strResult
+= strPrefix
;
624 strResult
+= "\t\t<Text>";
625 strResult
+= sItem
.strDisplay
;
626 strResult
+= "</Text>\n";
628 strResult
+= strPrefix
;
629 strResult
+= "\t\t<Prob>";
630 sprintf(m_szTempBuffer
, "%0.6f", sItem
.prob
);
631 strResult
+= m_szTempBuffer
;
632 strResult
+= "</Prob>\n";
634 strResult
+= strPrefix
;
635 strResult
+= "\t</Add>\n";
640 if (pLocation
->numDeleted
> 0)
642 strResult
+= strPrefix
;
643 strResult
+= "\t<NumDeleted>";
644 sprintf(m_szTempBuffer
, "%d", pLocation
->numDeleted
);
645 strResult
+= m_szTempBuffer
;
646 strResult
+= "</NumDeleted>\n";
649 if (pLocation
->span
!= NULL
)
651 string strPrefixTabTabTab
= strPrefix
;
652 strPrefixTabTabTab
+= "\t";
654 strResult
+= pLocation
->span
->GetXML(strPrefixTabTabTab
);
657 strResult
+= strPrefix
;
658 strResult
+= "</Location>\n";
663 // Output the XML for the summary section of XML
664 string
CUserLogTrial::GetSummaryXML(const string
& strPrefix
)
666 //CFunctionLogger f1("CUserLogTrial::GetSummaryXML", g_pLogger);
668 string strResult
= "";
670 strResult
+= strPrefix
;
671 strResult
+= "\t<Summary>\n";
673 // Figure out what the user ended up writing and how fast they did it
675 double dAvgBits
= 0.0;
677 NavLocation
* pLocation
= GetCurrentNavLocation();
678 if (pLocation
!= NULL
)
680 strText
= GetHistoryDisplay();
681 dAvgBits
= pLocation
->avgBits
;
684 int iButtonCount
= GetButtonCount();
685 double dTotalBits
= GetTotalBits();
687 strResult
+= GetStatsXML(strPrefix
, strText
, m_pSpan
, dAvgBits
, iButtonCount
, dTotalBits
);
689 strResult
+= strPrefix
;
690 strResult
+= "\t</Summary>\n";
695 // Calculates the various summary stats we output
696 string
CUserLogTrial::GetStatsXML(const string
& strPrefix
, const string
& strText
, CTimeSpan
* pSpan
, double dAvgBits
, int iButtonCount
, double dTotalBits
)
698 //CFunctionLogger f1("CUserLogTrial::GetStatsXML", g_pLogger);
700 string strResult
= "";
704 g_pLogger
->Log("CUserLogTrial::GetStatsXML, pSpan = NULL!", logNORMAL
);
708 strResult
+= strPrefix
;
709 strResult
+= "\t\t<Text>";
710 strResult
+= strText
;
711 strResult
+= "</Text>\n";
713 // Average number of bits along the path to the final string
714 strResult
+= strPrefix
;
715 strResult
+= "\t\t<AvgBits>";
716 sprintf(m_szTempBuffer
, "%0.6f", dAvgBits
);
717 strResult
+= m_szTempBuffer
;
718 strResult
+= "</AvgBits>\n";
720 strResult
+= strPrefix
;
721 strResult
+= "\t\t<TotalBits>";
722 sprintf(m_szTempBuffer
, "%0.6f", dTotalBits
);
723 strResult
+= m_szTempBuffer
;
724 strResult
+= "</TotalBits>\n";
727 strResult
+= strPrefix
;
728 strResult
+= "\t\t<ButtonCount>";
729 sprintf(m_szTempBuffer
, "%d", iButtonCount
);
730 strResult
+= m_szTempBuffer
;
731 strResult
+= "</ButtonCount>\n";
733 // Calculate the number of words and characters
734 strResult
+= strPrefix
;
735 strResult
+= "\t\t<Chars>";
737 // We want the number of symbols which might differ
738 // from the actual length of the text history.
739 int iNumChars
= m_vHistory
.size();
740 sprintf(m_szTempBuffer
, "%d", iNumChars
);
741 strResult
+= m_szTempBuffer
;
742 strResult
+= "</Chars>\n";
744 strResult
+= strPrefix
;
745 strResult
+= "\t\t<Words>";
746 double dNumWords
= (double) iNumChars
/ (double) 5;
747 sprintf(m_szTempBuffer
, "%0.2f", dNumWords
);
748 strResult
+= m_szTempBuffer
;
749 strResult
+= "</Words>\n";
756 dWPM
= (double) dNumWords
/ (m_pSpan
->GetElapsed() / 60.0);
757 dCPM
= (double) iNumChars
/ (m_pSpan
->GetElapsed() / 60.0);
760 strResult
+= strPrefix
;
761 strResult
+= "\t\t<WPM>";
762 sprintf(m_szTempBuffer
, "%0.3f", dWPM
);
763 strResult
+= m_szTempBuffer
;
764 strResult
+= "</WPM>\n";
766 strResult
+= strPrefix
;
767 strResult
+= "\t\t<CPM>";
768 sprintf(m_szTempBuffer
, "%0.3f", dCPM
);
769 strResult
+= m_szTempBuffer
;
770 strResult
+= "</CPM>\n";
772 string strPrefixTabTab
= strPrefix
;
773 strPrefixTabTab
+= "\t\t";
776 strResult
+= m_pSpan
->GetXML(strPrefixTabTab
);
781 string
CUserLogTrial::GetWindowCanvasXML(const string
& strPrefix
)
783 //CFunctionLogger f1("CUserLogTrial::GetWindowCanvasXML", g_pLogger);
785 string strResult
= "";
787 // Log the window location and size that was last used during this trial
788 strResult
+= strPrefix
;
789 strResult
+= "\t<WindowCoordinates>\n";
791 strResult
+= strPrefix
;
792 sprintf(m_szTempBuffer
, "\t\t<Top>%d</Top>\n", m_sWindowCoordinates
.top
);
793 strResult
+= m_szTempBuffer
;
795 strResult
+= strPrefix
;
796 sprintf(m_szTempBuffer
, "\t\t<Bottom>%d</Bottom>\n", m_sWindowCoordinates
.bottom
);
797 strResult
+= m_szTempBuffer
;
799 strResult
+= strPrefix
;
800 sprintf(m_szTempBuffer
, "\t\t<Left>%d</Left>\n", m_sWindowCoordinates
.left
);
801 strResult
+= m_szTempBuffer
;
803 strResult
+= strPrefix
;
804 sprintf(m_szTempBuffer
, "\t\t<Right>%d</Right>\n", m_sWindowCoordinates
.right
);
805 strResult
+= m_szTempBuffer
;
807 strResult
+= strPrefix
;
808 strResult
+= "\t</WindowCoordinates>\n";
810 // Log the canvas location and size that was last used during this trial
811 strResult
+= strPrefix
;
812 strResult
+= "\t<CanvasCoordinates>\n";
814 strResult
+= strPrefix
;
815 sprintf(m_szTempBuffer
, "\t\t<Top>%d</Top>\n", m_sCanvasCoordinates
.top
);
816 strResult
+= m_szTempBuffer
;
818 strResult
+= strPrefix
;
819 sprintf(m_szTempBuffer
, "\t\t<Bottom>%d</Bottom>\n", m_sCanvasCoordinates
.bottom
);
820 strResult
+= m_szTempBuffer
;
822 strResult
+= strPrefix
;
823 sprintf(m_szTempBuffer
, "\t\t<Left>%d</Left>\n", m_sCanvasCoordinates
.left
);
824 strResult
+= m_szTempBuffer
;
826 strResult
+= strPrefix
;
827 sprintf(m_szTempBuffer
, "\t\t<Right>%d</Right>\n", m_sCanvasCoordinates
.right
);
828 strResult
+= m_szTempBuffer
;
830 strResult
+= strPrefix
;
831 strResult
+= "\t</CanvasCoordinates>\n";
836 string
CUserLogTrial::GetParamsXML(const string
& strPrefix
)
838 //CFunctionLogger f1("CUserLogTrial::GetParamsXML", g_pLogger);
840 string strResult
= "";
842 if (m_vpParams
.size() > 0)
844 // Make parameters with the same name appear near each other in the results
845 sort(m_vpParams
.begin(), m_vpParams
.end(), CUserLogParam::ComparePtr
);
847 strResult
+= strPrefix
;
848 strResult
+= "\t<Params>\n";
850 string strPrefixPlusTabTab
= strPrefix
;
851 strPrefixPlusTabTab
+= "\t\t";
853 for (unsigned int i
= 0; i
< m_vpParams
.size(); i
++)
855 CUserLogParam
* pParam
= (CUserLogParam
*) m_vpParams
[i
];
857 strResult
+= GetParamXML(pParam
, strPrefixPlusTabTab
);
860 strResult
+= strPrefix
;
861 strResult
+= "\t</Params>\n";
868 int CUserLogTrial::GetButtonCount() {
871 for(VECTOR_NAV_CYCLE_PTR::iterator
it(m_vpNavCycles
.begin()); it
!= m_vpNavCycles
.end(); ++it
)
872 for(VECTOR_USER_BUTTON_PTR::iterator
it2((*it
)->vectorButtons
.begin()); it2
!= (*it
)->vectorButtons
.end(); ++it2
)
873 iCount
+= (*it2
)->GetCount();
878 double CUserLogTrial::GetTotalBits() {
881 for(VECTOR_NAV_CYCLE_PTR::iterator
it(m_vpNavCycles
.begin()); it
!= m_vpNavCycles
.end(); ++it
)
882 dBits
+= (*it
)->dBits
;
887 // Parameters can optionally be specified to be added to the Trial objects.
888 // This allows us to easily see what a certain parameter value was used
890 void CUserLogTrial::AddParam(const string
& strName
, const string
& strValue
, int iOptionMask
)
892 //CFunctionLogger f1("CUserLogTrial::AddParam", g_pLogger);
894 bool bTrackMultiple
= false;
896 if (iOptionMask
& userLogParamTrackMultiple
)
897 bTrackMultiple
= true;
899 // See if this matches an existing parameter value that we may want to
900 // overwrite. But only if we aren't suppose to keep track of multiple changes.
903 for (unsigned int i
= 0; i
< m_vpParams
.size(); i
++)
905 CUserLogParam
* pParam
= (CUserLogParam
*) m_vpParams
[i
];
909 if (pParam
->strName
.compare(strName
) == 0)
911 pParam
->strValue
= strValue
;
917 // We need to add a new param
918 CUserLogParam
* pNewParam
= new CUserLogParam
;
919 if (pNewParam
== NULL
)
921 g_pLogger
->Log("CUserLogTrial::AddParam, newParam was NULL!", logNORMAL
);
925 pNewParam
->strName
= strName
;
926 pNewParam
->strValue
= strValue
;
927 pNewParam
->strTimeStamp
= "";
929 // Parameters that can have multiple values logged will also log when they were changed
931 pNewParam
->strTimeStamp
= CTimeSpan::GetTimeStamp();
933 m_vpParams
.push_back(pNewParam
);
936 // Static method that generates the XML representation of a
937 // single param name value set. Used both to output params
938 // for a trial and for the parent UserLog object.
939 string
CUserLogTrial::GetParamXML(CUserLogParam
* pParam
, const string
& strPrefix
)
941 //CFunctionLogger f1("CUserLogTrial::GetParamXML", g_pLogger);
943 string strResult
= "";
947 strResult
+= strPrefix
;
949 strResult
+= pParam
->strName
;
952 if (pParam
->strTimeStamp
.length() > 0)
955 strResult
+= strPrefix
;
956 strResult
+= "\t<Value>";
957 strResult
+= pParam
->strValue
;
958 strResult
+= "</Value>\n";
960 strResult
+= strPrefix
;
961 strResult
+= "\t<Time>";
962 strResult
+= pParam
->strTimeStamp
;
963 strResult
+= "</Time>\n";
965 strResult
+= strPrefix
;
969 strResult
+= pParam
->strValue
;
973 strResult
+= pParam
->strName
;
980 // Returns a pointer to the currently active navigation cycle
981 NavCycle
* CUserLogTrial::GetCurrentNavCycle()
983 //CFunctionLogger f1("CUserLogTrial::GetCurrentNavCycle", g_pLogger);
985 if (m_vpNavCycles
.size() <= 0)
987 return m_vpNavCycles
[m_vpNavCycles
.size() - 1];
990 // Gets a pointer to the last NavLocation object
991 // in the current navication cycle.
992 NavLocation
* CUserLogTrial::GetCurrentNavLocation()
994 //CFunctionLogger f1("CUserLogTrial::GetCurrentNavLocation", g_pLogger);
996 // NavCycle* pCycle = GetCurrentNavCycle();
998 // if (pCycle == NULL)
1001 // if (pCycle->vectorNavLocations.size() <= 0)
1004 // return (NavLocation*) pCycle->vectorNavLocations[pCycle->vectorNavLocations.size() - 1];
1006 // New version - reverse iterate through the list and find the last nav cycle which has any locations
1008 for(VECTOR_NAV_CYCLE_PTR::reverse_iterator
it(m_vpNavCycles
.rbegin()); it
!= m_vpNavCycles
.rend(); ++it
) {
1009 if((*it
)->vectorNavLocations
.size() > 0)
1010 return (NavLocation
*) (*it
)->vectorNavLocations
[(*it
)->vectorNavLocations
.size() - 1];
1016 // Adds a new navgiation cycle to our collection
1017 NavCycle
* CUserLogTrial::AddNavCycle()
1019 //CFunctionLogger f1("CUserLogTrial::AddNavCycle", g_pLogger);
1021 NavCycle
* pNewCycle
= new NavCycle
;
1022 if (pNewCycle
== NULL
)
1024 g_pLogger
->Log("CUserLogTrial::AddNavCycle, failed to create NavCycle!", logNORMAL
);
1028 pNewCycle
->pSpan
= new CTimeSpan("Time", false);
1030 m_vpNavCycles
.push_back(pNewCycle
);
1034 string
CUserLogTrial::GetNavCyclesXML(const string
& strPrefix
)
1036 //CFunctionLogger f1("CUserLogTrial::GetNavCyclesXML", g_pLogger);
1038 string strResult
= "";
1040 string strPrefixTab
= strPrefix
;
1041 strPrefixTab
+= "\t";
1043 string strPrefixTabTab
= strPrefixTab
;
1044 strPrefixTabTab
+= "\t";
1046 string strPrefixTabTabTab
= strPrefixTabTab
;
1047 strPrefixTabTabTab
+= "\t";
1049 string strPrefixTabTabTabTab
= strPrefixTabTabTab
;
1050 strPrefixTabTabTabTab
+= "\t";
1052 strResult
+= strPrefixTab
;
1053 strResult
+= "<Navs>\n";
1055 for (unsigned int i
= 0; i
< m_vpNavCycles
.size(); i
++)
1057 NavCycle
* pCycle
= (NavCycle
*) m_vpNavCycles
[i
];
1061 strResult
+= strPrefixTabTab
;
1062 strResult
+= "<Nav>\n";
1064 if (pCycle
->pSpan
!= NULL
)
1065 strResult
+= pCycle
->pSpan
->GetXML(strPrefixTabTabTab
);
1067 if (pCycle
->vectorNavLocations
.size() > 0)
1069 strResult
+= strPrefixTabTabTab
;
1070 strResult
+= "<Locations>\n";
1072 for (unsigned int i
= 0; i
< pCycle
->vectorNavLocations
.size(); i
++)
1074 NavLocation
* pLocation
= (NavLocation
*) pCycle
->vectorNavLocations
[i
];
1076 if (pLocation
!= NULL
)
1077 strResult
+= GetLocationXML(pLocation
, strPrefixTabTabTabTab
);
1079 strResult
+= strPrefixTabTabTab
;
1080 strResult
+= "</Locations>\n";
1083 if (pCycle
->vectorMouseLocations
.size() > 0)
1085 strResult
+= strPrefixTabTabTab
;
1086 strResult
+= "<MousePositions>\n";
1088 for (unsigned int i
= 0; i
< pCycle
->vectorMouseLocations
.size(); i
++)
1090 CUserLocation
* pLocation
= (CUserLocation
*) pCycle
->vectorMouseLocations
[i
];
1092 if (pLocation
!= NULL
)
1094 strResult
+= pLocation
->GetXML(strPrefixTabTabTabTab
);
1098 strResult
+= strPrefixTabTabTab
;
1099 strResult
+= "</MousePositions>\n";
1102 if (pCycle
->vectorButtons
.size() > 0)
1104 strResult
+= strPrefixTabTabTab
;
1105 strResult
+= "<Buttons>\n";
1107 for (unsigned int i
= 0; i
< pCycle
->vectorButtons
.size(); i
++)
1109 CUserButton
* pButton
= (CUserButton
*) pCycle
->vectorButtons
[i
];
1113 strResult
+= pButton
->GetXML(strPrefixTabTabTabTab
);
1117 strResult
+= strPrefixTabTabTab
;
1118 strResult
+= "</Buttons>\n";
1121 strResult
+= strPrefixTabTab
;
1122 strResult
+= "</Nav>\n";
1127 strResult
+= strPrefixTab
;
1128 strResult
+= "</Navs>\n";
1133 // Construct based on some XML, second parameter is just to make signature
1134 // different from the normal constructor.
1135 CUserLogTrial::CUserLogTrial(const string
& strXML
, int iIgnored
)
1137 //CFunctionLogger f1("CUserLogTrial::CUserLogTrial(XML)", g_pLogger);
1140 VECTOR_STRING vNavs
;
1142 string strParams
= XMLUtil::GetElementString("Params", strXML
, true);
1143 string strWindow
= XMLUtil::GetElementString("WindowCoordinates", strXML
, true);
1144 string strCanvas
= XMLUtil::GetElementString("CanvasCoordinates", strXML
, true);
1145 string strNavs
= XMLUtil::GetElementString("Navs", strXML
, true);
1146 string strSummary
= XMLUtil::GetElementString("Summary", strXML
, true);
1147 string strSummaryTime
= XMLUtil::GetElementString("Time", strSummary
, true);
1148 vNavs
= XMLUtil::GetElementStrings("Nav", strNavs
, true);
1150 string strCurrentTrial
= XMLUtil::GetElementString("CurrentTrial", strXML
, false);
1151 if (strCurrentTrial
.length() > 0)
1153 // We copied the XML string directly into the member variable
1154 // including the start/end tag, so we need to reproduce the
1156 m_strCurrentTrial
= "\t\t\t<CurrentTrial>\n";
1157 m_strCurrentTrial
+= strCurrentTrial
;
1158 m_strCurrentTrial
+= "</CurrentTrial>\n";
1161 m_vpParams
= ParseParamsXML(strParams
);
1162 m_sWindowCoordinates
= ParseWindowXML(strWindow
);
1163 m_sCanvasCoordinates
= ParseWindowXML(strCanvas
);
1164 m_pSpan
= new CTimeSpan("Time", strSummaryTime
);
1166 // Process each <Nav> tag
1167 string strTime
= "";
1168 string strLocations
= "";
1169 string strMousePositions
= "";
1171 VECTOR_STRING vLocations
;
1172 VECTOR_STRING vMousePositions
;
1173 VECTOR_STRING vAdded
;
1175 for (VECTOR_STRING_ITER iter
= vNavs
.begin(); iter
< vNavs
.end(); iter
++)
1179 strMousePositions
= "";
1180 vLocations
.erase(vLocations
.begin(), vLocations
.end());
1181 vMousePositions
.erase(vMousePositions
.begin(), vMousePositions
.end());
1183 strTime
= XMLUtil::GetElementString("Time", *iter
, true);
1184 strLocations
= XMLUtil::GetElementString("Locations", *iter
, true);
1185 strMousePositions
= XMLUtil::GetElementString("MousePositions", *iter
, true);
1187 NavCycle
* pCycle
= new NavCycle();
1190 g_pLogger
->Log("CUserLogTrial::CUserLogTrial, failed to create NavCycle!", logNORMAL
);
1194 pCycle
->pSpan
= NULL
;
1196 if (strTime
.length() > 0)
1198 pCycle
->pSpan
= new CTimeSpan("Time", strTime
);
1200 if (strLocations
.length() > 0)
1202 vLocations
= XMLUtil::GetElementStrings("Location", strLocations
, true);
1204 for (VECTOR_STRING_ITER iter2
= vLocations
.begin(); iter2
< vLocations
.end(); iter2
++)
1206 vAdded
.erase(vAdded
.begin(), vAdded
.end());
1208 NavLocation
* pLocation
= new NavLocation();
1209 if (pLocation
== NULL
)
1211 g_pLogger
->Log("CUserLogTrial::CUserLogTrial, failed to create NavLocation!", logNORMAL
);
1215 pLocation
->strHistory
= XMLUtil::GetElementString("History", *iter2
);
1216 pLocation
->avgBits
= (double) XMLUtil::GetElementFloat("AvgBits", *iter2
);
1217 pLocation
->event
= (eUserLogEventType
) XMLUtil::GetElementInt("Event", *iter2
);
1218 pLocation
->numDeleted
= XMLUtil::GetElementInt("NumDeleted", *iter2
);
1220 pLocation
->span
= NULL
;
1221 string strTime
= XMLUtil::GetElementString("Time", *iter2
);
1222 pLocation
->span
= new CTimeSpan("Time", strTime
);
1224 // Handle the multiple <Add> tags that might exist
1225 vAdded
= XMLUtil::GetElementStrings("Add", *iter2
);
1226 pLocation
->pVectorAdded
= new Dasher::VECTOR_SYMBOL_PROB
;
1228 for (VECTOR_STRING_ITER iter3
= vAdded
.begin(); iter3
< vAdded
.end(); iter3
++)
1230 Dasher::SymbolProb
sAdd(0, // We don't have the original integer symbol index
1231 XMLUtil::GetElementString("Text", *iter3
),
1232 XMLUtil::GetElementFloat("Prob", *iter3
));
1234 if (pLocation
->pVectorAdded
!= NULL
)
1235 pLocation
->pVectorAdded
->push_back(sAdd
);
1237 // Also track it in one complete vector of all the adds
1238 m_vHistory
.push_back(sAdd
);
1241 // If this was a deleted event, then we need to erase some stuff from the running history
1242 // Be careful not to pop more things than we have (this will hork the
1243 // memory up on linux but not windows).
1244 int iActualNumToDelete
= min((int) m_vHistory
.size(), pLocation
->numDeleted
);
1245 for (int i
= 0; i
< iActualNumToDelete
; i
++)
1246 m_vHistory
.pop_back();
1248 pCycle
->vectorNavLocations
.push_back(pLocation
);
1252 if (strMousePositions
.length() > 0)
1254 vMousePositions
= XMLUtil::GetElementStrings("Pos", strMousePositions
, true);
1255 for (VECTOR_STRING_ITER iter2
= vMousePositions
.begin(); iter2
< vMousePositions
.end(); iter2
++)
1257 CUserLocation
* pLocation
= new CUserLocation(*iter2
);
1258 pCycle
->vectorMouseLocations
.push_back(pLocation
);
1263 m_vpNavCycles
.push_back(pCycle
);
1268 // Helper that parses parameters out of the XML block, used by UserLog
1269 // and by UserLogTrial to do the same thing.
1270 VECTOR_USER_LOG_PARAM_PTR
CUserLogTrial::ParseParamsXML(const string
& strXML
)
1272 //CFunctionLogger f1("CUserLogTrial::ParseParamsXML", g_pLogger);
1274 VECTOR_USER_LOG_PARAM_PTR vResult
;
1275 VECTOR_NAME_VALUE_PAIR vParams
;
1277 vParams
= XMLUtil::GetNameValuePairs(strXML
, true);
1279 // Handle adding all the name/value parameter pairs. XML looks like:
1280 // <Eyetracker>0</Eyetracker>
1282 // <Value>7.0100</Value>
1283 // <Time>15:48:53.140</Time>
1285 for (VECTOR_NAME_VALUE_PAIR_ITER iter
= vParams
.begin(); iter
< vParams
.end(); iter
++)
1287 CUserLogParam
* pParam
= new CUserLogParam();
1291 pParam
->strName
= iter
->strName
;
1293 // See if we have a type that has a timestamp
1294 string strValue
= XMLUtil::GetElementString("Value", iter
->strValue
, true);
1295 string strTime
= XMLUtil::GetElementString("Time", iter
->strValue
, true);
1297 if ((strValue
.length() > 0) || (strTime
.length() > 0))
1299 pParam
->strValue
= strValue
;
1300 pParam
->strTimeStamp
= strTime
;
1303 pParam
->strValue
= iter
->strValue
;
1305 pParam
->options
= 0;
1307 vResult
.push_back(pParam
);
1314 // Parse our window or canvas coorindates from XML
1315 WindowSize
CUserLogTrial::ParseWindowXML(const string
& strXML
)
1317 //CFunctionLogger f1("CUserLogTrial::ParseWindowXML", g_pLogger);
1321 sResult
.top
= XMLUtil::GetElementInt("Top", strXML
);
1322 sResult
.bottom
= XMLUtil::GetElementInt("Bottom", strXML
);
1323 sResult
.left
= XMLUtil::GetElementInt("Left", strXML
);
1324 sResult
.right
= XMLUtil::GetElementInt("Right", strXML
);
1329 // Returns a vector that contains the tab delimited mouse
1330 // coordinates for each of our navigation cycles.
1331 VECTOR_STRING
CUserLogTrial::GetTabMouseXY(bool bReturnNormalized
)
1333 //CFunctionLogger f1("CUserLogTrial::GetTabMouseXY", g_pLogger);
1335 VECTOR_STRING vResult
;
1336 for (VECTOR_NAV_CYCLE_PTR_ITER iter
= m_vpNavCycles
.begin(); iter
< m_vpNavCycles
.end(); iter
++)
1338 string strResult
= "";
1342 for (VECTOR_USER_LOCATION_PTR_ITER iter2
= (*iter
)->vectorMouseLocations
.begin();
1343 iter2
< (*iter
)->vectorMouseLocations
.end();
1347 strResult
+= (*iter2
)->GetTabMouseXY(bReturnNormalized
);
1351 vResult
.push_back(strResult
);
1357 // Calculates the mouse density with a given grid size number of buckets.
1358 // Each element of the vector is a 2D array of double values from 0.0 - 1.0.
1360 // NOTE: We allocate the memory here for the double**, our caller must
1361 // handle freeing it!
1362 VECTOR_DENSITY_GRIDS
CUserLogTrial::GetMouseDensity(int iGridSize
)
1364 //CFunctionLogger f1("CUserLogTrial::GetMouseDensity", g_pLogger);
1366 VECTOR_DENSITY_GRIDS vResult
;
1368 for (VECTOR_NAV_CYCLE_PTR_ITER iter
= m_vpNavCycles
.begin(); iter
< m_vpNavCycles
.end(); iter
++)
1372 DENSITY_GRID ppGrid
;
1374 // Init the grid with all 0.0 values
1375 ppGrid
= new double*[iGridSize
];
1376 for (int i
= 0; i
< iGridSize
; i
++)
1377 ppGrid
[i
] = new double[iGridSize
];
1379 for (int i
= 0; i
< iGridSize
; i
++)
1380 for (int j
= 0; j
< iGridSize
; j
++)
1383 unsigned int iCount
= 0;
1385 // Assign each mouse to location to one of the buckets and increment
1386 // the count on that bucket.
1387 for (VECTOR_USER_LOCATION_PTR_ITER iter2
= (*iter
)->vectorMouseLocations
.begin();
1388 iter2
< (*iter
)->vectorMouseLocations
.end();
1395 (*iter2
)->GetMouseGridLocation(iGridSize
, &i
, &j
);
1396 // Increment the count on this location, we'll throw away points
1397 // that were outside the canvas grid.
1398 if ((i
< iGridSize
) && (j
< iGridSize
) && (i
>= 0) && (j
>= 0))
1400 // We reverse j and i to get x to increase left top right
1401 // and y from top to bottom
1402 ppGrid
[j
][i
] = ppGrid
[j
][i
] + 1.0;
1407 // Now normalize everything so each grid location is a
1408 // percentage of the time we spent in that location.
1409 for (int i
= 0; i
< iGridSize
; i
++)
1412 for (int j
= 0; j
< iGridSize
; j
++)
1414 ppGrid
[i
][j
] = ppGrid
[i
][j
] / (double) iCount
;
1419 vResult
.push_back(ppGrid
);
1426 // Merge the density of two grids together. This is done but adding their values
1427 // and dividing by two. If either pointer is NULL, then we return the other grid
1428 // values intact. We free the memory in our parameter grids.
1429 DENSITY_GRID
CUserLogTrial::MergeGrids(int iGridSize
, DENSITY_GRID ppGridA
, DENSITY_GRID ppGridB
)
1431 //CFunctionLogger f1("CUserLogTrial::MergeGrids", g_pLogger);
1433 DENSITY_GRID ppResult
;
1434 ppResult
= new double*[iGridSize
];
1435 for (int i
= 0; i
< iGridSize
; i
++)
1436 ppResult
[i
] = new double[iGridSize
];
1438 for (int i
= 0; i
< iGridSize
; i
++)
1439 for (int j
= 0; j
< iGridSize
; j
++)
1440 ppResult
[i
][j
] = 0.0;
1442 // Both NULL, then return grid of all 0.0's
1443 if ((ppGridA
== NULL
) && (ppGridB
== NULL
))
1446 if (ppGridA
== NULL
)
1448 // grid A is NULL, return copy of grid B
1449 for (int i
= 0; i
< iGridSize
; i
++)
1451 for (int j
= 0; j
< iGridSize
; j
++)
1452 ppResult
[i
][j
] = ppGridB
[i
][j
];
1455 else if (ppGridB
== NULL
)
1457 // grid B is NULL, return copy of grid A
1458 for (int i
= 0; i
< iGridSize
; i
++)
1460 for (int j
= 0; j
< iGridSize
; j
++)
1461 ppResult
[i
][j
] = ppGridA
[i
][j
];
1467 // Normal case, merge the two density grids
1468 for (int i
= 0; i
< iGridSize
; i
++)
1470 for (int j
= 0; j
< iGridSize
; j
++)
1471 ppResult
[i
][j
] = (ppGridA
[i
][j
] + ppGridB
[i
][j
]) / 2.0;
1475 if (ppGridA
!= NULL
)
1477 for (int i
= 0; i
< iGridSize
; i
++)
1479 if (ppGridA
[i
] != NULL
)
1489 if (ppGridB
!= NULL
)
1491 for (int i
= 0; i
< iGridSize
; i
++)
1493 if (ppGridB
[i
] != NULL
)