Caps and Joins

As the PenDashStyle program indicates, when lines start to get wide, they assume a graphical form of their own. You may like the square and rectangular appearance of the dots and dashes in styled lines, but you may prefer a more rounded appearance instead.

This is the realm of line caps (also known as ends) and joins. The cap governs the appearance of the lines at their beginning and end, or the appearance of the dots and dashes. The join governs what happens at the meeting of two connected lines. Here are the four basic caps and joins properties:

Pen Properties (selection)

Type

Property

Accessibility

LineCap

StartCap

get/set

LineCap

EndCap

get/set

Pen Properties (selection)

Type

Property

Accessibility

DashCap

DashCap

get/set

LineJoin

LineJoin

get/set

I want to begin with the LineJoin property because that's probably the simplest. The property can take on one of the following enumeration values:

LineJoin Enumeration

Member

Value

Description

Miter

0

Default, pointed

Bevel

1

Leveled off

Round

2

Rounded

MiterClipped

3

Pointed with limitations

The LineJoin property affects only lines that are connected, that is, polylines drawn with DrawLines or connected lines in a path. Here's a program that draws simple V-shaped polylines with the four different LineJoin values.

LineJoins.cs

// LineJoins.cs ® 2001 by Charles Petzold //----------------------------------------

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

class LineJoins: PrintableForm

public new static void Main()

Application.Run(new LineJoins()

public LineJoins()

Text = "Line Joins: Miter, Bevel, Round, MiterClipped";

protected override void DoPage(Graphics grfx, Color clr, int cx, int

Pen penNarrow = new Pen(clr);

Point[] apt = { new Point(1 * cx / 32, 1 * cy / 8), new Point(4 * cx / 32, 6 * cy / 8), new Point(7 * cx / 32, 1 * cy / 8) };

(int i = 0; i < 4; i++) penWide.LineJoin = (LineJoin) i;

And here's what it looks like:

Wpf Pen Miterlimit

The wide gray pen is the one whose LineJoin property is set. The thin black line shows the actual geometric line. You'll notice that the MiterClipped join looks just like Miter, but try making the form very tall: the Miter join continues to get longer and pointier, but at some point the MiterClipped join is truncated to look the same as a Bevel join. There's a reason to limit the length of miter joins: as the angle between two joined lines increases, the miter join can become very long. For example, a 1-inch-thick polyline joined at an angle of 1 degree would have a miter join that extended over 4C feet!111 The Pen class has a special property to limit this extent when the LineJoin property is MiterClipped:

Pen Properties (selection)

Type

Property

Accessibility

float

MiterLimit

get/set

The property truncates the miter join at a distance of pen.MiterLimit i pen.Width / 2. The default MiterLimit is 10. If the Width property of the pen is 20, the miter extends only 100 units past the theoretical end of the line.

Let's now take a look at the DashCap property that affects the appearance of dots and dashes in styled lines. The property can take on one of the following enumeration values.

DashCap Enumeration

Member

Value

Flat

0

Round

2

Triangle

3

Here's a variation of the PenDashStyles program that displays a DashDotDot line using the three different DashCap values.

PenDashCaps.cs grfx.DrawLines(penWide, apt); grfx.DrawLines(penNarrow, apt); grfx.TranslateTransform(cx / 4, 0);

// PenDashCaps.es ® 2001 by Charles Petzold //------------------------------------------

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

elass PenDashCaps: PrintableForm {

Menultem miCheeked;

publie new statie void Main() {

Applieation.Run(new PenDashCaps());

publie PenDashCaps() {

Text = "Pen Dash Caps: Flat, Round, Triangle";

Menu = new MainMenu(); Menu.Menultems.Add("&Width");

foreaeh (int iWidth in aiWidth)

Menu.MenuItems[0].MenuItems.Add(iWidth,ToString(), new

EventHandler(MenuWidthOnCliek));

miCheeked = Menu.Menultems[0].MenuItems[0]; miCheeked.Cheeked = true;

void MenuWidthOnCliek(objeet obj, EventArgs ea) {

miCheeked.Cheeked = false; miCheeked = (Menultem) obj; miCheeked.Cheeked = true; Invalidate();

proteeted override void DoPage(Graphies grfx, Color elr, int ex, int ey)

Pen pen = new Pen(elr, Convert.ToInt32(miCheeked.Text)); pen.DashStyle = DashStyle.DashDotDot;

foreaeh (DashCap de in Enum.GetValues(typeof(DashCap)))

pen.DashCap = de;

grfx.DrawLine(pen, ex / 8, grfx.TranslateTransform(0, ey / 4, 7 * ex / 8, ey / 4) ey / 4);

Here's the display when you select a width of 25:

These look a little odd because the beginning and end of the actual line is still squared off. The appearance of the beginning and end of the line is affected by the StartCap and EndCap properties, both of which are of type LineCap. You can insert the following statement into the PenDashCaps program to make these two properties consistent with the DashCap property:

pen.StartCap = pen.EndCap = (LineCap) (int) dc; The display then looks like this:

Net Customstartcap

The two lines with the round and triangle caps aren't quite aligned with the flat caps. The reason they're not is that (as we'll see) the round and triangle caps go beyond the geometric point marking the beginning and end of the line. But the full width of the dashes and dots is kept consistent regardless of the cap style.

Here's the complete LineCap enumeration:

LineCap Enumeration

Member

Value

LineCap Enumeration

Member

Value

Flat

0x00

Square

0x01

Round

0x02

Triangle

0x03

NoAnchor

0x10

SquareAnchor

0x11

RoundAnchor

0x12

DiamondAnchor

0x13

ArrowAnchor

0x14

AnchorMask

0xF0

Custom

0xFF

And here's a program that draws wide lines using all these values. The width of the line is fixed at the Font.Height property. In addition, the program draws thin lines showing the geometric beginning and end of each line.

LineCaps.es

// LineCaps.es ® 2001 by Charles Petzold //---------------------------------------

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

elass LineCaps: PrintableForm {

publie new statie void Main()

Application. Run(new LineCapsO);

public LineCapsO

proteeted override void DoPage(Graphies grfx, Color elr, int ex, int

Pen penWide = new Pen(Color.Gray, Font.Height);

Pen penNarrow = new Pen(elr);

Brush brush = new SolidBrush(elr);

foreach (LineCap lc in Enum.GetValues(typeof(LineCap)))

grfx.DrawString(le.ToString(), Font, brush,

Font.Height, Font.Height / 2);

penWide.StartCap = le; penWide.EndCap = le;

grfx.DrawLine(penWide, 2 * ex / 4, Font.Height,

grfx.DrawLine(penNarrow, 2 * ex / 4, Font.Height,

grfx.TranslateTransform(0, 2 * Font.Height);

Here's the result:

Adjustablearrowcap Net

Keep in mind that I'm using the same enumeration value for the beginning and end of the line. You can use different values if you want.

The NoAnchor value produces the same result as Flat. The SquareAnchor, RoundAnchor, and DiamondAnchor line ends are similar to Square, Round, and Triangle, respectively (as their enumeration values indicate), except that they are larger.

If the various line caps provided by the LineCap enumeration aren't enough for you, you can set the StartCap and/or EndCap properties of the pen equal to LineCap.Custom and then make use of the following properties: Pen Properties (selection)

Type

Property

Accessibility

CustomLineCap

CustomStartCap

get/set

CustomLineCap

CustomEndCap

get/set

The CustomLineCap class (in System.Drawing.Drawing2D) lets you use a path to define the outline of your custom caps. In addition, the AdjustableArrowCap class derives from CustomLineCap to let you draw arrow caps with more control over the arrow size and filled interior.

111 Let w be the width of the line and a the join angle. It's easy to show that the extension of the miter tip past the actual join point is (w/2)/sin(a/2).

0 0

Post a comment

  • Receive news updates via email from this site