// rotate the points by the given radians about their centroid public static List <PointR> RotateByRadians(List <PointR> points, double radians) { List <PointR> newPoints = new List <PointR>(points.Count); PointR c = Centroid(points); double cos = Math.Cos(radians); double sin = Math.Sin(radians); double cx = c.X; double cy = c.Y; for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; double dx = p.X - cx; double dy = p.Y - cy; PointR q = PointR.Empty; q.X = dx * cos - dy * sin + cx; q.Y = dx * sin + dy * cos + cy; newPoints.Add(q); } return(newPoints); }
// scales the points so that they form the size given. does not restore the // origin of the box. // adapted from ScaleTo() to smartly handle 1D scaling -- ie, if a gesture is 1D, // scale to the longer dimension only, or we warp our gestures too much // Lisa 12/28/2007 public static List <PointR> ScaleTo1D(List <PointR> points, SizeR sz) { List <PointR> newPoints = new List <PointR>(points.Count); RectangleR r = FindBox(points); // scale both by same factor double scaleFactor = 0.0; if (r.Width > r.Height) { scaleFactor = r.Width; } else { scaleFactor = r.Height; } for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; if (r.Width != 0d) { p.X *= (sz.Width / scaleFactor); } if (r.Height != 0d) { p.Y *= (sz.Height / scaleFactor); } newPoints.Add(p); } return(newPoints); }
// determines the angle, in radians, between two points. the angle is defined // by the circle centered on the start point with a radius to the end point, // where 0 radians is straight right from start (+x-axis) and PI/2 radians is // straight down (+y-axis). public static double AngleInRadians(PointR start, PointR end, bool positiveOnly) { double radians = 0.0; if (start.X != end.X) { radians = Math.Atan2(end.Y - start.Y, end.X - start.X); } else // pure vertical movement { if (end.Y < start.Y) { radians = -Math.PI / 2.0; // -90 degrees is straight up } else if (end.Y > start.Y) { radians = Math.PI / 2.0; // 90 degrees is straight down } } if (positiveOnly && radians < 0.0) { radians += Math.PI * 2.0; } return(radians); }
// copy constructor public PointR(PointR p) { X = p.X; Y = p.Y; T = p.T; P = p.P; }
// new scaling methods rewritten by Lisa as of 8/9/2009 from input from Jake public static List <PointR> Scale(List <PointR> pts, double oneDRatio, SizeR size) // scales the oriented bbox based on 1D or 2D { if (NDollarParameters.Instance.UseUniformScaling) // scale to a uniform circle { // do new thing PointR centroid = Utils.Centroid(pts); double radiusSquared = 1.0d; foreach (PointR point in pts) { double distanceSquared = Math.Pow((centroid.X - point.X), 2.0) + Math.Pow((centroid.Y - point.Y), 2.0); if (distanceSquared > radiusSquared) { radiusSquared = distanceSquared; } } double factor = size.Width / Math.Sqrt(radiusSquared);//Assume that size is a square and arbitrarily select width //this could also be replaced with a constant value (250?) List <PointR> scaledPts = new List <PointR>(); for (int i = 0; i < pts.Count; i++) { PointR p = new PointR((PointR)pts[i]); p.X *= factor; p.Y *= factor; scaledPts.Add(p); } return(scaledPts); } else // do old thing { return(Utils.ScaleByDimension(pts, oneDRatio, size)); } }
public static double Distance(PointR p1, PointR p2) { double dx = p2.X - p1.X; double dy = p2.Y - p1.Y; return(Math.Sqrt(dx * dx + dy * dy)); }
// Rotate a point 'p' around a point 'c' by the given radians. // Rotation (around the origin) amounts to a 2x2 matrix of the form: // // [ cos A -sin A ] [ p.x ] // [ sin A cos A ] [ p.y ] // // Note that the C# Math coordinate system has +x-axis stright right and // +y-axis straight down. Rotation is clockwise such that from +x-axis to // +y-axis is +90 degrees, from +x-axis to -x-axis is +180 degrees, and // from +x-axis to -y-axis is -90 degrees. public static PointR RotatePoint(PointR p, PointR c, double radians) { PointR q = PointR.Empty; q.X = (p.X - c.X) * Math.Cos(radians) - (p.Y - c.Y) * Math.Sin(radians) + c.X; q.Y = (p.X - c.X) * Math.Sin(radians) + (p.Y - c.Y) * Math.Cos(radians) + c.Y; return(q); }
public override bool Equals(object obj) { if (obj is PointR) { PointR p = (PointR)obj; return(X == p.X && Y == p.Y); } return(false); }
// scales by the percentages contained in the 'sz' parameter. values of 1.0 would result in the // identity scale (that is, no change). public static List <PointR> ScaleBy(List <PointR> points, SizeR sz) { List <PointR> newPoints = new List <PointR>(points.Count); for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; p.X *= sz.Width; p.Y *= sz.Height; newPoints.Add(p); } return(newPoints); }
// translates the points so that their centroid lies at 'toPt' public static List <PointR> TranslateCentroidTo(List <PointR> points, PointR toPt) { List <PointR> newPoints = new List <PointR>(points.Count); PointR centroid = Centroid(points); for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; p.X += (toPt.X - centroid.X); p.Y += (toPt.Y - centroid.Y); newPoints.Add(p); } return(newPoints); }
// Calculate the angle from the initial point of the array of Points (points[0]) // to points[index]. // // Returns this angle represented by a unit vector (stored in a Point). // // **This is used in Gesture.cs:Gesture() to compute the start angle in support // of the optimization to not compare candidates to templates whose start angles // are widely different. Lisa 8/8/2009 public static PointR CalcStartUnitVector(List <PointR> points, int index) { // v is the vector from points[0] to points[index] PointR v = new PointR(((PointR)points[index]).X - ((PointR)points[0]).X, ((PointR)points[index]).Y - ((PointR)points[0]).Y); // len is the length of vector v double len = Math.Sqrt(v.X * v.X + v.Y * v.Y); // the unit vector representing the angle between points[0] and points[index] // is the vector v divided by its length len // TODO: does there need to be a divide by zero check? return(new PointR(v.X / len, v.Y / len)); }
// translates the points so that the upper-left corner of their bounding box lies at 'toPt' public static List <PointR> TranslateBBoxTo(List <PointR> points, PointR toPt) { List <PointR> newPoints = new List <PointR>(points.Count); RectangleR r = Utils.FindBox(points); for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; p.X += (toPt.X - r.X); p.Y += (toPt.Y - r.Y); newPoints.Add(p); } return(newPoints); }
// scales the points so that the length of their shorter side // matches the length of the shorter side of the given box. // thus, both dimensions are warped proportionally, rather than // independently, like in the function ScaleTo. public static List <PointR> ScaleToMin(List <PointR> points, RectangleR box) { List <PointR> newPoints = new List <PointR>(points.Count); RectangleR r = FindBox(points); for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; p.X *= (box.MinSide / r.MinSide); p.Y *= (box.MinSide / r.MinSide); newPoints.Add(p); } return(newPoints); }
// will return result in radians public static double AngleBetweenUnitVectors(PointR v1, PointR v2) // gives acute angle between unit vectors from (0,0) to v1, and (0,0) to v2 { // changed this method on 9/28/2009, Lisa double test = v1.X * v2.X + v1.Y * v2.Y; // arc cosine of the vector dot product // sometimes these two cases can happen because of rounding error in the dot product calculation if (test < -1.0) { test = -1.0; // truncate rounding errors } if (test > 1.0) { test = 1.0; // truncate rounding errors } return(Math.Acos(test)); }
// scales the points so that they form the size given. does not restore the // origin of the box. public static List <PointR> ScaleTo(List <PointR> points, SizeR sz) { List <PointR> newPoints = new List <PointR>(points.Count); RectangleR r = FindBox(points); for (int i = 0; i < points.Count; i++) { PointR p = (PointR)points[i]; if (r.Width != 0d) { p.X *= (sz.Width / r.Width); } if (r.Height != 0d) { p.Y *= (sz.Height / r.Height); } newPoints.Add(p); } return(newPoints); }
public static List <PointR> Resample(List <PointR> points, int n) { double I = PathLength(points) / (n - 1); // interval length double D = 0.0; List <PointR> srcPts = new List <PointR>(points); List <PointR> dstPts = new List <PointR>(n); dstPts.Add(srcPts[0]); for (int i = 1; i < srcPts.Count; i++) { PointR pt1 = (PointR)srcPts[i - 1]; PointR pt2 = (PointR)srcPts[i]; double d = Distance(pt1, pt2); if ((D + d) >= I) { double qx = pt1.X + ((I - D) / d) * (pt2.X - pt1.X); double qy = pt1.Y + ((I - D) / d) * (pt2.Y - pt1.Y); PointR q = new PointR(qx, qy); dstPts.Add(q); // append new point 'q' srcPts.Insert(i, q); // insert 'q' at position i in points s.t. 'q' will be the next i D = 0.0; } else { D += d; } } // somtimes we fall a rounding-error short of adding the last point, so add it if so if (dstPts.Count == n - 1) { dstPts.Add(srcPts[srcPts.Count - 1]); } return(dstPts); }
public Gesture(List <PointR> points) { this.Name = String.Empty; this.RawPoints = new List <PointR>(points); // copy (saved for drawing) Points = RawPoints; // added for debugging, Lisa 1/2/2008 // reflects new order of pre-processing steps as of 8/31/2009 // old order (from 8/8/2009) was // 1. check for 1D // 2. scale // 3. resample // 4. translate // 5. rotate (if rot-invariant) // 6. calculate start angle // new order (as of 8/31/2009) is // 1. resample // 2. rotate (all); save amount // 3. check for 1D // 4. scale // 5. rotate back (if NOT rot-invariant) // 6. translate // 7. calculate start angle // first, resample (influences calculation of centroid) Points = Utils.Resample(Points, NDollarParameters.Instance.NumResamplePoints); // then, if we are rotation-invariant, rotate to a common reference angle // otherwise skip that step // Lisa 8/8/2009: this is now set by a flag in the NDollarRecognizer.cs file with all the other flags // rotate so that the centroid-to-1st-point is at zero degrees double radians = Utils.AngleInRadians(Utils.Centroid(Points), (PointR)Points[0], false); Points = Utils.RotateByRadians(Points, -radians); // then, resize to a square // check for 1D vs 2D (because we resize differently) // Lisa 1/2/2008 // replace with boolean, Lisa 8/9/2009 this.Is1D = Utils.Is1DGesture(RawPoints); // scale to a common (square) dimension // moved determination of scale method to within the scale() method for less branching here // Lisa 8/9/2009 Points = Utils.Scale(Points, GeometricRecognizer._1DThreshold, GeometricRecognizer.ResampleScale); // next, if NOT rotation-invariant, rotate back if (!NDollarParameters.Instance.RotationInvariant) { Points = Utils.RotateByRadians(Points, +radians); // undo angle } // next, translate to a common origin Points = Utils.TranslateCentroidTo(Points, GeometricRecognizer.ResampleOrigin); // finally, save the start angle // Lisa 8/8/2009 // store the start unit vector after post-processing steps this.StartUnitVector = Utils.CalcStartUnitVector(Points, NDollarParameters.Instance.StartAngleIndex); // Lisa 3/7/2011 // make the simple vector-based version for Protractor testing this.VectorVersion = Vectorize(this.Points); }
// determines the angle, in degrees, between two points. the angle is defined // by the circle centered on the start point with a radius to the end point, // where 0 degrees is straight right from start (+x-axis) and 90 degrees is // straight down (+y-axis). public static double AngleInDegrees(PointR start, PointR end, bool positiveOnly) { double radians = AngleInRadians(start, end, positiveOnly); return(Rad2Deg(radians)); }