Most of the techniques needed to draw an outlined path are included in those you need to draw compound lines. For the basic idea behind compound lines and a technique that lets you draw symmetric compound lines relatively easily, see the post Draw symmetric compound lines in WPF and C#.

Drawing more general compound lines and drawing an outlined path are much harder tasks. The code used by this example is pretty long so I’m going to omit most of the code from this post. Instead I’ll explain the technique and the methods that implement it. You can look at the code to see the details.

First, what is a path? A `Path` object contains a collection of lines and curves. Each non-connected piece of the `Path` is stored in a figure.

The path mini-language lets you “easily” specify shapes in a `Path` in XAML code. The following code shows how the example program defines its `Path`.

<Path Stroke="Black" StrokeThickness="20" Name="scribblePath"> <Path.Data> <PathGeometry x:Name="scribbleGeometry" Figures="M 20,20 C 200,0 0,200 40,60 L 150,30 C 60,200 200,150 200,60 M 70,140 L 150,240 180,170 30,200 M 270,70 L 270,230 200,200 Z"/> </Path.Data> </Path>

The `Path` object contains a `Data` property that contains a `PathGeometry` object. That object’s `Figures` property uses the path mini-language to specify the shapes. The commands it uses are:

`M 20,20`– Moves the position to (20, 20).`C 200,0 0,200 40,60`– Draws a curve starting at the current point, using control points (200, 0) and (0, 200), and finishing at (40, 60).`L 150,30`– Draws a line from the current point to the point (150, 30).`C 60,200 200,150 200,60`– Draws a curve starting at the current point, using control points (60, 200) and (200, 150), and finishing at (200, 60).`M 70,140`– Moves the position to (70, 140).`L 150,240 180,170 30,200`– Starts at the current point draws lines to the points (250, 240), (180, 170), and (30, 200).`M 270,70`– Moves the position to (270, 70).`L 270,230 200,200`– Starts at the current point draws lines to the points (270, 230) and (200, 200).`Z`– Closes the current figure. In this case drawing a line to the start of the current figure, which is (270, 70).

Making an outlined path directly from a `Path` object would be extremely hard because of all of the shapes it might contain. You can simplify the problem if you first flatten the path so it is made up of lines and polylines. The following `GetFlattenedFigurePoints` does just that.

The program uses the following statement to convert the initial `PathGeometry` object into a flattened version.

PathGeometry flat = scribbleGeometry.GetFlattenedPathGeometry();

The result is a new `PathGeometry` that contains only `LineSegment` and `PolylineSegment` objects.

So the first step is flattening the path. Next we need to find the points that make up the left and right edges of the flattened path. I'll explain why we need those points in my next post. For now, we'll use them to outline the path.

To understand the general idea, take a look at Figure 1, which shows two segments along the flattened path. The path moves from point p1 to point p2 and then to point p3.

The red point marks the left edge of the path at point p2. Similarly the green point marks the right edge at point p2.

To find the red point, we need to find the intersection of the left edge of segment p1→p2 and the left edge of segment p2→p3. (Similarly the green point is at the intersection of the segments' right edges.)

To see how you find the left edge of a segment, look at Figure 2. The values dx and dy are the changes in the X and Y coordinates moving from a segment's starting point to its ending point. In that case, the directions <-dy, dx> and <dy, -dx> are both perpendicular to the line segment.

After calculating dx and dy, the program divides them by the length of the segment and then multiplies the result by half of the segment's thickness to get the values px and py. In other words:

px = dx / length * half_thickness py = dy / length * half_thickness

If p1 and p2 are the segment's starting and ending points, then p1 + <-py, px> and p2 + <-py, px> are points on the left edge of the segment. (Similarly p1 + <py, -px> and p2 + <py, -px> are points on the right edge of the segment.)

At this point, you know everything you need to know to find a path's left and right edge points.

- Loop through the path's figures. For each figure:
- Flatten the figure.
- Loop through the flattened figure's
`LineSegment`and`PolylineSegment`objects and make a list of their points. - Loop through the segments. For each pair of adjacent segments:
- Find points on the left edges of the segments.
- Find the intersection of the lines defined by the left edge points. Add that point of intersection to the left edge point list.
- Repeat for the right edges.

After you've found the left and right edge points, outlining a figure is relatively simple. Start by following the left edge points. When you get to the end of the figure, follow the right edge points backwards to the beginning. The result is a loop that outlines the figure.

This high-level description skims over a **lot** of details. Look at the code to see all of the minutiae.

While the code that performs all of those calculations is pretty involved, the final result is fairly easy to use. The following code shows how the example program outlines its path.

private void btnGo_Click(object sender, RoutedEventArgs e) { // Get the outline path. PathGeometry flat = scribbleGeometry.GetFlattenedPathGeometry(); double thickness = scribblePath.StrokeThickness; // Get polygons representing the path's outlines. Listpolygons = GetPathOutlinePolygons(flat, thickness); // Add the polygons to the main Grid. foreach (Polygon polygon in polygons) { polygon.Stroke = Brushes.Red; polygon.StrokeThickness = 1; grdMain.Children.Add(polygon); } // Disable the Go button. btnGo.IsEnabled = false; btnGo.Opacity = 0.5; }

Recall that the `scribbleGeometry` object is the `PathGeometry` object that contains the shapes. The code starts by calling its `GetFlattenedPathGeometry` method (provided by the .NET Framework `PathGeometry` class) to get the flattened path. It also copies the `Path` object's thickness into a variable.

Next the code calls the `GetPathOutlinePolygons` method to do all of the example's heavy lifting. The result is a list of `Polygon` objects, each of which represents the outline of a figure in the path. The program sets each `polygon` object's color and thickness, and adds it to the program's main `Grid` control.

The code finishes by disabling the Go button.

Download the example and experiment with it. In my next post I'll explain how you can use the left and right edge points to create general compound lines.

Pingback: Make compound lines in WPF and C# - C# HelperC# Helper

Hello.

I have a question about this.

I want to make ouline about 3d model.

How can i make outline about 3d model in viewport3d?

I’m not sure I understand. Do you mean you want to draw the outline around an object as seen from a particular point of view?

I don’t think that’s easy, at least not analytically. You could draw the model onto a bitmap and then process that image to generate the outline.

Or you could transform the model’s vertices so you’re looking at the model from the top. Then you could ignore their Y coordinates and generate polygons in the X-Z plane.

If I misunderstand the question and you want a wireframe, search this site for “wireframe” and you’ll see some examples.