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