Reputation: 3380
It seems that WPF's InkCanvas
is only able to provide the points of the stroke (independent of the width and height of the stroke). For an application, I need to know all the points that are drawn by the InkCanvas
.
For instance, assume that the width and height of the stroke are 16. Using this stroke size I paint a dot on the InkCanvas
. Is there a straightforward way to obtain all 256 pixels in this dot (and not the center point of this giant dot alone)?
Why I care:
In my application, the user uses an InkCanvas
to draw on top of a Viewport3D
which is displaying a few 3D objects. I want to use all the points of the strokes to perform ray casting and determine which objects in the Viewport3D
have been overlaid by the user's strokes.
Upvotes: 1
Views: 1239
Reputation: 114
You can use the StrokeCollection's HitTest method. I've compared the performance of your solution with this implementation and found the HitTest method performs better. Your mileage etc.
// get our position on our parent.
var ul = TranslatePoint(new Point(0, 0), this.Parent as UIElement);
// get our area rect
var controlArea = new Rect(ul, new Point(ul.X + ActualWidth, ul.Y + ActualHeight));
// hit test for any strokes that have at least 5% of their length in our area
var strokes = _formInkCanvas.Strokes.HitTest(controlArea, 5);
if (strokes.Any())
{
// do something with this new knowledge
}
You can find the documentation here: https://learn.microsoft.com/en-us/dotnet/api/system.windows.ink.strokecollection.hittest?view=netframework-4.7.2
Further, if you only care if any point is in your rect, you can use the code below. It's an order of magnitude faster than StrokeCollection.HitTest because it doesn't care about percentages of strokes so it does a lot less work.
private bool StrokeHitTest(Rect bounds, StrokeCollection strokes)
{
for (int ix = 0; ix < strokes.Count; ix++)
{
var stroke = strokes[ix];
var stylusPoints = stroke.DrawingAttributes.FitToCurve ?
stroke.GetBezierStylusPoints() :
stroke.StylusPoints;
for (int i = 0; i < stylusPoints.Count; i++)
{
if (bounds.Contains((Point)stylusPoints[i]))
{
return true;
}
}
}
return false;
}
Upvotes: 2
Reputation: 3380
I found a very dirty way of handling this. If anyone knows of a better method, I'll be more than happy to upvote and accept their response as an answer.
Basically my method involves getting the Geometry
of each stroke, traversing all the points inside the boundaries of that geometry and determining whether the point is inside the geometry or not.
Here's the code that I am using now:
foreach (var stroke in inkCanvas.Strokes)
{
List<Point> pointsInside = new List<Point>();
Geometry sketchGeo = stroke.GetGeometry();
Rect strokeBounds = sketchGeo.Bounds;
for (int x = (int)strokeBounds.TopLeft.X; x < (int)strokeBounds.TopRight.X + 1; x++)
for (int y = (int)strokeBounds.TopLeft.Y; y < (int)strokeBounds.BottomLeft.Y + 1; y++)
{
Point p = new Point(x, y);
if (sketchGeo.FillContains(p))
pointsInside.Add(p);
}
}
Upvotes: 2