/// <summary> /// Writes all or part of this data object to XML. If this data object owns other /// data objects that will also be written, this method may leave some XML elements /// open, which will be closed with a later call to <i>WriteXmlFooter</i>. /// </summary> /// <param name="writer">An open XML writer. The writer will be left open by this method /// after writing.</param> /// <returns>Returns <b>true</b> if successful; <b>false</b> otherwise.</returns> public bool WriteXmlHeader(XmlTextWriter writer) { writer.WriteStartElement("Movement"); writer.WriteAttributeString("count", XmlConvert.ToString(_moves.Count)); writer.WriteAttributeString("travel", XmlConvert.ToString(Math.Round(this.Travel, 4))); writer.WriteAttributeString("duration", XmlConvert.ToString(this.Duration)); // write out the submovement information Profiles resampled = CreateResampledProfiles(); if (resampled.IsEmpty) // this can happen if the trial is terminated prematurely { writer.WriteAttributeString("submovements", XmlConvert.ToString(0)); writer.WriteAttributeString("maxVelocity", PointF.Empty.ToString()); writer.WriteAttributeString("maxAcceleration", PointF.Empty.ToString()); writer.WriteAttributeString("maxJerk", PointF.Empty.ToString()); } else { int nsub = GetNumSubmovements(); // from smoothed velocity profile int vidx = SeriesEx.Max(resampled.Velocity, 0, -1); int aidx = SeriesEx.Max(resampled.Acceleration, 0, -1); int jidx = SeriesEx.Max(resampled.Jerk, 0, -1); writer.WriteAttributeString("submovements", XmlConvert.ToString(nsub)); writer.WriteAttributeString("maxVelocity", resampled.Velocity[vidx].ToString()); writer.WriteAttributeString("maxAcceleration", resampled.Acceleration[aidx].ToString()); writer.WriteAttributeString("maxJerk", resampled.Jerk[jidx].ToString()); } // write out all of MacKenzie's path analysis measures PathAnalyses analyses = DoPathAnalyses(_owner.Start, _owner.TargetCenterFromStart); writer.WriteAttributeString("taskAxisCrossings", XmlConvert.ToString(analyses.TaskAxisCrossings)); writer.WriteAttributeString("movementDirectionChanges", XmlConvert.ToString(analyses.MovementDirectionChanges)); writer.WriteAttributeString("orthogonalDirectionChanges", XmlConvert.ToString(analyses.OrthogonalDirectionChanges)); writer.WriteAttributeString("movementVariability", XmlConvert.ToString(Math.Round(analyses.MovementVariability, 4))); writer.WriteAttributeString("movementError", XmlConvert.ToString(Math.Round(analyses.MovementError, 4))); writer.WriteAttributeString("movementOffset", XmlConvert.ToString(Math.Round(analyses.MovementOffset, 4))); // write out all the mouse move points that make up this trial int i = 0; foreach (TimePointF pt in _moves) { writer.WriteStartElement("move"); writer.WriteAttributeString("index", XmlConvert.ToString(i++)); writer.WriteAttributeString("point", pt.ToString()); writer.WriteEndElement(); // </move> } writer.WriteEndElement(); // </Movement> return(true); }
/// <summary> /// Performs any analyses on this data object and writes the results to a space-delimitted /// file for subsequent copy-and-pasting into a spreadsheet like Microsoft Excel or SAS JMP. /// </summary> /// <param name="writer">An open stream writer pointed to a text file. The writer will be closed by /// this method after writing.</param> /// <returns>True if writing is successful; false otherwise.</returns> public bool WriteResultsToTxt(StreamWriter writer) { bool success = true; try { // write an identifying title for this file. writer.WriteLine("FittsStudy log analysis results for '{0}' on {1} at {2}. FittsStudy.exe version: {3}.\r\n", this.FilenameBase + ".xml", // Note: ((FileStream) writer.BaseStream).Name holds the file path. DateTime.Now.ToLongDateString(), DateTime.Now.ToLongTimeString(), Assembly.GetExecutingAssembly().GetName().Version); // write the column headers with comma separation -- see Columns.txt writer.WriteLine("Subject,Circular?,Block,Condition,Trial,Practice?,Metronome?,MT%,MTPred,MT,a(given),b(given),A,W,ID,axis,angle,ae(1d),dx(1d),ae(2d),dx(2d),Ae(1d),SD(1d),We(1d),IDe(1d),TP(1d),Ae(2d),SD(2d),We(2d),IDe(2d),TP(2d),MTe,MTRatio,MeanMTe,MeanMTe(sx),MeanMTe(tx),Entries,Overshoots,Error?,Errors,Errors(sx),Errors(tx),Error%,Error%(sx),Error%(tx),SpatialOutlier?,TemporalOutlier?,SpatialOutliers,TemporalOutliers,StartX,StartY,EndX,EndY,TargetX,TargetY,Travel,Duration,Submovements,MaxVelocity,MaxAcceleration,MaxJerk,tMaxVelocity,tMaxAcceleration,tMaxJerk,TAC,MDC,ODC,MV,ME,MO,N,Fitts_TP_avg(1d),Fitts_TP_inv(1d),Fitts_a(1d),Fitts_b(1d),Fitts_r(1d),Fitts_TP_avg(2d),Fitts_TP_inv(2d),Fitts_a(2d),Fitts_b(2d),Fitts_r(2d),Error_m(1d),Error_b(1d),Error_r(1d),Error_m(2d),Error_b(2d),Error_r(2d)"); // pre-compute session-level values here Model fm = this.BuildModel(); fm.RoundTerms(4); // now iterate through all of the conditions for (int i = 0; i < _conditions.Count; i++) { // get the condition and pre-compute any condition-level values here. we could // compute them again and again while writing each trial, but that is inefficient. ConditionData cd = _conditions[i]; double cAe_1d = Math.Round(cd.GetAe(false), 4); double cSD_1d = Math.Round(cd.GetSD(false), 4); double cWe_1d = Math.Round(cd.GetWe(false), 4); double cIDe_1d = Math.Round(cd.GetIDe(false), 4); double cTP_1d = Math.Round(cd.GetTP(false), 4); double cAe_2d = Math.Round(cd.GetAe(true), 4); double cSD_2d = Math.Round(cd.GetSD(true), 4); double cWe_2d = Math.Round(cd.GetWe(true), 4); double cIDe_2d = Math.Round(cd.GetIDe(true), 4); double cTP_2d = Math.Round(cd.GetTP(true), 4); long meanMTe = cd.GetMTe(ExcludeOutliersType.None); long meanMTe_sx = cd.GetMTe(ExcludeOutliersType.Spatial); long meanMTe_tx = cd.GetMTe(ExcludeOutliersType.Temporal); int errors = cd.GetNumErrors(ExcludeOutliersType.None); int errors_sx = cd.GetNumErrors(ExcludeOutliersType.Spatial); int errors_tx = cd.GetNumErrors(ExcludeOutliersType.Temporal); double errorPct = Math.Round(cd.GetErrorRate(ExcludeOutliersType.None), 4); double errorPct_sx = Math.Round(cd.GetErrorRate(ExcludeOutliersType.Spatial), 4); double errorPct_tx = Math.Round(cd.GetErrorRate(ExcludeOutliersType.Temporal), 4); int nSpatialOutliers = cd.NumSpatialOutliers; int nTemporalOutliers = cd.NumTemporalOutliers; // within each condition, iterate through the trials. start at index 1 because // the trial at index 0 is the special start-area trial, and should be ignored. for (int j = 1; j <= cd.NumTrials; j++) { TrialData td = cd[j]; MovementData md = td.Movement; // the movement path itself // calculate the resampled submovement profiles MovementData.Profiles profiles = md.CreateResampledProfiles(); int vidx = SeriesEx.Max(profiles.Velocity, 0, -1); // max velocity int aidx = SeriesEx.Max(profiles.Acceleration, 0, -1); // max acceleration int jidx = SeriesEx.Max(profiles.Jerk, 0, -1); // max jerk // calculate the MacKenzie et al. (2001) path analysis measures MovementData.PathAnalyses analyses = md.DoPathAnalyses(td.Start, td.TargetCenterFromStart); // write the spreadsheet row here writer.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31},{32},{33},{34},{35},{36},{37},{38},{39},{40},{41},{42},{43},{44},{45},{46},{47},{48},{49},{50},{51},{52},{53},{54},{55},{56},{57},{58},{59},{60},{61},{62},{63},{64},{65},{66},{67},{68},{69},{70},{71},{72},{73},{74},{75},{76},{77},{78},{79},{80},{81},{82},{83},{84},{85},{86}", _subject, // Subject, _circular ? 1 : 0, // Circular? cd.Block, // Block, cd.Index, // Condition, td.Number, // Trial, (== j) td.IsPractice ? 1 : 0, // Practice?, cd.UsedMetronome ? 1 : 0, // Metronome?, (== td.UsedMetronome) cd.MTPct, // MT%, cd.MTPred, // MTPred, cd.MT, // MT, (== td.MT) _intercept, // a(given), _slope, // b(given), cd.A, // A, (== td.A) cd.W, // W, (== td.W) Math.Round(cd.ID, 4), // ID, (== td.ID) Math.Round(GeotrigEx.Radians2Degrees(td.Axis), 4), // axis, Math.Round(GeotrigEx.Radians2Degrees(td.Angle), 4), // angle, Math.Round(td.GetAe(false), 4), // ae(1d), Math.Round(td.GetDx(false), 4), // dx(1d), Math.Round(td.GetAe(true), 4), // ae(2d), Math.Round(td.GetDx(true), 4), // dx(2d), cAe_1d, // Ae(1d), cSD_1d, // SD(1d), cWe_1d, // We(1d), cIDe_1d, // IDe(1d), cTP_1d, // TP(1d), cAe_2d, // Ae(2d), cSD_2d, // SD(2d), cWe_2d, // We(2d), cIDe_2d, // IDe(2d), cTP_2d, // TP(2d), td.MTe, // MTe, Math.Round(td.MTRatio, 4), // MTRatio, meanMTe, // MeanMTe, meanMTe_sx, // MeanMTe(sx), meanMTe_tx, // MeanMTe(tx), td.TargetEntries, // Entries, td.TargetOvershoots, // Overshoots, td.IsError ? 1 : 0, // Error?, errors, // Errors, errors_sx, // Errors(sx), errors_tx, // Errors(tx), errorPct, // Error%, errorPct_sx, // Error%(sx), errorPct_tx, // Error%(tx), td.IsSpatialOutlier ? 1 : 0, // SpatialOutlier?, td.IsTemporalOutlier ? 1 : 0, // TemporalOutlier?, nSpatialOutliers, // SpatialOutliers, nTemporalOutliers, // TemporalOutliers, td.Start.X, // StartX td.Start.Y, // StartY td.End.X, // EndX td.End.Y, // EndY td.TargetCenterFromStart.X, // TargetX td.TargetCenterFromStart.Y, // TargetY Math.Round(md.Travel, 4), // Travel, md.Duration, // Duration, md.GetNumSubmovements(), // Submovements, Math.Round(profiles.Velocity[vidx].Y, 4), // MaxVelocity, Math.Round(profiles.Acceleration[aidx].Y, 4), // MaxAcceleration, Math.Round(profiles.Jerk[jidx].Y, 4), // MaxJerk, Math.Round(profiles.Velocity[vidx].X / md.Duration, 4), // tMaxVelocity, Math.Round(profiles.Acceleration[aidx].X / md.Duration, 4), // tMaxAcceleration, Math.Round(profiles.Jerk[jidx].X / md.Duration, 4), // tMaxJerk, analyses.TaskAxisCrossings, // TAC, analyses.MovementDirectionChanges, // MDC, analyses.OrthogonalDirectionChanges, // ODC, Math.Round(analyses.MovementVariability, 4), // MV, Math.Round(analyses.MovementError, 4), // ME, Math.Round(analyses.MovementOffset, 4), // MO, fm.N, // N, fm.Fitts_TP_avg_1d, // Fitts_TP_avg(1d), fm.Fitts_TP_inv_1d, // Fitts_TP_inv(1d), fm.Fitts_a_1d, // Fitts_a(1d), fm.Fitts_b_1d, // Fitts_b(1d), fm.Fitts_r_1d, // Fitts_r(1d), fm.Fitts_TP_avg_2d, // Fitts_TP_avg(2d), fm.Fitts_TP_inv_2d, // Fitts_TP_inv(2d), fm.Fitts_a_2d, // Fitts_a(2d), fm.Fitts_b_2d, // Fitts_b(2d), fm.Fitts_r_2d, // Fitts_r(2d), fm.Error_m_1d, // Error_m(1d), fm.Error_b_1d, // Error_b(1d), fm.Error_r_1d, // Error_r(1d), fm.Error_m_2d, // Error_m(2d), fm.Error_b_2d, // Error_b(2d), fm.Error_r_2d // Error_r(2d) ); } } } catch (IOException ioex) { Console.WriteLine(ioex); success = false; } catch (Exception ex) { Console.WriteLine(ex); success = false; } finally { if (writer != null) { writer.Close(); } } return(success); }