private Graph gphErr; // error rates /// <summary> /// /// </summary> /// <param name="sd"></param> public ResultsForm(SessionData sd, string filename) { InitializeComponent(); this.Text = filename; // create a graph for speed gphWpm = new Graph(); gphWpm.Title = "Speed"; gphWpm.XAxisName = "Trial No."; gphWpm.YAxisName = "WPM"; gphWpm.XAxisTicks = (sd.NumTrials > 1) ? sd.NumTrials - 1 : 1; gphWpm.XAxisDecimals = 1; gphWpm.YAxisDecimals = 1; gphWpm.Legend = true; // create a graph for error rates gphErr = new Graph(); gphErr.Title = "Error Rates"; gphErr.XAxisName = "Trial No."; gphErr.YAxisName = "Error Rate"; gphErr.XAxisTicks = (sd.NumTrials > 1) ? sd.NumTrials - 1 : 1; gphErr.XAxisDecimals = 1; gphErr.YAxisDecimals = 3; gphErr.Legend = true; // create each series we wish to graph Graph.Series sWpm = new Graph.Series("WPM", Color.Red, Color.Red, true, true); Graph.Series sAdj = new Graph.Series("AdjWPM", Color.Gray, Color.Gray, true, true); Graph.Series sTot = new Graph.Series("Total", Color.Red, Color.Red, true, true); Graph.Series sUnc = new Graph.Series("Uncorrected", Color.Gray, Color.Gray, true, true); Graph.Series sCor = new Graph.Series("Corrected", Color.LightGray, Color.LightGray, true, true); // use these lists to compute the means and stdevs for each series List <double> mWpm = new List <double>(); List <double> sdWpm = new List <double>(); List <double> mAdj = new List <double>(); List <double> sdAdj = new List <double>(); List <double> mTot = new List <double>(); List <double> sdTot = new List <double>(); List <double> mUnc = new List <double>(); List <double> sdUnc = new List <double>(); List <double> mCor = new List <double>(); List <double> sdCor = new List <double>(); // add the points for each series for (int i = 0; i < sd.NumTrials; i++) { if (sd[i].NumEntries > 0) { sWpm.AddPoint(new PointR(i + 1, sd[i].WPM)); mWpm.Add(sd[i].WPM); sdWpm.Add(sd[i].WPM); sAdj.AddPoint(new PointR(i + 1, sd[i].AdjWPM)); mAdj.Add(sd[i].AdjWPM); sdAdj.Add(sd[i].AdjWPM); sTot.AddPoint(new PointR(i + 1, sd[i].TotalErrorRate)); mTot.Add(sd[i].TotalErrorRate); sdTot.Add(sd[i].TotalErrorRate); sUnc.AddPoint(new PointR(i + 1, sd[i].UncorrectedErrorRate)); mUnc.Add(sd[i].UncorrectedErrorRate); sdUnc.Add(sd[i].UncorrectedErrorRate); sCor.AddPoint(new PointR(i + 1, sd[i].CorrectedErrorRate)); mCor.Add(sd[i].CorrectedErrorRate); sdCor.Add(sd[i].CorrectedErrorRate); } } // add the means and standard deviations to the series' names sWpm.Name = String.Format("{0} (µ={1:f1}, σ={2:f1})", sWpm.Name, StatsEx.Mean(mWpm.ToArray()), StatsEx.StdDev(sdWpm.ToArray())); sAdj.Name = String.Format("{0} (µ={1:f1}, σ={2:f1})", sAdj.Name, StatsEx.Mean(mAdj.ToArray()), StatsEx.StdDev(sdAdj.ToArray())); sTot.Name = String.Format("{0} (µ={1:f3}, σ={2:f3})", sTot.Name, StatsEx.Mean(mTot.ToArray()), StatsEx.StdDev(sdTot.ToArray())); sUnc.Name = String.Format("{0} (µ={1:f3}, σ={2:f3})", sUnc.Name, StatsEx.Mean(mUnc.ToArray()), StatsEx.StdDev(sdUnc.ToArray())); sCor.Name = String.Format("{0} (µ={1:f3}, σ={2:f3})", sCor.Name, StatsEx.Mean(mCor.ToArray()), StatsEx.StdDev(sdCor.ToArray())); // add the origin point to the graphs Graph.Series origin = new Graph.Series(String.Empty, Color.Black, Color.Black, false, false); origin.AddPoint(1.0, 0.0); gphWpm.AddSeries(origin); gphErr.AddSeries(origin); // finally, add the series gphWpm.AddSeries(sWpm); gphWpm.AddSeries(sAdj); gphErr.AddSeries(sTot); gphErr.AddSeries(sUnc); gphErr.AddSeries(sCor); }
/// <summary> /// Shows all the information for a trial-level node highlighted in the treeview. /// </summary> /// <param name="node">The highlighted node in the left treeview, assumed to represent an individual trial.</param> private void ShowTrialInfo(TreeNode node) { // // Get the trial and its condition from the 'Tag' field from the selected node. // _tdata = (TrialData)node.Tag; _cdata = (ConditionData)node.Parent.Tag; // // Get the resampled and smoothed velocity, acceleration, and jerk profiles. // MovementData.Profiles resampled = _tdata.Movement.CreateResampledProfiles(); MovementData.Profiles smoothed = _tdata.Movement.CreateSmoothedProfiles(); // // Clear any series in the graphs. // grfDistance.ClearSeries(); grfVelocity.ClearSeries(); grfAcceleration.ClearSeries(); grfJerk.ClearSeries(); // // Graph the resampled distance to the target over time. // List <PointF> rdist = new List <PointF>(resampled.Position.Count); for (int i = 0; i < resampled.Position.Count; i++) { long t = resampled.Position[i].Time - resampled.Position[0].Time; double dx = GeotrigEx.Distance(resampled.Position[i], _tdata.TargetCenterFromStart); rdist.Add(new PointF(t, (float)dx)); } Graph.Series rd = new Graph.Series("resampled distance", Color.Salmon, Color.Salmon, false, true); rd.AddPoints(rdist); // // Graph the smoothed distance from the target over time. // List <PointF> sdist = new List <PointF>(smoothed.Position.Count); for (int i = 0; i < smoothed.Position.Count; i++) { long t = smoothed.Position[i].Time - smoothed.Position[0].Time; double dx = GeotrigEx.Distance(smoothed.Position[i], _tdata.TargetCenterFromStart); sdist.Add(new PointF(t, (float)dx)); } Graph.Series sd = new Graph.Series("smoothed distance", Color.MediumBlue, Color.MediumBlue, false, true); sd.AddPoints(sdist); grfDistance.AddSeries(rd); grfDistance.AddSeries(sd); // // Graph the velocity. // Graph.Series rv = new Graph.Series("resampled velocity", Color.Salmon, Color.Salmon, false, true); rv.AddPoints(resampled.Velocity); Graph.Series sv = new Graph.Series("smoothed velocity", Color.MediumBlue, Color.MediumBlue, false, true); sv.AddPoints(smoothed.Velocity); grfVelocity.AddSeries(rv); grfVelocity.AddSeries(sv); // // Graph the acceleration. // Graph.Series aaxis = new Graph.Series("zero line", Color.Gray, Color.Gray, false, true); aaxis.AddPoint(0f, 0f); aaxis.AddPoint(resampled.Acceleration[resampled.Acceleration.Count - 1].X, 0f); Graph.Series ra = new Graph.Series("resampled acceleration", Color.Salmon, Color.Salmon, false, true); ra.AddPoints(resampled.Acceleration); Graph.Series sa = new Graph.Series("smoothed acceleration", Color.MediumBlue, Color.MediumBlue, false, true); sa.AddPoints(smoothed.Acceleration); grfAcceleration.AddSeries(aaxis); grfAcceleration.AddSeries(ra); grfAcceleration.AddSeries(sa); // // Graph the jerk. // Graph.Series jaxis = new Graph.Series("zero line", Color.Gray, Color.Gray, false, true); jaxis.AddPoint(0f, 0f); jaxis.AddPoint(resampled.Jerk[resampled.Jerk.Count - 1].X, 0f); Graph.Series rj = new Graph.Series("resampled jerk", Color.Salmon, Color.Salmon, false, true); rj.AddPoints(resampled.Jerk); Graph.Series sj = new Graph.Series("smoothed jerk", Color.MediumBlue, Color.MediumBlue, false, true); sj.AddPoints(smoothed.Jerk); grfJerk.AddSeries(jaxis); grfJerk.AddSeries(rj); grfJerk.AddSeries(sj); // // Now show the trial-level XML in the web control. // XmlTextWriter writer = null; string tmpXml = String.Format("{0}{1}.xml", Path.GetTempPath(), Path.GetRandomFileName()); try { writer = new XmlTextWriter(tmpXml, Encoding.UTF8); writer.Formatting = Formatting.Indented; _tdata.WriteXmlHeader(writer); } catch (Exception ex) { Console.WriteLine(ex); tmpXml = String.Empty; } finally { if (writer != null) { writer.Close(); } if (tmpXml != String.Empty) { webXml.Navigate(tmpXml); rtxXml.LoadFile(tmpXml, RichTextBoxStreamType.PlainText); _tmpXml.Add(tmpXml); } } // // Invalidate everything that needs to be repainted based on the node just selected. // pnlTrial.Invalidate(); grfDistance.Invalidate(); grfVelocity.Invalidate(); grfAcceleration.Invalidate(); grfJerk.Invalidate(); }
/// <summary> /// Handles the Load event for the form. Loads the data from the model into the graph control, and /// displays the model terms in the read-only textbox. /// </summary> /// <param name="sender">The sender of this event.</param> /// <param name="e">The arguments for this event.</param> private void ModelForm_Load(object sender, EventArgs e) { // // Ask the session instance to build a Fitts' law model. // Model fm = _sdata.BuildModel(); fm.RoundTerms(4); // round for display if (_sdata.MTPct == null) // regular Fitts experiment { PointF min = PointF.Empty; PointF max = PointF.Empty; // // Add the (IDe, MTe) points for plotting. // Graph.Series s1 = new Graph.Series("Fitts model 1D points", Color.Tomato, Color.Tomato, true, false); s1.AddPoints(fm.FittsPts_1d); // add the points to the series grfFitts1D.AddSeries(s1); // add the series to the graph if (s1.NumPoints > 1) { RectangleF r = s1.DomainRange; // get the domain and range of the first series Graph.Series s3 = new Graph.Series("Fitts model 1D line", Color.OrangeRed, Color.OrangeRed, false, true); s3.AddPoint(0f, (float)fm.Fitts_a_1d); // y-intercept s3.AddPoint(r.Right, (float)(fm.Fitts_b_1d * r.Right + fm.Fitts_a_1d)); // rightmost point grfFitts1D.AddSeries(s3); min = s3[0]; max = s3[1]; } if (_sdata.Is2D) { Graph.Series s2 = new Graph.Series("Fitts model 2D points", Color.Tomato, Color.Tomato, true, false); s2.AddPoints(fm.FittsPts_2d); grfFitts2D.AddSeries(s2); if (s2.NumPoints > 1) { RectangleF r = s2.DomainRange; // get the domain and range of the first series Graph.Series s4 = new Graph.Series("Fitts model 2D line", Color.OrangeRed, Color.OrangeRed, false, true); s4.AddPoint(0f, (float)fm.Fitts_a_2d); // y-intercept s4.AddPoint(r.Right, (float)(fm.Fitts_b_2d * r.Right + fm.Fitts_a_2d)); // rightmost point grfFitts2D.AddSeries(s4); min.X = Math.Min(min.X, s4[0].X); min.Y = Math.Min(min.Y, s4[0].Y); max.X = Math.Max(max.X, s4[1].X); max.Y = Math.Max(max.Y, s4[1].Y); } } // add a series to ensure both graphs display at the same scale for better comparisons Graph.Series s5 = new Graph.Series("Fitts min-max", Color.Black, Color.Black, false, false); s5.AddPoint(min); s5.AddPoint(max); grfFitts1D.AddSeries(s5); grfFitts2D.AddSeries(s5); // // Fill the text box with the Fitts' law model parameters. // txtFitts1D.Text = String.Format("Fitts' Law - Univariate for Subject {0}\r\n", _sdata.Subject) + String.Format("N = {0}\r\n", fm.N) + String.Format("MTavg = {0} ms\r\n", fm.MTe) + String.Format("Error% = {0}\r\n", fm.ErrorPct) + String.Format("MT(1d) = {0} + {1} * ID\r\n", fm.Fitts_a_1d, fm.Fitts_b_1d) + String.Format("TP_avg(1d) = {0} bits/s\r\n", fm.Fitts_TP_avg_1d) + String.Format("TP_inv(1d) = {0} bits/s\r\n", fm.Fitts_TP_inv_1d) + String.Format("a(1d) = {0} ms\r\n", fm.Fitts_a_1d) + String.Format("b(1d) = {0} ms/bit\r\n", fm.Fitts_b_1d) + String.Format("r(1d) = {0}\r\n", fm.Fitts_r_1d); if (_sdata.Is2D) { txtFitts2D.Text = String.Format("Fitts' Law - Bivariate for Subject {0}\r\n", _sdata.Subject) + String.Format("N = {0}\r\n", fm.N) + String.Format("MTavg = {0} ms\r\n", fm.MTe) + String.Format("Error% = {0}\r\n", fm.ErrorPct) + String.Format("MT(2d) = {0} + {1} * ID\r\n", fm.Fitts_a_2d, fm.Fitts_b_2d) + String.Format("TP_avg(2d) = {0} bits/s\r\n", fm.Fitts_TP_avg_2d) + String.Format("TP_inv(2d) = {0} bits/s\r\n", fm.Fitts_TP_inv_2d) + String.Format("a(2d) = {0} ms\r\n", fm.Fitts_a_2d) + String.Format("b(2d) = {0} ms/bit\r\n", fm.Fitts_b_2d) + String.Format("r(2d) = {0}\r\n", fm.Fitts_r_2d); } else { tabModel.TabPages.RemoveAt(1); // remove the Fitts' law 2D tab } tabModel.TabPages.RemoveAt(tabModel.TabPages.Count - 1); // remove the Error model 2D tab tabModel.TabPages.RemoveAt(tabModel.TabPages.Count - 1); // remove the Error model 1D tab } else // metronome experiment { // // Add the diagonal line for easy visual model comparisons. // Graph.Series s0 = new Graph.Series("Diagonal", Color.LightGray, Color.LightGray, false, true); s0.AddPoint(0f, 0f); s0.AddPoint(1f, 1f); grfErrors1D.AddSeries(s0); grfErrors2D.AddSeries(s0); // // Add the (predicted error rate, observed error rate) points for plotting. // Graph.Series s1 = new Graph.Series("Error model 1D points", Color.Tomato, Color.Tomato, true, false); s1.AddPoints(fm.ErrorPts_1d); grfErrors1D.AddSeries(s1); if (s1.NumPoints > 1) { RectangleF r = s1.DomainRange; Graph.Series s3 = new Graph.Series("Error model 1D line", Color.OrangeRed, Color.OrangeRed, false, true); s3.AddPoint(0f, (float)fm.Error_b_1d); // y-intercept s3.AddPoint(r.Right, (float)(fm.Error_m_1d * r.Right + fm.Error_b_1d)); grfErrors1D.AddSeries(s3); } if (_sdata.Is2D) { Graph.Series s2 = new Graph.Series("Error model 2D points", Color.Tomato, Color.Tomato, true, false); s2.AddPoints(fm.ErrorPts_2d); grfErrors2D.AddSeries(s2); if (s2.NumPoints > 1) { RectangleF r = s2.DomainRange; Graph.Series s4 = new Graph.Series("Error model 2D line", Color.OrangeRed, Color.OrangeRed, false, true); s4.AddPoint(0f, (float)fm.Error_b_2d); // y-intercept s4.AddPoint(r.Right, (float)(fm.Error_m_2d * r.Right + fm.Error_b_2d)); grfErrors2D.AddSeries(s4); } } // // Fill the text box with the Error model parameters. // txtErrors1D.Text = String.Format("Pointing Error Model - Univariate for Subject {0}\r\n", _sdata.Subject) + String.Format("N = {0}\r\n", fm.N) + String.Format("MTavg = {0} ms\r\n", fm.MTe) + String.Format("Error% = {0}\r\n", fm.ErrorPct) + String.Format("Predicted(1d) = 1 - erf[(2.066 * W/A * (2^((MT-{0})/{1}) - 1)) / sqrt(2)]\r\n", fm.Fitts_a_1d, fm.Fitts_b_1d) + String.Format("Observed(1d) = {0} * Predicted + {1}\r\n", fm.Error_m_1d, fm.Error_b_1d) + String.Format("m(1d) = {0}\r\n", fm.Error_m_1d) + String.Format("b(1d) = {0}\r\n", fm.Error_b_1d) + String.Format("r(1d) = {0}\r\n", fm.Error_r_1d); if (_sdata.Is2D) { txtErrors2D.Text = String.Format("Pointing Error Model - Bivariate for Subject {0}\r\n", _sdata.Subject) + String.Format("N = {0}\r\n", fm.N) + String.Format("MTavg = {0} ms\r\n", fm.MTe) + String.Format("Error% = {0}\r\n", fm.ErrorPct) + String.Format("Predicted(2d) = 1 - erf[(2.066 * W/A * (2^((MT-{0})/{1}) - 1)) / sqrt(2)]\r\n", fm.Fitts_a_2d, fm.Fitts_b_2d) + String.Format("Observed(2d) = {0} * Predicted + {1}\r\n", fm.Error_m_2d, fm.Error_b_2d) + String.Format("m(2d) = {0}\r\n", fm.Error_m_2d) + String.Format("b(2d) = {0}\r\n", fm.Error_b_2d) + String.Format("r(2d) = {0}\r\n", fm.Error_r_2d); } else { tabModel.TabPages.RemoveAt(3); // remove the Error model 2D tab } tabModel.TabPages.RemoveAt(0); // remove the Fitts' law 1D tab tabModel.TabPages.RemoveAt(0); // remove the Fitts' law 2D tab } }