/// <summary>Adds a point to DragState with a filtering algorithm. The /// default filtering algorithm supports "backing up the mouse" for erasure.</summary> /// <returns>true if a point was added, false if not.</returns> protected virtual bool AddFiltered(DragState state, DragPoint dp) { var points = state.MousePoints; if (points.Count < 2) { return(AddIfFarEnough(points, dp)); } var newSeg = (LineSegment <float>)points.Last.Point.To(dp.Point); // Strategy: // 1. Stroke the new segment with a simple rectangle with no endcap. // The rectangle will be a thin box around the point (halfwidth // is 1..2) var newRect = SimpleStroke(newSeg, EraseThreshold1); var newRectBB = newRect.ToBoundingBox(); // 2. Identify the most recent intersection point between this rectangle // (newRect) and the line being drawn. (if there is no such point, // there is no erasure. Done.) // 2b. That intersection point is the one _entering_ the rectangle. Find // the previous intersection point, the one that exits the rectangle. // this is the beginning of the region to potentially erase. var older = points.Reverse().AdjacentPairs().Select(pair => pair.B.Point.To(pair.A.Point)); Point <float> beginning = default(Point <float>); bool keepLooking = false; int offs = 0; var e = older.GetEnumerator(); for (; e.MoveNext(); offs++) { var seg = e.Current; var list = FindIntersectionsWith(seg, newRect, true).ToList(); if (list.Count != 0) { var min = list.MinOrDefault(p => p.A); beginning = min.B; if (!(offs == 0 && min.A == 1)) { if (keepLooking || !PolygonMath.IsPointInPolygon(newRect, seg.A)) { break; } keepLooking = true; } } else if (offs == 0) { } // todo: use IsPointInPolygon if itscs unstable } int iFirst = points.Count - 1 - offs; // index of the first point inside the region (iFirst-1 is outside) if (iFirst > 0) { // 3. Between here and there, identify the farthest point away from the // new point (dp.Point). var region = ((IList <DragPoint>)points).Slice(iFirst); int offsFarthest = region.IndexOfMax(p => (p.Point.Sub(dp.Point)).Quadrance()); int iFarthest = iFirst + offsFarthest; // 4. Make sure that all the points between here and there are close to // this line (within, say... 8 pixels). If so, we have erasure. var seg = dp.Point.To(points[iFarthest].Point); if (region.All(p => p.Point.DistanceTo(seg) < EraseThreshold2)) { // 5. Respond to erasure by deleting all the points between there // and here, not including the first or last point. // 5b. Consider adding the intersection point found in step 2b to // the point list, before adding the new point. points.Resize(iFirst); if (points.Count == 0 || (points.Last.Point.Sub(beginning)).Length() >= MinDistBetweenDragPoints) { points.Add(new DragPoint(beginning, 10, points)); } } } return(AddIfFarEnough(points, dp)); }