/// <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);
        }