// The base class gathers mouse events and calls AnalyzeGesture() protected override bool AddFiltered(Util.WinForms.DragState state_, DragPoint dp) { DragState state = (DragState)state_; if (state.ClickedShape != null && state.ClickedShape.AllowsDrag) { return(false); // gesture recognition is off } return(base.AddFiltered(state, dp)); }
protected override void AnalyzeGesture(Util.WinForms.DragState state_, bool mouseUp) { // TODO: Analyze on separate thread and maybe even draw on a separate thread. // Otherwise, on slow computers, mouse input may be missed or laggy due to drawing/analysis DragState state = (DragState)state_; var adorners = Control.DragAdornerShapes; adorners.Clear(); Shape newShape = null; IEnumerable <Shape> eraseSet = null; if (state.IsDrag) { if (state.ClickedShape != null && state.ClickedShape.AllowsDrag) { HandleShapeDrag(state); } else { List <PointT> simplified; bool cancel; eraseSet = RecognizeScribbleForEraseOrCancel(state, out cancel, out simplified); if (eraseSet != null) { ShowEraseDuringDrag(state, adorners, eraseSet, simplified, cancel); } else { bool potentialSelection = false; newShape = DetectNewShapeDuringDrag(state, adorners, out potentialSelection); if (potentialSelection) { var selecting = ShapesInside(newShape.BBox).ToList(); if (selecting.Count != 0) { newShape.Style = SelectorBoxStyle; } } } } } if (mouseUp) { adorners.Clear(); HandleMouseUp(state, newShape, eraseSet); } else if (newShape != null) { newShape.AddLLShapesTo(adorners); newShape.Dispose(); } Control.DragAdornersChanged(); }
/// <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)); }
/// <summary>This method is called when the mouse is clicked, for every /// mouse move event while the mouse is down, and one last time on mouse up.</summary> /// <param name="state">Contains the history of mouse moves since mouse down.</param> /// <param name="mouseUp">If true, the gesture is ending because the mouse /// button was just released.</param> protected abstract void AnalyzeGesture(DragState state, bool mouseUp);