/// <summary>
        /// Calculates the six path analyses defined by MacKenzie et al. (CHI 2001) using
        /// the task axis given. The smoothed position is used to avoid counting minor
        /// artifacts that occur in the raw or resampled position paths.
        /// </summary>
        /// <param name="axisStart">The starting point of the task axis.</param>
        /// <param name="axisEnd">The ending point of the task axis.</param>
        /// <returns></returns>
        public PathAnalyses DoPathAnalyses(PointF axisStart, PointF axisEnd)
        {
            if (_moves.Count == 0)
            {
                return(PathAnalyses.Empty);
            }

            // get the smoothed position profile and covert it to PointFs (we don't need time for path analyses)
            Profiles      smoothed = CreateSmoothedProfiles();
            List <PointF> pts      = TimePointF.ConvertList(smoothed.Position);

            // rotate the task so that it proceeds at 0 degrees (+x axis, straight right)
            PointF c       = GeotrigEx.Centroid(pts);
            double radians = GeotrigEx.Angle(axisStart, axisEnd, true);

            axisStart = GeotrigEx.RotatePoint(axisStart, c, -radians);
            axisEnd   = GeotrigEx.RotatePoint(axisEnd, c, -radians);
            List <PointF> rotatedPts = GeotrigEx.RotatePoints(pts, -radians);

            // perform the path analysis computations for each path analysis measure
            PathAnalyses analyses;

            analyses.TaskAxisCrossings          = TaskAxisCrossings(rotatedPts, axisEnd.Y);
            analyses.MovementDirectionChanges   = MovementDirectionChanges(rotatedPts);
            analyses.OrthogonalDirectionChanges = OrthogonalDirectionChanges(rotatedPts);
            analyses.MovementVariability        = MovementVariability(rotatedPts, axisEnd.Y);
            analyses.MovementError  = MovementError(rotatedPts, axisEnd.Y);
            analyses.MovementOffset = MovementOffset(rotatedPts, axisEnd.Y);
            return(analyses);
        }
Example #2
0
        public List<double> Vector;        // vector representation -- for Protractor

        /// <summary>
        /// Constructor of a unistroke gesture. A unistroke is comprised of a set of points drawn
        /// out over time in a sequence.
        /// </summary>
        /// <param name="name">The name of the unistroke gesture.</param>
        /// <param name="timepoints">The array of points supplied for this unistroke.</param>
        public Unistroke(string name, List<TimePointF> timepoints)
        {
            this.Name = name;
            this.RawPoints = new List<TimePointF>(timepoints); // copy (saved for drawing)
            double I = GeotrigEx.PathLength(timepoints) / (Recognizer.NumPoints - 1); // interval distance between points
            this.Points = TimePointF.ConvertList(SeriesEx.ResampleInSpace(timepoints, I));
            double radians = GeotrigEx.Angle(GeotrigEx.Centroid(this.Points), this.Points[0], false);
            this.Points = GeotrigEx.RotatePoints(this.Points, -radians);
            this.Points = GeotrigEx.ScaleTo(this.Points, Recognizer.SquareSize);
            this.Points = GeotrigEx.TranslateTo(this.Points, Recognizer.Origin, true);
            this.Vector = Vectorize(this.Points); // vectorize resampled points (for Protractor)
        }
Example #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="timepoints"></param>
        /// <param name="protractor">false</param>
        /// <returns></returns>
        public NBestList Recognize(List <TimePointF> timepoints, bool protractor)       // candidate points
        {
            double        I       = GeotrigEx.PathLength(timepoints) / (NumPoints - 1); // interval distance between points
            List <PointF> points  = TimePointF.ConvertList(SeriesEx.ResampleInSpace(timepoints, I));
            double        radians = GeotrigEx.Angle(GeotrigEx.Centroid(points), points[0], false);

            points = GeotrigEx.RotatePoints(points, -radians);
            points = GeotrigEx.ScaleTo(points, SquareSize);
            points = GeotrigEx.TranslateTo(points, Origin, true);
            List <double> vector = Unistroke.Vectorize(points); // candidate's vector representation

            NBestList nbest = new NBestList();

            foreach (Unistroke u in _gestures.Values)
            {
                if (protractor) // Protractor extension by Yang Li (CHI 2010)
                {
                    double[] best  = OptimalCosineDistance(u.Vector, vector);
                    double   score = 1.0 / best[0];
                    nbest.AddResult(u.Name, score, best[0], best[1]); // name, score, distance, angle
                }
                else // original $1 angular invariance search -- Golden Section Search (GSS)
                {
                    double[] best = GoldenSectionSearch(
                        points,                                 // to rotate
                        u.Points,                               // to match
                        GeotrigEx.Degrees2Radians(-45.0),       // lbound
                        GeotrigEx.Degrees2Radians(+45.0),       // ubound
                        GeotrigEx.Degrees2Radians(2.0)          // threshold
                        );

                    double score = 1.0 - best[0] / HalfDiagonal;
                    nbest.AddResult(u.Name, score, best[0], best[1]); // name, score, distance, angle
                }
            }
            nbest.SortDescending(); // sort descending by score so that nbest[0] is best result
            return(nbest);
        }