Beispiel #1
0
        /// <summary>
        /// Shows all the information for a session-level node highlighted in the treeview.
        /// </summary>
        /// <param name="node">The highlighted node in the left treeview, assumed to represent a single session.</param>
        private void ShowSessionInfo(TreeNode node)
        {
            //
            // Get the session data. Note that sdata should be the same instance as _sdata.
            // For consistency, we pull it from the node's tag.
            //
            SessionData sdata = (SessionData)node.Tag;

            //
            // Set up a reusable XML writer.
            //
            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;
                sdata.WriteXmlHeader(writer);

                //
                // Iterate through the conditions within the session.
                //
                foreach (TreeNode bnode in node.Nodes)
                {
                    foreach (TreeNode cnode in bnode.Nodes)
                    {
                        ConditionData cd = (ConditionData)cnode.Tag;
                        cd.WriteXmlHeader(writer); // writes out trials as well
                    }
                }
                sdata.WriteXmlFooter(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);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Shows all the information for a block-level node highlighted in the treeview.
        /// </summary>
        /// <param name="node">The highlighted node in the left treeview, assumed to represent a single block.</param>
        private void ShowBlockInfo(TreeNode node)
        {
            //
            // Set up a reusable XML writer.
            //
            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;
                writer.WriteStartElement("Block");
                writer.WriteAttributeString("index", XmlConvert.ToString(((ConditionData)node.Nodes[0].Tag).Block));

                //
                // Blocks themselves are not data objects, they're just identifiers on conditions.
                // So iterate through the block's children nodes and write them out, to be later merged.
                //
                foreach (TreeNode cnode in node.Nodes)
                {
                    ConditionData cd = (ConditionData)cnode.Tag;
                    cd.WriteXmlHeader(writer);
                }

                writer.WriteEndElement(); // </Block>
            }
            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);
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Shows all the information for a condition-level node highlighted in the treeview.
        /// </summary>
        /// <param name="node">The highlighted node in the left treeview, assumed to represent a single condition.</param>
        private void ShowConditionInfo(TreeNode node)
        {
            //
            // Get the condition from the 'Tag' field from the selected node. We don't
            // set the member variable because we're leaving the most recent trial depicted
            // on the graphs, etc., and just loading the XML for the condition.
            //
            ConditionData cd = (ConditionData)node.Tag;

            //
            // Set up a reusable XML writer.
            //
            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;
                cd.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);
                }
            }
        }
Beispiel #4
0
        /// <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();
        }
Beispiel #5
0
        /// <summary>
        /// Build the tree view of the session, blocks, conditions, and trials on the left. Use icons
        /// to depict each, and show valuable information in each string label.
        /// </summary>
        /// <param name="sender">The sender of this event.</param>
        /// <param name="e">The arguments for this event.</param>
        private void GraphForm_Load(object sender, EventArgs e)
        {
            string s;

            trvTrials.SuspendLayout();

            s = String.Format("Subject {0} - {1}D, {2}", _sdata.Subject, _sdata.Is2D ? 2 : 1, _sdata.UsedMetronome ? "with metronome" : "no metronome");
            TreeNode sNode = new TreeNode(s);

            sNode.Tag                = _sdata;
            sNode.ImageIndex         = SessionIcon;
            sNode.SelectedImageIndex = SessionIcon;
            trvTrials.Nodes.Add(sNode);

            for (int b = 0; b < _sdata.NumBlocks; b++)
            {
                s = String.Format("Block {0}", b);
                TreeNode bNode = new TreeNode(s);
                bNode.ImageIndex         = BlockIcon;
                bNode.SelectedImageIndex = BlockIcon;
                sNode.Nodes.Add(bNode);

                for (int i = 0; i < _sdata.NumConditionsPerBlock; i++)
                {
                    ConditionData cdata = _sdata[b, i];
                    if (cdata.MTPct != -1.0)
                    {
                        s = String.Format("Condition {0} - {1}×{2}×{3}", cdata.Index, cdata.MTPct, cdata.A, cdata.W);
                    }
                    else
                    {
                        s = String.Format("Condition {0} - {1}×{2}", cdata.Index, cdata.A, cdata.W);
                    }
                    TreeNode cNode = new TreeNode(s);
                    cNode.Tag                = cdata;
                    cNode.ImageIndex         = ConditionIcon;
                    cNode.SelectedImageIndex = ConditionIcon;
                    bNode.Nodes.Add(cNode);

                    for (int j = 1; j <= cdata.NumTrials; j++)
                    {
                        TrialData tdata = cdata[j];
                        s = String.Format("Trial {0}", tdata.Number);
                        TreeNode tNode = new TreeNode(s);
                        tNode.Tag = tdata;
                        if (tdata.IsPractice)
                        {
                            tNode.ImageIndex         = TrialPracticeIcon;
                            tNode.SelectedImageIndex = TrialPracticeIcon;
                        }
                        else if (tdata.IsSpatialOutlier || tdata.IsTemporalOutlier)
                        {
                            tNode.ImageIndex         = TrialOutlierIcon;
                            tNode.SelectedImageIndex = TrialOutlierIcon;
                        }
                        else if (tdata.IsError)
                        {
                            tNode.ImageIndex         = TrialErrorIcon;
                            tNode.SelectedImageIndex = TrialErrorIcon;
                        }
                        else
                        {
                            tNode.ImageIndex         = TrialGoodIcon;
                            tNode.SelectedImageIndex = TrialGoodIcon;
                        }

                        cNode.Nodes.Add(tNode);
                    }
                }
            }

            sNode.Nodes[0].Nodes[0].Nodes[0].EnsureVisible();          // ensure first trial node is visible
            trvTrials.SelectedNode = sNode.Nodes[0].Nodes[0].Nodes[0]; // select first trial
            trvTrials.ResumeLayout(true);
        }
        /// <summary>
        /// Constructs a FittsSession instance. A FittsSession contains conditions for a Fitts' law study,
        /// which, in turn, contain a set of trials. A constructed instance contains a list of conditions
        /// in sequence, which themselves contain a list of trials.
        /// </summary>
        /// <param name="o">The options that configure this session obtained from the OptionsForm dialog.</param>
        public SessionData(OptionsForm.Options o)
        {
            //
            // Set the condition variables that define this test.
            //
            _subject    = o.Subject;
            _circular   = o.Is2D;
            _a          = o.A;
            _w          = o.W;
            _mtpct      = o.MTPct;
            _intercept  = o.Intercept;
            _slope      = o.Slope;
            _screen     = Screen.PrimaryScreen.Bounds;
            _conditions = new List <ConditionData>();

            //
            // Create the order of conditions. Nesting is mt%[a[w]]].
            //
            int[] a_order, w_order, mt_order;
            if (o.Randomize) // randomize the order of conditions
            {
                a_order = RandomEx.Array(0, o.A.Length - 1, o.A.Length, true);
                w_order = RandomEx.Array(0, o.W.Length - 1, o.W.Length, true);
                if (o.MTPct != null)
                {
                    mt_order = RandomEx.Array(0, o.MTPct.Length - 1, o.MTPct.Length, true);
                    while (o.MTPct[mt_order[0]] < StatsEx.Mean(o.MTPct)) // enforce that the first MT% condition is >avg(MT%)
                    {
                        mt_order = RandomEx.Array(0, o.MTPct.Length - 1, o.MTPct.Length, true);
                    }
                }
                else
                {
                    mt_order = null;
                }
            }
            else // in-order arrays
            {
                a_order = new int[o.A.Length];
                for (int i = 0; i < o.A.Length; i++)
                {
                    a_order[i] = i;
                }
                w_order = new int[o.W.Length];
                for (int i = 0; i < o.W.Length; i++)
                {
                    w_order[i] = i;
                }
                mt_order = (o.MTPct != null) ? new int[o.MTPct.Length] : null;
                for (int i = 0; o.MTPct != null && i < o.MTPct.Length; i++)
                {
                    mt_order[i] = i;
                }
            }

            //
            // Create the ordered condition list for the first block of all conditions (i.e., one time through).
            //
            int n = 0;

            for (int i = 0; o.MTPct == null || i < o.MTPct.Length; i++)
            {
                for (int j = 0; j < o.A.Length; j++)
                {
                    for (int k = 0; k < o.W.Length; k++)
                    {
                        double        pct   = (o.MTPct != null) ? o.MTPct[mt_order[i]] : -1.0; // MT%
                        double        fitts = o.Intercept + o.Slope * Math.Log((double)o.A[a_order[j]] / o.W[w_order[k]] + 1.0, 2.0);
                        long          mt    = (o.MTPct != null) ? (long)fitts : -1L;           // Fitts' law predicted movement time
                        ConditionData cd    = new ConditionData(0, n++, o.A[a_order[j]], o.W[w_order[k]], pct, mt, o.Trials, o.Practice, o.Is2D);
                        _conditions.Add(cd);
                    }
                }
                if (o.MTPct == null)
                {
                    break;
                }
            }

            //
            // Now possibly replicate the created order multiple times.
            //
            int nConditions = _conditions.Count; // number of conditions in one block

            for (int b = 1; b < o.Blocks; b++)
            {
                for (int c = 0; c < nConditions; c++)
                {
                    ConditionData fx = _conditions[c];
                    ConditionData fc = new ConditionData(b, c, fx.A, fx.W, fx.MTPct, fx.MTPred, o.Trials, o.Practice, o.Is2D);
                    _conditions.Add(fc);
                }
            }
        }
        /// <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);
        }
        /// <summary>
        /// Reads a data object from XML and returns an instance of the object.
        /// </summary>
        /// <param name="reader">An open XML reader. The reader will be closed by this
        /// method after reading.</param>
        /// <returns>Returns <b>true</b> if successful; <b>false</b> otherwise.</returns>
        /// <remarks>Clients should first create a new instance using a default constructor, and then
        /// call this method to populate the data fields of the default instance.</remarks>
        public bool ReadFromXml(XmlTextReader reader)
        {
            bool success = true;

            try
            {
                // start up the xml text reader and move to the xml header
                reader.WhitespaceHandling = WhitespaceHandling.None;
                if (!reader.IsStartElement("Fitts_Study")) // moves to content and tests top tag
                {
                    throw new XmlException("XML format error: Expected the <Fitts_Study> tag.");
                }

                // read the session-level information from the header
                _subject  = XmlConvert.ToInt32(reader.GetAttribute("subject"));
                _circular = XmlConvert.ToBoolean(reader.GetAttribute("circular"));
                int conditions = XmlConvert.ToInt32(reader.GetAttribute("conditions"));
                _mtpct     = StringEx.String2DoubleArray(reader.GetAttribute("MTPct"));
                _a         = StringEx.String2IntArray(reader.GetAttribute("A"));
                _w         = StringEx.String2IntArray(reader.GetAttribute("W"));
                _intercept = XmlConvert.ToDouble(reader.GetAttribute("intercept"));
                _slope     = XmlConvert.ToDouble(reader.GetAttribute("slope"));
                _screen    = StringEx.String2RectangleF(reader.GetAttribute("screen")); // saved for zoom feature

                // read the conditions and add condition objects to the session. the
                // conditions will be responsible for reading their individual trials.
                _conditions = new List <ConditionData>(conditions);
                for (int i = 0; i < conditions; i++)
                {
                    ConditionData fc = new ConditionData();
                    if (!fc.ReadFromXml(reader))
                    {
                        throw new XmlException("Failed to read the ConditionData.");
                    }
                    else
                    {
                        _conditions.Add(fc);
                    }
                }
                // here would be the place to read the Fitts_Model parameters, but the model
                // can be recreated from the session data anyway, so there's really no point.
            }
            catch (XmlException xex)
            {
                Console.WriteLine(xex);
                success = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                success = false;
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }
            return(success);
        }
        /// <summary>
        /// Performs linear regression on the entire session's worth of data to fit a Fitts' law model
        /// of the form MTe = a + b * log2(Ae/We + 1).
        /// </summary>
        /// <returns>Model and fitting parameters.</returns>
        public Model BuildModel()
        {
            Model model = Model.Empty;

            model.FittsPts_1d = new List <PointF>(_conditions.Count);
            model.FittsPts_2d = new List <PointF>(_conditions.Count);

            double[] ide = new double[_conditions.Count];   // observed index of difficulties for each condition
            double[] mte = new double[_conditions.Count];   // observed mean movement times for each condition
            double[] tp  = new double[_conditions.Count];   // observed throughputs for each condition

            for (int i = 0; i <= 1; i++)                    // first loop is univariate, second loop is bivariate
            {
                for (int j = 0; j < _conditions.Count; j++) // each A x W or MT% x A x W condition (blocks kept separate)
                {
                    ConditionData cdata = _conditions[j];
                    ide[j] = cdata.GetIDe(i == 1);                      // bits
                    mte[j] = cdata.GetMTe(ExcludeOutliersType.Spatial); // ms
                    tp[j]  = cdata.GetTP(i == 1);                       // bits/s
                    if (i == 0)
                    {
                        model.FittsPts_1d.Add(new PointF((float)ide[j], (float)mte[j]));   // for graphing later
                    }
                    else
                    {
                        model.FittsPts_2d.Add(new PointF((float)ide[j], (float)mte[j]));   // for graphing later
                    }
                }
                if (i == 0)                                                  // univariate
                {
                    model.N               = _conditions.Count;               // == model.FittsPts.Count
                    model.MTe             = StatsEx.Mean(mte);               // ms
                    model.Fitts_TP_avg_1d = StatsEx.Mean(tp);                // bits/s
                    model.Fitts_a_1d      = StatsEx.Intercept(ide, mte);     // ms
                    model.Fitts_b_1d      = StatsEx.Slope(ide, mte);         // ms/bit
                    model.Fitts_TP_inv_1d = 1.0 / model.Fitts_b_1d * 1000.0; // bits/s
                    model.Fitts_r_1d      = StatsEx.Pearson(ide, mte);       // correlation
                }
                else // bivariate
                {
                    model.Fitts_TP_avg_2d = _circular ? StatsEx.Mean(tp) : 0.0;                // bits/s
                    model.Fitts_a_2d      = _circular ? StatsEx.Intercept(ide, mte) : 0.0;     // ms
                    model.Fitts_b_2d      = _circular ? StatsEx.Slope(ide, mte) : 0.0;         // ms/bit
                    model.Fitts_TP_inv_2d = _circular ? 1.0 / model.Fitts_b_2d * 1000.0 : 0.0; // bits/s
                    model.Fitts_r_2d      = _circular ? StatsEx.Pearson(ide, mte) : 0.0;       // correlation
                }
            }

            //
            // Now compute the predicted error rates relevant to metronome experiments.
            //
            model.ErrorPts_1d = new List <PointF>(_conditions.Count);
            model.ErrorPts_2d = new List <PointF>(_conditions.Count);

            double[] errPred = new double[_conditions.Count]; // x, predicted errors based on the error model for pointing
            double[] errObs  = new double[_conditions.Count]; // y, observed error rates for each condition (spatial outliers included)

            for (int i = 0; i <= 1; i++)                      // first loop is univariate, second loop is bivariate
            {
                for (int j = 0; j < _conditions.Count; j++)   // each MT% x A x W condition, duplicates left separate
                {
                    ConditionData cdata = _conditions[j];
                    errObs[j] = cdata.GetErrorRate(ExcludeOutliersType.Temporal); // observed error rates -- temporal outliers excluded
                    double mt = cdata.GetMTe(ExcludeOutliersType.Temporal);       // observed movement times -- temporal outliers excluded

                    double z = (i == 0)
                        ? 2.066 * ((double)cdata.W / cdata.A) * (Math.Pow(2.0, (mt - model.Fitts_a_1d) / model.Fitts_b_1d) - 1.0)
                        : 2.066 * ((double)cdata.W / cdata.A) * (Math.Pow(2.0, (mt - model.Fitts_a_2d) / model.Fitts_b_2d) - 1.0);

                    errPred[j] = GeotrigEx.PinValue(2.0 * (1.0 - StatsEx.CDF(0.0, 1.0, z)), 0.0, 1.0); // predicted error rates == 1 - erf(z / sqrt(2))
                    //double shah = 1.0 - (2.0 * StatsEx.ShahNormal(z)); // Shah approximation of the standard normal distribution

                    if (i == 0)
                    {
                        model.ErrorPts_1d.Add(new PointF((float)errPred[j], (float)errObs[j]));   // for graphing later
                    }
                    else
                    {
                        model.ErrorPts_2d.Add(new PointF((float)errPred[j], (float)errObs[j]));   // for graphing later
                    }
                }
                if (i == 0)                                  // univariate
                {
                    model.ErrorPct   = StatsEx.Mean(errObs); // percent
                    model.Error_m_1d = StatsEx.Slope(errPred, errObs);
                    model.Error_b_1d = StatsEx.Intercept(errPred, errObs);
                    model.Error_r_1d = StatsEx.Pearson(errPred, errObs);
                }
                else // bivariate
                {
                    model.Error_m_2d = _circular ? StatsEx.Slope(errPred, errObs) : 0.0;
                    model.Error_b_2d = _circular ? StatsEx.Intercept(errPred, errObs) : 0.0;
                    model.Error_r_2d = _circular ? StatsEx.Pearson(errPred, errObs) : 0.0;
                }
            }
            return(model);
        }