microsoft / Win2D

Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering with GPU acceleration. It is available to C#, C++ and VB developers writing apps for the Windows Universal Platform (UWP). It utilizes the power of Direct2D, and integrates seamlessly with XAML and CoreWindow.
http://microsoft.github.io/Win2D
Other
1.8k stars 284 forks source link

Is it possible to extract the boundary path of a stroke #511

Closed Ponant closed 7 years ago

Ponant commented 7 years ago

Hi, I have an InkCanvas where I draw strokes, and I would like to know if it is possible to extract the path which outlines the stroke by using Win2D. By outline, I mean the path boundary (not rect) in which the stroke is contained and filled. Here is a picture which hopefully is clear enough. The draw was made with a rectangle pentip with an angle but the pressure was set to Off for clarity. Thanks stroketooutline

shawnhar commented 7 years ago

After converting the ink to Win2D geometry, you can use any of the CanvasGeometry methods to query information about the path. This provides rich functionality for querying, hit testing, or reading back the path information.

Ponant commented 7 years ago

Thanks. Could you be more specific? You say that any method would work but I do not see how to get the path yet. I assume you propose to start with this

    var canvasGeometry = CanvasGeometry.CreateInk(device, strokes);
    var bounds = canvasGeometry.ComputeBounds();

The only candidate I can see is the void method canvasGeometry.SendPathTo But I do not know how to populate the argument ICanvasPathReceiver ...

shawnhar commented 7 years ago

Depending what you are wanting to do with the path, you might want to use SendPathTo, or Tesselate, or ComputePointOnPath, or FillContainsPoint.

ICanvasPathReciever is an interface that is called by Win2D - you implement this yourself to do whatever you want with the path data.

Ponant commented 7 years ago

I am converting the path to an svg path. I can do this without trouble when the pentip is a circle modulo any transform applied to it, but I am having troubles with the rectangle shape, so the solution is to be able to get the path and convert it to an svg path. Any insight or example would be welcomed.

shawnhar commented 7 years ago

SendPathTo is probably what you want for that.

Ponant commented 7 years ago

Do you have any samples on this repo where can I get the logic behind the interface?

shawnhar commented 7 years ago

I believe it is used somewhere in Example Gallery.

Ponant commented 7 years ago

OK thanks, hopefully I will be able to locate some code.

Ponant commented 7 years ago

Hmm, after reflection and reading this answer by @clandrew https://github.com/Microsoft/Win2D/issues/232 , I think the SendPathTo will provide me with the basic stroke properties (cubic Bezier points, inkpoints position), whereas I would like to retrieve the outline path for a given stroke.

damyanp commented 7 years ago

Are you looking for the "Outline" operation?

See InkExample in example gallery with "Dry Ink Rendering" set to Geometry for an example of how this looks.

Ponant commented 7 years ago

@damyanp , the XAML equivalent is when you take a shape, you remove the fill but keep the border. I am looking at the same but with a stroke.

Ponant commented 7 years ago

@damyanp , I think your proposal is what suits best. As a reminder, this sample has this piece of code:

var strokeStyle = new CanvasStrokeStyle { DashStyle = CanvasDashStyle.Dot };

                var strokesGroupedByColor = from stroke in strokes
                                            group stroke by stroke.DrawingAttributes.Color into strokesOfColor
                                            select strokesOfColor;

                foreach (var strokesOfColor in strokesGroupedByColor)
                {
                    var geometry = CanvasGeometry.CreateInk(ds, strokesOfColor.ToList()).Outline();

                    ds.DrawGeometry(geometry, strokesOfColor.Key, 1, strokeStyle);
                }

With this I can render the outline on a png file by using DrawGeometry. However, what I would like to do is to access the X,Y coordinates of each of the data points + Bezier points in this dashed outline, because these will then be manipulated later in my app. Any advice is welcomed and I should be able to fix it.

damyanp commented 7 years ago

To get at the coordinates of the points on the path you need to use SendPathTo .

Ponant commented 7 years ago

OK guys, thanks a lot I can make it work. I have a few questions on some obscure info in the docs and I we can close this issue. 1) The number of points that I get for the outline or for the stroke itself is much bigger than the number of points provided by InkCanvas (via GetInkPointsand GetRenderingSegments). This makes my SVG files quite big unfortunately. So I wonder if there no cleanup method that I can call. I tried var geometry = CanvasGeometry.CreateInk(ds, strokes).Simplify(CanvasGeometrySimplification.CubicsAndLines).Outline();

and

var geometry = CanvasGeometry.CreateInk(ds, strokes).).Outline().Simplify(CanvasGeometrySimplification.CubicsAndLines);

but they seem to make no difference with respect to

var geometry = CanvasGeometry.CreateInk(ds, strokes).Outline()

2) I left AddQuadraticBezierand AddArc empty , because it seems to me that InCanvas nevers uses Quadratic Bezier and Arcs for strokes. Can you confirm, or else tell me in which pen settings they would appear?

3) What is flatteningTolerance in the Outline method, and what is its range (<1?) and default or recommended value?

4) Is there a simple explanation for CanvasFilledRegionDetermination Winding vs Alternate?

5) CanvasFigureSegmentOptions.ForceUnstroked ???

Thanks

shawnhar commented 7 years ago

1) Win2D/D2D do not provide a general purpose geometry optimizer. The Simplify method simplifies the /type/ of geometry, replacing complex curve types with more restricted beziers or straight line segments, but it will not approximate multiple curves or lines with smaller numbers of shapes. If you need something like that you will have to build it yourself.

Note that the ink rendering implementation in Windows 10 is quite sophisticated and well optimized, so I'm not surprised that you may encounter size or speed problems when moving that same data across to some other more general purpose rendering technology.

2) What types of curve you get is an implementation detail that could change at any time. If you don't want to deal with these, use Simplify to get rid of them.

3) https://microsoft.github.io/Win2D/html/M_Microsoft_Graphics_Canvas_Geometry_CanvasGeometry_ComputeFlatteningTolerance.htm

4) https://msdn.microsoft.com/en-us/library/windows/desktop/dd368110%28v=vs.85%29.aspx

5) Specifies that the segments of the figure will not appear stroked, for example, when drawn with DrawGeometry Overload.

xiedongweo commented 7 years ago

@Ponant Could you please show me how to convert CanvasGeometry path to svg path? I've been troubled by this problem for a long time.