/// <remarks> /// Calculates the direction in radians between this point and the given point. /// 0 is to the right, PI / 2 is up, etc. /// /// @param comparePoint the point to get the direction to from this point /// @return the direction in radians between this point and the given point. /// </remarks> public double GetDirection(WrittenPoint comparePoint) { double dx = X - comparePoint.X; double dy = Y - comparePoint.Y; return(Math.PI - Math.Atan2(dy, dx)); }
/// <remarks> /// Calculates the direction in radians between this point and the given point. /// 0 is to the right, PI / 2 is up, etc. /// /// @param comparePoint the point to get the direction to from this point /// @return the direction in radians between this point and the given point. /// </remarks> public double GetDirection(WrittenPoint comparePoint) { double dx = X - comparePoint.X; double dy = Y - comparePoint.Y; return Math.PI - Math.Atan2(dy, dx); }
public double Distance(WrittenPoint comparePoint) { double dx = X - comparePoint.X; double dy = Y - comparePoint.Y; return(Math.Sqrt(dx * dx + dy * dy)); }
public List <SubStrokeDescriptor> GetSubStrokes(double charWidth, double charHeight) { if (!this.isAnalyzed) { this.AnalyzeAndMark(); } List <SubStrokeDescriptor> subStrokes = new List <SubStrokeDescriptor>(); // Any WrittenStroke should have at least two points, (a single point cannot constitute a Stroke). // We should therefore be safe calling an iterator without checking for the first point. WrittenPoint previousPoint = pointList[0]; for (int i = 1; i != pointList.Count; ++i) { WrittenPoint nextPoint = pointList[i]; if (nextPoint.IsPivot) { // The direction from each previous point to each successive point, in radians. double direction = previousPoint.GetDirection(nextPoint); // Use the normalized length, to account for relative character size. double normalizedLength = previousPoint.GetDistanceNormalized(nextPoint, charWidth, charHeight); SubStrokeDescriptor subStroke = new SubStrokeDescriptor(direction, normalizedLength); subStrokes.Add(subStroke); previousPoint = nextPoint; } } return(subStrokes); }
public void AddPoint(WrittenPoint point, ref double charLeftX, ref double charRightX, ref double charTopY, ref double charBottomY) { int pointX = point.X; int pointY = point.Y; // Expand the bounding box coordinates for this WrittenCharacter in necessary. charLeftX = Math.Min(pointX, charLeftX); charRightX = Math.Max(pointX, charRightX); charTopY = Math.Min(pointY, charTopY); charBottomY = Math.Max(pointY, charBottomY); this.pointList.Add(point); }
/// <remarks> /// Normalized length takes into account the size of the WrittenCharacter on the canvas. /// For example, if the WrittenCharacter was written small in the upper left portion of the canvas, /// then the lengths not be based on the full size of the canvas, but rather only on the relative /// size of the WrittenCharacter. /// /// @param comparePoint the point to get the normalized distance to from this point /// /// @return the normalized length from this point to the compare point /// </remarks> public double GetDistanceNormalized(WrittenPoint comparePoint, double charWidth, double charHeight) { double width = (double)charWidth; double height = (double)charHeight; // normalizer is a diagonal along a square with sides of size the larger dimension of the bounding box double dimensionSquared = width > height ? width * width : height * height; double normalizer = Math.Sqrt(dimensionSquared + dimensionSquared); double distanceNormalized = Distance(comparePoint) / normalizer; // shouldn't be longer than 1 if it's normalized distanceNormalized = Math.Min(distanceNormalized, 1.0); return(distanceNormalized); }
/// <remarks> /// Normalized length takes into account the size of the WrittenCharacter on the canvas. /// For example, if the WrittenCharacter was written small in the upper left portion of the canvas, /// then the lengths not be based on the full size of the canvas, but rather only on the relative /// size of the WrittenCharacter. /// /// @param comparePoint the point to get the normalized distance to from this point /// /// @return the normalized length from this point to the compare point /// </remarks> public double GetDistanceNormalized(WrittenPoint comparePoint, double charWidth, double charHeight) { double width = (double)charWidth; double height = (double)charHeight; // normalizer is a diagonal along a square with sides of size the larger dimension of the bounding box double dimensionSquared = width > height ? width * width : height * height; double normalizer = Math.Sqrt(dimensionSquared + dimensionSquared); double distanceNormalized = Distance(comparePoint) / normalizer; // shouldn't be longer than 1 if it's normalized distanceNormalized = Math.Min(distanceNormalized, 1.0); return distanceNormalized; }
/// <summary> /// Start recognizing a character in a BG thread after strokes have changed. /// </summary> private void startNewCharRecog(IEnumerable<WritingPad.Stroke> strokes) { // If there are other matchers running, stop them now lock (runningMatchers) { foreach (StrokesMatcher sm in runningMatchers) sm.Stop(); } // Convert stroke data to HanziLookup's format WrittenCharacter wc = new WrittenCharacter(); foreach (WritingPad.Stroke stroke in strokes) { WrittenStroke ws = new WrittenStroke(); foreach (PointF p in stroke.Points) { WrittenPoint wp = new WrittenPoint((int)(p.X), (int)(p.Y)); ws.AddPoint(wp, ref wc.LeftX, ref wc.RightX, ref wc.TopY, ref wc.BottomY); } wc.AddStroke(ws); } if (wc.StrokeList.Count == 0) { // Don't bother doing anything if nothing has been input yet (number of strokes == 0). ctrlCharPicker.SetItems(null); return; } ThreadPool.QueueUserWorkItem(recognize, wc); }
public double Distance(WrittenPoint comparePoint) { double dx = X - comparePoint.X; double dy = Y - comparePoint.Y; return Math.Sqrt(dx * dx + dy * dy); }
/// <summary> /// Analyzes the given WrittenStroke and marks its constituent WrittenPoints to demarcate the SubStrokes. /// Points that demarcate between the SubStroke segments are marked as pivot points. /// These pivot points can later be used to build up a List of SubStroke objects. /// </summary> public void AnalyzeAndMark() { var pointIter = pointList.GetEnumerator(); // It should be impossible for a stroke to have < 2 points, so we are safe calling next() twice. pointIter.MoveNext(); WrittenPoint firstPoint = pointIter.Current; WrittenPoint previousPoint = firstPoint; pointIter.MoveNext(); WrittenPoint pivotPoint = pointIter.Current; // The first point of a Stroke is always a pivot point. firstPoint.IsPivot = true; int subStrokeIndex = 1; // The first point and the next point are always part of the first SubStroke. firstPoint.SubStrokeIndex = subStrokeIndex; pivotPoint.SubStrokeIndex = subStrokeIndex; // localLength keeps track of the immediate distance between the latest three points. // We can use the localLength to find an abrupt change in SubStrokes, such as at a corner. // We do this by checking localLength against the distance between the first and last // of the three points. If localLength is more than a certain amount longer than the // length between the first and last point, then there must have been a corner of some kind. double localLength = firstPoint.Distance(pivotPoint); // runningLength keeps track of the length between the start of the current SubStroke // and the point we are currently examining. If the runningLength becomes a certain // amount longer than the straight distance between the first point and the current // point, then there is a new SubStroke. This accounts for a more gradual change // from one SubStroke segment to another, such as at a longish curve. double runningLength = localLength; // Iterate over the points, marking the appropriate ones as pivots. while (pointIter.MoveNext()) { WrittenPoint nextPoint = pointIter.Current; // pivotPoint is the point we're currently examining to see if it's a pivot. // We get the distance between this point and the next point and add it // to the length sums we're using. double pivotLength = pivotPoint.Distance(nextPoint); localLength += pivotLength; runningLength += pivotLength; // Check the lengths against the ratios. If the lengths are a certain among // longer than a straight line between the first and last point, then we // mark the point as a pivot. double distFromPrevious = previousPoint.Distance(nextPoint); double distFromFirst = firstPoint.Distance(nextPoint); if (localLength > MAX_LOCAL_LENGTH_RATIO * distFromPrevious || runningLength > MAX_RUNNING_LENGTH_RATIO * distFromFirst) { if (previousPoint.IsPivot && previousPoint.Distance(pivotPoint) < MIN_SEGMENT_LENGTH) { // If the previous point was a pivot and was very close to this point, // which we are about to mark as a pivot, then unmark the previous point as a pivot. // Also need to decrement the SubStroke that it belongs to since it's not part of // the new SubStroke that begins at this pivot. previousPoint.IsPivot = false; previousPoint.SubStrokeIndex = subStrokeIndex - 1; } else { // If we didn't have to unmark a previous pivot, then the we can increment the SubStrokeIndex. // If we did unmark a previous pivot, then the old count still applies and we don't need to increment. subStrokeIndex++; } pivotPoint.IsPivot = true; // A new SubStroke has begun, so the runningLength gets reset. runningLength = pivotLength; firstPoint = pivotPoint; } localLength = pivotLength; // Always update the localLength, since it deals with the last three seen points. previousPoint = pivotPoint; pivotPoint = nextPoint; pivotPoint.SubStrokeIndex = subStrokeIndex; } // last point (currently referenced by pivotPoint) has to be a pivot pivotPoint.IsPivot = true; // Point before the final point may need to be handled specially. // Often mouse action will produce an unintended small segment at the end. // We'll want to unmark the previous point if it's also a pivot and very close to the lat point. // However if the previous point is the first point of the stroke, then don't unmark it, because then we'd only have one pivot. if (previousPoint.IsPivot && previousPoint.Distance(pivotPoint) < MIN_SEGMENT_LENGTH && previousPoint != this.pointList[0]) { previousPoint.IsPivot = false; pivotPoint.SubStrokeIndex = subStrokeIndex - 1; } // Mark the stroke as analyzed so that it won't need to be analyzed again. this.isAnalyzed = true; }