/// <summary> /// /// </summary> /// <param name="path"></param> /// <param name="stylusShape"></param> /// <returns></returns> public bool HitTest(IEnumerable <Point> path, StylusShape stylusShape) { // Check the input parameters if (path == null) { throw new System.ArgumentNullException("path"); } if (stylusShape == null) { throw new System.ArgumentNullException("stylusShape"); } if (IEnumerablePointHelper.GetCount(path) == 0) { return(false); } ErasingStroke erasingStroke = new ErasingStroke(stylusShape); erasingStroke.MoveTo(path); Rect erasingBounds = erasingStroke.Bounds; if (erasingBounds.IsEmpty) { return(false); } if (erasingBounds.IntersectsWith(this.GetBounds())) { return(erasingStroke.HitTest(StrokeNodeIterator.GetIterator(this, this.DrawingAttributes))); } return(false); }
/// <summary> /// The implementation behind the public methods AddPoint/AddPoints /// </summary> /// <param name="points">a set of points representing the last increment /// in the moving of the erasing shape</param> protected override void AddPointsCore(IEnumerable <Point> points) { System.Diagnostics.Debug.Assert((points != null) && (IEnumerablePointHelper.GetCount(points) != 0)); System.Diagnostics.Debug.Assert(_erasingStroke != null); // Move the shape through the new points and build the contour of the move. _erasingStroke.MoveTo(points); Rect erasingBounds = _erasingStroke.Bounds; if (erasingBounds.IsEmpty) { return; } List <StrokeHitEventArgs> strokeHitEventArgCollection = null; // Do nothing if there's nobody listening to the events if (StrokeHit != null) { List <StrokeIntersection> eraseAt = new List <StrokeIntersection>(); // Test stroke by stroke and collect the results. for (int x = 0; x < this.StrokeInfos.Count; x++) { StrokeInfo strokeInfo = this.StrokeInfos[x]; // Skip the stroke if its bounding box doesn't intersect with the one of the hitting shape. if ((erasingBounds.IntersectsWith(strokeInfo.StrokeBounds) == false) || (_erasingStroke.EraseTest(StrokeNodeIterator.GetIterator(strokeInfo.Stroke, strokeInfo.Stroke.DrawingAttributes), eraseAt) == false)) { continue; } // Create an event args to raise after done with hit-testing // We don't fire these events right away because user is expected to // modify the stroke collection in her event handler, and that would // invalidate this foreach loop. if (strokeHitEventArgCollection == null) { strokeHitEventArgCollection = new List <StrokeHitEventArgs>(); } strokeHitEventArgCollection.Add(new StrokeHitEventArgs(strokeInfo.Stroke, eraseAt.ToArray())); // We must clear eraseAt or it will contain invalid results for the next strokes eraseAt.Clear(); } } // Raise StrokeHit event if needed. if (strokeHitEventArgCollection != null) { System.Diagnostics.Debug.Assert(strokeHitEventArgCollection.Count != 0); for (int x = 0; x < strokeHitEventArgCollection.Count; x++) { StrokeHitEventArgs eventArgs = strokeHitEventArgCollection[x]; System.Diagnostics.Debug.Assert(eventArgs.HitStroke != null); OnStrokeHit(eventArgs); } } }
/// <summary> /// Erases all ink hit by the contour of an erasing stroke /// </summary> /// <param name="eraserShape">Shape of the eraser</param> /// <param name="eraserPath">a path making the spine of the erasing stroke </param> public void Erase(IEnumerable <Point> eraserPath, StylusShape eraserShape) { // Check the input parameters if (eraserShape == null) { throw new System.ArgumentNullException(SR.Get(SRID.SCEraseShape)); } if (eraserPath == null) { throw new System.ArgumentNullException(SR.Get(SRID.SCErasePath)); } if (IEnumerablePointHelper.GetCount(eraserPath) == 0) { return; } ErasingStroke erasingStroke = new ErasingStroke(eraserShape, eraserPath); for (int i = 0; i < this.Count; i++) { Stroke stroke = this[i]; List <StrokeIntersection> intersections = new List <StrokeIntersection>(); erasingStroke.EraseTest(StrokeNodeIterator.GetIterator(stroke, stroke.DrawingAttributes), intersections); StrokeCollection eraseResult = stroke.Erase(intersections.ToArray()); UpdateStrokeCollection(stroke, eraseResult, ref i); } }
/// <summary> /// Erases all ink inside a lasso /// </summary> /// <param name="lassoPoints">lasso to erase within</param> public void Erase(IEnumerable <Point> lassoPoints) { // Check the input parameters if (lassoPoints == null) { throw new System.ArgumentNullException("lassoPoints"); } int length = IEnumerablePointHelper.GetCount(lassoPoints); if (length == 0) { throw new ArgumentException(SR.Get(SRID.EmptyArray)); } if (length < 3) { return; } Lasso lasso = new SingleLoopLasso(); lasso.AddPoints(lassoPoints); for (int i = 0; i < this.Count; i++) { Stroke stroke = this[i]; StrokeCollection eraseResult = stroke.Erase(stroke.HitTest(lasso)); UpdateStrokeCollection(stroke, eraseResult, ref i); } }
/// <summary>Hit tests all segments within a contour generated with shape and path</summary> /// <param name="shape"></param> /// <param name="path"></param> /// <returns>StrokeIntersection array for these segments</returns> internal StrokeIntersection[] EraseTest(IEnumerable <Point> path, StylusShape shape) { System.Diagnostics.Debug.Assert(shape != null); System.Diagnostics.Debug.Assert(path != null); if (IEnumerablePointHelper.GetCount(path) == 0) { return(Array.Empty <StrokeIntersection>()); } ErasingStroke erasingStroke = new ErasingStroke(shape, path); List <StrokeIntersection> intersections = new List <StrokeIntersection>(); erasingStroke.EraseTest(StrokeNodeIterator.GetIterator(this, this.DrawingAttributes), intersections); return(intersections.ToArray()); }
/// <summary> /// Erase with lasso points. /// </summary> /// <param name="lassoPoints">Lasso points to erase with</param> /// <returns>The after-erasing strokes</returns> public StrokeCollection GetEraseResult(IEnumerable <Point> lassoPoints) { // Check the input parameters if (lassoPoints == null) { throw new System.ArgumentNullException("lassoPoints"); } if (IEnumerablePointHelper.GetCount(lassoPoints) == 0) { throw new ArgumentException(SR.Get(SRID.EmptyArray)); } Lasso lasso = new SingleLoopLasso(); lasso.AddPoints(lassoPoints); return(this.Erase(this.HitTest(lasso))); }
/// <summary> /// Issue: what's the return value /// </summary> /// <param name="path"></param> /// <param name="stylusShape"></param> /// <returns></returns> public StrokeCollection HitTest(IEnumerable <Point> path, StylusShape stylusShape) { // Check the input parameters if (stylusShape == null) { throw new System.ArgumentNullException("stylusShape"); } if (path == null) { throw new System.ArgumentNullException("path"); } if (IEnumerablePointHelper.GetCount(path) == 0) { return(new StrokeCollection()); } // validate input ErasingStroke erasingStroke = new ErasingStroke(stylusShape, path); Rect erasingBounds = erasingStroke.Bounds; if (erasingBounds.IsEmpty) { return(new StrokeCollection()); } StrokeCollection hits = new StrokeCollection(); foreach (Stroke stroke in this) { // samgeo - Presharp issue // Presharp gives a warning when get methods might deref a null. It's complaining // here that 'stroke'' could be null, but StrokeCollection never allows nulls to be added // so this is not possible #pragma warning disable 1634, 1691 #pragma warning suppress 6506 if (erasingBounds.IntersectsWith(stroke.GetBounds()) && erasingStroke.HitTest(StrokeNodeIterator.GetIterator(stroke, stroke.DrawingAttributes))) { hits.Add(stroke); } #pragma warning restore 1634, 1691 } return(hits); }
/// <summary> /// Adds an array of points representing an incremental move of the hit-testing tool /// </summary> /// <param name="points">points representing an incremental move of the hitting tool</param> public void AddPoints(IEnumerable <Point> points) { if (points == null) { throw new System.ArgumentNullException("points"); } if (IEnumerablePointHelper.GetCount(points) == 0) { throw new System.ArgumentException(SR.Get(SRID.EmptyArrayNotAllowedAsArgument), "points"); } if (false == _fValid) { throw new System.InvalidOperationException(SR.Get(SRID.EndHitTestingCalled)); } System.Diagnostics.Debug.Assert(_strokes != null); AddPointsCore(points); }
/// <summary> /// Clips out all ink outside a given lasso /// </summary> /// <param name="lassoPoints">lasso</param> public void Clip(IEnumerable <Point> lassoPoints) { // Check the input parameters if (lassoPoints == null) { throw new System.ArgumentNullException("lassoPoints"); } int length = IEnumerablePointHelper.GetCount(lassoPoints); if (length == 0) { throw new ArgumentException(SR.Get(SRID.EmptyArray)); } if (length < 3) { // // if you're clipping with a point or a line with // two points, it doesn't matter where the line is or if it // intersects any of the strokes, the point or line has no region // so technically everything in the strokecollection // should be removed // this.Clear(); //raises the appropriate events return; } Lasso lasso = new SingleLoopLasso(); lasso.AddPoints(lassoPoints); for (int i = 0; i < this.Count; i++) { Stroke stroke = this[i]; StrokeCollection clipResult = stroke.Clip(stroke.HitTest(lasso)); UpdateStrokeCollection(stroke, clipResult, ref i); } }
/// <summary> /// The implementation behind the public methods AddPoint/AddPoints /// </summary> /// <param name="points">new points to add to the lasso</param> protected override void AddPointsCore(IEnumerable <Point> points) { System.Diagnostics.Debug.Assert((points != null) && (IEnumerablePointHelper.GetCount(points) != 0)); // Add the new points to the lasso int lastPointIndex = (0 != _lasso.PointCount) ? (_lasso.PointCount - 1) : 0; _lasso.AddPoints(points); // Do nothing if there's not enough points, or there's nobody listening // The points may be filtered out, so if all the points are filtered out, (lastPointIndex == (_lasso.PointCount - 1). // For this case, check if the incremental lasso is disabled (i.e., points modified). if ((_lasso.IsEmpty) || (lastPointIndex == (_lasso.PointCount - 1) && false == _lasso.IsIncrementalLassoDirty) || (SelectionChanged == null)) { return; } // Variables for possible HitChanged events to fire StrokeCollection strokesHit = null; StrokeCollection strokesUnhit = null; // Create a lasso that represents the current increment Lasso lassoUpdate = new Lasso(); if (false == _lasso.IsIncrementalLassoDirty) { if (0 < lastPointIndex) { lassoUpdate.AddPoint(_lasso[0]); } // Only the points the have been successfully added to _lasso will be added to // lassoUpdate. for (; lastPointIndex < _lasso.PointCount; lastPointIndex++) { lassoUpdate.AddPoint(_lasso[lastPointIndex]); } } // Enumerate through the strokes and update their hit-test results foreach (StrokeInfo strokeInfo in this.StrokeInfos) { Lasso lasso; if (true == strokeInfo.IsDirty || true == _lasso.IsIncrementalLassoDirty) { // If this is the first time this stroke gets hit-tested with this lasso, // or if the stroke (or its DAs) has changed since the last hit-testing, // or if the lasso points have been modified, // then (re)hit-test this stroke against the entire lasso. lasso = _lasso; strokeInfo.IsDirty = false; } else { // Otherwise, hit-test it against the lasso increment first and then only // those ink points that are in that small lasso need to be hit-tested // against the big (entire) lasso. // This is supposed to be a significant piece of optimization, since // lasso increments are usually very small, they are defined by just // a few points and they don't capture and/or release too many ink nodes. lasso = lassoUpdate; } // Skip those stroke which bounding box doesn't even intersects with the lasso bounds double hitWeightChange = 0f; if (lasso.Bounds.IntersectsWith(strokeInfo.StrokeBounds)) { // Get the stroke node points for the hit-testing. StylusPointCollection stylusPoints = strokeInfo.StylusPoints; // Find out if the lasso update has changed the hit count of the stroke. for (int i = 0; i < stylusPoints.Count; i++) { // Consider only the points that become captured/released with this particular update if (true == lasso.Contains((Point)stylusPoints[i])) { double weight = strokeInfo.GetPointWeight(i); if (lasso == _lasso || _lasso.Contains((Point)stylusPoints[i])) { hitWeightChange += weight; } else { hitWeightChange -= weight; } } } } // Update the stroke hit weight and check whether it has crossed the margin // in either direction since the last update. if ((hitWeightChange != 0) || (lasso == _lasso)) { strokeInfo.HitWeight = (lasso == _lasso) ? hitWeightChange : (strokeInfo.HitWeight + hitWeightChange); bool isHit = DoubleUtil.GreaterThanOrClose(strokeInfo.HitWeight, strokeInfo.TotalWeight * _percentIntersect / 100f - Stroke.PercentageTolerance); if (strokeInfo.IsHit != isHit) { strokeInfo.IsHit = isHit; if (isHit) { // The hit count became greater than the margin percentage, the stroke // needs to be reported for selection if (null == strokesHit) { strokesHit = new StrokeCollection(); } strokesHit.Add(strokeInfo.Stroke); } else { // The hit count just became less than the margin percentage, // the stroke needs to be reported for de-selection if (null == strokesUnhit) { strokesUnhit = new StrokeCollection(); } strokesUnhit.Add(strokeInfo.Stroke); } } } } _lasso.IsIncrementalLassoDirty = false; // Raise StrokesHitChanged event if any strokes has changed thier // hit status and there're the event subscribers. if ((null != strokesHit) || (null != strokesUnhit)) { OnSelectionChanged(new LassoSelectionChangedEventArgs(strokesHit, strokesUnhit)); } }
/// <summary> /// Hit-testing with lasso /// </summary> /// <param name="lassoPoints">points making the lasso</param> /// <param name="percentageWithinLasso">the margin value to tell whether a stroke /// is in or outside of the rect</param> /// <returns>collection of strokes found inside the rectangle</returns> public StrokeCollection HitTest(IEnumerable <Point> lassoPoints, int percentageWithinLasso) { // Check the input parameters if (lassoPoints == null) { throw new System.ArgumentNullException("lassoPoints"); } if ((percentageWithinLasso < 0) || (percentageWithinLasso > 100)) { throw new System.ArgumentOutOfRangeException("percentageWithinLasso"); } if (IEnumerablePointHelper.GetCount(lassoPoints) < 3) { return(new StrokeCollection()); } Lasso lasso = new SingleLoopLasso(); lasso.AddPoints(lassoPoints); // Enumerate through the strokes and collect those captured by the lasso. StrokeCollection lassoedStrokes = new StrokeCollection(); foreach (Stroke stroke in this) { if (percentageWithinLasso == 0) { lassoedStrokes.Add(stroke); } else { StrokeInfo strokeInfo = null; try { strokeInfo = new StrokeInfo(stroke); StylusPointCollection stylusPoints = strokeInfo.StylusPoints; double target = strokeInfo.TotalWeight * percentageWithinLasso / 100.0f - Stroke.PercentageTolerance; for (int i = 0; i < stylusPoints.Count; i++) { if (true == lasso.Contains((Point)stylusPoints[i])) { target -= strokeInfo.GetPointWeight(i); if (DoubleUtil.LessThanOrClose(target, 0f)) { lassoedStrokes.Add(stroke); break; } } } } finally { if (strokeInfo != null) { //detach from event handlers, or else we leak. strokeInfo.Detach(); } } } } // Return the resulting collection return(lassoedStrokes); }