# TikZ: changing colour of a path half way along

Here's the problem:

\documentclass{minimal}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
[scale = 3,
foo/.style={line width = 5pt}]
\draw[foo,red] (0,0) -- (0,1);
\draw[foo,blue] (0,1) -- (1,2);
\end{tikzpicture}
\end{document}


I want a line that changes colour half way along. I could do this with two paths (as above) but as you can see, the picture doesn't look great.

I can make the first path go up to (1,2) as well, and then just draw over it, but the fundamental problem is that the thick line kind of cuts at the wrong angle. I'd like the transition between the red and blue to be angled nicely.

Is there a way to do this with paths, or am I going to have to create custom shapes to achieve the effect I want? Which looks like this:

Use clipping. Supplying a clip does affect the line endings so if you clip against a path at the correct angle, it will truly clip the path as you want. There may be easier ways to implement this (namely, there may be quicker ways to get at the coordinates involved), but the following seems to work.

\documentclass{minimal}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (2,1);
\coordinate (c) at (0,-1);
\path (a);
\pgfgetlastxy{\ax}{\ay}
\path (b);
\pgfgetlastxy{\bx}{\by}
\path (c);
\pgfgetlastxy{\cx}{\cy}
\pgfmathsetmacro{\bx}{\bx - \ax}
\pgfmathsetmacro{\by}{\by - \ay}
\pgfmathsetmacro{\cx}{\cx - \ax}
\pgfmathsetmacro{\cy}{\cy - \ay}
\pgfmathsetmacro{\blen}{veclen(\bx,\by)}
\pgfmathsetmacro{\clen}{veclen(\cx,\cy)}
\pgfmathsetmacro{\dx}{\bx * \clen/\blen + \cx}
\pgfmathsetmacro{\dy}{\by * \clen/\blen + \cy}

\begin{scope}
\clip (a) -- ++(\dx pt, \dy pt) -- ++(2* \bx pt, 2* \by pt) -- ++(-2* \dx pt, -2 * \dy pt) -- ++(-2 * \bx pt, -2 * \by pt) -- (a);
\draw[line width=1cm,blue,line cap=rect] (a) -- (b);
\end{scope}
\begin{scope}
\clip (a) -- ++(\dx pt, \dy pt) -- ++(2* \cx pt, 2* \cy pt) -- ++(-2* \dx pt, -2 * \dy pt) -- ++(-2 * \cx pt, -2 * \cy pt) -- (a);
\draw[line width=1cm,red,line cap=rect] (a) -- (c);
\end{scope}
\end{tikzpicture}
\end{document}


The horrendous calculations are simply to find a point that bisects the angle between the vectors (a)->(b) and (a)->(c). We do that by finding the point on the line from (a) to (b) which is the same distance as (c) is from (a) and then finding the midpoint between that point and (c). That (or rather, the relative vector from (a) to that point) is our desired vector. We then draw two quadrilaterals whose basic properties are that near (a), they are angled to that they bisect the angle (b)(a)(c) and are large enough to contain the rest of the paths.

This method is robust in that changing the coordinates of the three points works as it should. Where it is not robust is that if the paths are more complicated then the clipping boxes will need to be more complicated to accommodate them. As I said, there might also be cleaner ways of implementing this using more internal stuff.

• That's pretty clever. Does pgf not have a "bisect angle and return a vector" macro? That seems to be the tricky bit... – Seamus Feb 13 '11 at 18:36

Forgive me for adding a second answer, but since the idea is quite different I thought that it would be better for organizing.

The following solution is completely automatic and doesn't require any math. It should work for any angle (except 0 and 180), though if it is larger than 180 degrees, you might want to exchange some of the below and above. Also, for very acute angles, you might want to add some clipping (and see the note about pos below).

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}
\begin{document}

\begin{tikzpicture}[scale = 3]
\def\linewidth{5pt}
\def\pointa{(0,0)}
\def\pointb{(0,1)}
\def\pointc{(1,2)}
\path \pointa --
node[pos=0,coordinate,sloped,above=\linewidth/2] (tmp1-1) {}
node[pos=2,coordinate,sloped,above=\linewidth/2] (tmp1-2) {}
node[pos=0,coordinate,sloped,below=\linewidth/2] (tmp1-3) {}
node[pos=2,coordinate,sloped,below=\linewidth/2] (tmp1-4) {}
\pointb;
\path \pointb --
node[pos=-1,coordinate,sloped,above=\linewidth/2] (tmp2-1) {}
node[pos=1,coordinate,sloped,above=\linewidth/2] (tmp2-2) {}
node[pos=-1,coordinate,sloped,below=\linewidth/2] (tmp2-3) {}
node[pos=1,coordinate,sloped,below=\linewidth/2] (tmp2-4) {}
\pointc;

\path[name path=line11] (tmp1-1) -- (tmp1-2);
\path[name path=line12] (tmp1-3) -- (tmp1-4);
\path[name path=line21] (tmp2-1) -- (tmp2-2);
\path[name path=line22] (tmp2-3) -- (tmp2-4);

\path[name intersections={of=line11 and line21}] node[coordinate] (i1) at (intersection-1) {};
\path[name intersections={of=line12 and line22}] node[coordinate] (i2) at (intersection-1) {};

\fill[red] (tmp1-1) -- (i1) -- (i2) -- (tmp1-3) -- cycle;
\fill[blue] (tmp2-2) -- (i1) -- (i2) -- (tmp2-4) -- cycle;
\end{tikzpicture}
\end{document}


First we create some nodes at the sides of the line, half the line width away from the center. The end nodes should be exactly at the height very the path ends, but the middle nodes need to be extended a bit further. For very short paths and big line widths or very acute angles you probably need to make the values in pos=2 and pos=-1 larger. On the other hand, for “decent proportions” you might want to make them smaller, so that the nodes don't enlarge your bounding box. (Or just reset the bounding box afterwards.)

We use these nodes to specify some paths at the sides of the lines we want to draw and then intersect those to get the point where the colors should meet.

Finally, instead of drawing the lines, we fill two shapes using all the points we made TikZ calculate for us.

Here's yet another answer for you. I was thinking how to implement the clip solution for the TeX-SX package and how to deal with the problem about ensuring that the clip path only clips the right part, and thought of a different solution entirely. This solution defines a new family of arrow heads which make the line pointy according to some set angle.

Here's the code:

\documentclass{standalone}

\usepackage{tikz}

\makeatletter

\pgfkeys{
/tikz/sharp arrow angle/.code={%
\pgfsetarrowoptions{sharp left}{#1}
\pgfsetarrowoptions{sharp right}{#1}
},
/tikz/sharp left arrow angle/.code={%
\pgfsetarrowoptions{sharp left}{#1}
},
/tikz/sharp right arrow angle/.code={%
\pgfsetarrowoptions{sharp right}{#1}
}
}

\tikzset{sharp arrow angle=30}

\pgfarrowsdeclare{sharp left}{sharp left}{%
\pgfmathsetlength{\pgf@xa}{.5*\pgflinewidth * tan(\pgfgetarrowoptions{sharp left})}
\pgfarrowsleftextend{\pgf@xa}
\pgfarrowsrightextend{\pgf@xa}
}{%
\pgfmathsetlength{\pgf@xa}{\pgflinewidth * tan(\pgfgetarrowoptions{sharp left})}
\pgfpathmoveto{\pgfqpoint{-.1\pgflinewidth}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{0pt}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{\pgf@xa}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{-.1\pgflinewidth}{.5\pgflinewidth}}
\pgfusepathqfill
}
\pgfarrowsdeclare{sharp right}{sharp right}{%
\pgfmathsetlength{\pgf@xa}{.5*\pgflinewidth * tan(\pgfgetarrowoptions{sharp right})}
\pgfarrowsleftextend{\pgf@xa}
\pgfarrowsrightextend{\pgf@xa}
}{%
\pgfmathsetlength{\pgf@xa}{\pgflinewidth * tan(\pgfgetarrowoptions{sharp right})}
\pgfpathmoveto{\pgfqpoint{-.1\pgflinewidth}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{0pt}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{\pgf@xa}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{-.1\pgflinewidth}{-.5\pgflinewidth}}
\pgfusepathqfill
}

\makeatother

\begin{document}
\begin{tikzpicture}
\path (0,2) rectangle (9,-6);
\draw[line width=3cm,red,sharp arrow angle=60,-sharp left] (0,0) -- (6,0);
\draw[line width=3cm,blue,sharp arrow angle=60,sharp right-] (6,0) -- +(-120:6);
\draw[thick,blue] (0,0) -- (6,0);
\draw[thick,red] (6,0) -- +(-120:6);
\end{tikzpicture}
\end{document}


and here's the result:

The rectangle is just to ensure that the bounding box is large enough (arrow tips seem to get ignored for that). The other two lines are to show that the pointy lines meet at the right place.

• Of my three solutions, I think I like this one the best. It is the simplest, the cleanest, and the most robust. – Loop Space Jun 4 '11 at 21:16
• Good idea! Now it only remains to calculate the angle automatically... – Caramdir Jun 4 '11 at 21:37
• @Caramdir: I'm not sure how best to do that because the first line doesn't know anything about the second line. So the macro that figured it out would have to take in both paths as its arguments, figure out the angle, and then draw the lines appropriately. – Loop Space Jun 4 '11 at 21:53
• Would it be possible to modify this cap style so that the two ends can have different angles? And would it be possible to extend the solution to Bézier curves? (so that the caps are curved in the same way as the path) – Darth Geek Aug 12 at 7:37
• @DarthGeek Just checking: do you mean different ends of the same line (the start and end)? That should be possible but would need a little investigation as to when the arrow options get "baked", as it were. Wrt beziers, that would be the question as to whether arrow tips in general can be curved. My instinct would be to say that the answer is "No", but I'm hesitating because I feel as though I've seen something, either here or in the PGF manual, that makes it possible. Search for "Bendable arrow tips" and if nothing comes up, that's worth a fresh question. – Loop Space Aug 12 at 9:10

I don't have any easy solution, but with some calculations, you can achieve the following (note that line width is not scaled, so everything is divided by three). With some further thought, one should be able to stuff that into a macro that automatically does the right calculations.

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
[scale = 3,
foo/.style={line width = 5pt}]
\draw[foo,red] (0,0) -- (0,1);
\draw[foo,blue] (0,1) -- (1,2);

\fill[red]
($(0,1) + (-2.5pt/3,{(2.5pt*(-1+sqrt(2)))/3})$) --
($(0,1) + ( 2.5pt/3,{(2.5pt*( 1-sqrt(2)))/3})$) --
($(0,1) + ( 2.5pt/3,-2.5pt/3)$) --
($(0,1) + (-2.5pt/3,-2.5pt/3)$) --
cycle;
\fill[blue]
($(0,1) + (-2.5pt/3,{(2.5pt*(-1+sqrt(2)))/3})$) --
($(0,1) + ( 2.5pt/3,{(2.5pt*( 1-sqrt(2)))/3})$) --
($(0.1,1.1) + ( 2.5pt/3,{(2.5pt*( 1-sqrt(2)))/3})$) --
($(0.1,1.1) + (-2.5pt/3,{(2.5pt*(-1+sqrt(2)))/3})$) --
cycle;
\end{tikzpicture}
\end{document}


• Calculating and filling is used at last... It seems easier to do such things in Asymptote, if we make a complex module for this. In TikZ, it is hard to use in practice. Anyway, it is exactly what is required. – Leo Liu Feb 13 '11 at 17:38
• I just completed a similar solution. It is a brute force solution, but I haven't yet figured out anything better. – Throwback1986 Feb 13 '11 at 17:52
• I had a feeling I would end up doing this sort of thing... – Seamus Feb 13 '11 at 17:52
• @Seamus: I have a idea to use rounedpath. I think I can do it in Asymptote, if you want. – Leo Liu Feb 13 '11 at 18:06
• @Seamus: But it seems there's no trivial solution. I think the underlying PostScript/PDF has some restriction. – Leo Liu Feb 13 '11 at 18:54

This is a proof-of-concept. It has a lot of implementation issues that would need sorting out to make it automatic, but Ulrike's answer to Bad intersection of lines in TikZ gave me an idea on how to do this by a very sneaky trick. Basically, you draw the paths up to the join point and then each turns towards the bisector path. That turn makes a nice sharp point. If the part of the turned path is the right length, this is a cheap way of making the path end at an angle. As you can see from the picture, it's not 100% perfect as there's a little extra filled in (try shortening the extensions to see why I put that in). And the numbers in the code were obtained by trial and error (though I believe they could be automated).

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}

\begin{tikzpicture}[line width=7]
\draw[red] (0,0) -- ($(1,1)+(-.5\pgflinewidth,-.5\pgflinewidth)$) -- +(180:.35\pgflinewidth);
\draw[green] (0,2) -- ($(1,1)+(-.5\pgflinewidth,.5\pgflinewidth)$) -- +(180:.35\pgflinewidth);
\end{tikzpicture}
\end{document}


Result:

Coming from pgfplots: problem with "non-smooth" intersection of two lines' ends I thought of custom arrow tips, though Andrew Stacey has been one step ahead of me with his answer.
This answer extends this so that an asymmetric arrow tip can be produced.

In a comment, he states truthfully that

[…] the first line doesn't know anything about the second line. So the macro that figured it out would have to take in both paths as its arguments, figure out the angle, and then draw the lines appropriately.

To circumvent this one could possible do this in one path, an extra key (possible to path with a list of coordinate to go through) and a few edge path operators.
A starting path is shown in the code below.

The sharp protrude switch makes the arrow tips slightly overlap the actual line to hide rendering issues (don’t use with transparency or use a transparency group).

## Code

\documentclass[tikz,convert=false]{standalone}
\usetikzlibrary{backgrounds}
\makeatletter

\pgfkeys{
/tikz/sharp angle/.code={%
\pgfsetarrowoptions{sharp >}{#1}%
\pgfsetarrowoptions{sharp <}{-#1}%
},
/tikz/sharp > angle/.code={%
\pgfsetarrowoptions{sharp >}{#1}%
},
/tikz/sharp < angle/.code={%
\pgfsetarrowoptions{sharp <}{#1}%
},
/tikz/sharp protrude/.code=\csname if#1\endcsname\qrr@tikz@sharp@z@-0.05\p@\else\qrr@tikz@sharp@z@\z@\fi,
/tikz/sharp protrude/.default=true
}

\newdimen\qrr@tikz@sharp@z@
\qrr@tikz@sharp@z@\z@
\pgfarrowsdeclare{sharp >}{sharp >}{%
\edef\pgf@marshal{\noexpand\pgfutil@in@{and}{\pgfgetarrowoptions{sharp >}}}%
\pgf@marshal
\ifpgfutil@in@
\edef\pgf@tempa{\pgfgetarrowoptions{sharp >}}
\expandafter\qrr@tikz@sharp@parse\pgf@tempa\@qrr@tikz@sharp@parse
\else
\qrr@tikz@sharp@parse\pgfgetarrowoptions{sharp >}and-\pgfgetarrowoptions{sharp >}\@qrr@tikz@sharp@parse
\fi
%
\pgfmathparse{max(\pgf@tempa,\pgf@tempb,0)}%
\let\qrr@tikz@sharp@max\pgfmathresult
\pgfmathsetlength\pgf@xa{.5*\pgflinewidth * tan(\qrr@tikz@sharp@max)}%
\pgfarrowsleftextend{+\pgf@xa}%
\pgfarrowsrightextend{+\pgf@xa}%
}{%
\edef\pgf@marshal{\noexpand\pgfutil@in@{and}{\pgfgetarrowoptions{sharp >}}}%
\pgf@marshal
\ifpgfutil@in@
\edef\pgf@tempa{\pgfgetarrowoptions{sharp >}}
\expandafter\qrr@tikz@sharp@parse\pgf@tempa\@qrr@tikz@sharp@parse
\else
\qrr@tikz@sharp@parse\pgfgetarrowoptions{sharp >}and-\pgfgetarrowoptions{sharp >}\@qrr@tikz@sharp@parse
\fi
%
\pgfmathsetlength\pgf@ya{.5*\pgflinewidth * tan(max(\pgf@tempa,\pgf@tempb,0))}%
\pgfmathsetlength\pgf@xa{-.5*\pgflinewidth * tan(\pgf@tempa)}%
\pgfmathsetlength\pgf@xb{-.5*\pgflinewidth * tan(\pgf@tempb)}%
\ifdim\pgf@xa>\pgf@xb
\pgftransformyscale{-1}%
\pgf@xc\pgf@xb
\pgf@xb\pgf@xa
\pgf@xa\pgf@xc
\fi
\pgfpathmoveto{\pgfqpoint{\qrr@tikz@sharp@z@}{.5\pgflinewidth}}%
\pgfpathlineto{\pgfqpoint{\pgf@xa}{.5\pgflinewidth}}%
\pgfpathlineto{\pgfqpoint{\pgf@ya}{+0pt}}%
\pgfpathlineto{\pgfqpoint{\pgf@xb}{-.5\pgflinewidth}}%
\pgfpathlineto{\pgfqpoint{\qrr@tikz@sharp@z@}{-.5\pgflinewidth}}%
\pgfusepathqfill
}
\pgfarrowsdeclare{sharp <}{sharp <}{%
\edef\pgf@marshal{\noexpand\pgfutil@in@{and}{\pgfgetarrowoptions{sharp <}}}%
\pgf@marshal
\ifpgfutil@in@
\edef\pgf@tempa{\pgfgetarrowoptions{sharp <}}
\expandafter\qrr@tikz@sharp@parse\pgf@tempa\@qrr@tikz@sharp@parse
\else
\expandafter\qrr@tikz@sharp@parse\pgfgetarrowoptions{sharp <}and-\pgfgetarrowoptions{sharp <}\@qrr@tikz@sharp@parse
\fi
%
\pgfmathparse{max(\pgf@tempa,\pgf@tempb,0)}%
\let\qrr@tikz@sharp@max\pgfmathresult
\pgfmathsetlength\pgf@xa{.5*\pgflinewidth * tan(\qrr@tikz@sharp@max)}%
\pgfarrowsleftextend{+\pgf@xa}%
\pgfarrowsrightextend{+\pgf@xa}%
}{%
\edef\pgf@marshal{\noexpand\pgfutil@in@{and}{\pgfgetarrowoptions{sharp <}}}%
\pgf@marshal
\ifpgfutil@in@
\edef\pgf@tempa{\pgfgetarrowoptions{sharp <}}
\expandafter\qrr@tikz@sharp@parse\pgf@tempa\@qrr@tikz@sharp@parse
\else
\expandafter\qrr@tikz@sharp@parse\pgfgetarrowoptions{sharp <}and-\pgfgetarrowoptions{sharp <}\@qrr@tikz@sharp@parse
\fi
%
\pgfmathsetlength\pgf@ya{.5*\pgflinewidth * tan(max(\pgf@tempa,\pgf@tempb,0))}%
%
\pgfmathsetlength\pgf@xa{-.5*\pgflinewidth * tan(\pgf@tempa)}%
\pgfmathsetlength\pgf@xb{-.5*\pgflinewidth * tan(\pgf@tempb)}%
\ifdim\pgf@xa>\pgf@xb
\pgftransformyscale{-1}%
\pgf@xc\pgf@xb
\pgf@xb\pgf@xa
\pgf@xa\pgf@xc
\fi
\pgfpathmoveto{\pgfqpoint{\qrr@tikz@sharp@z@}{.5\pgflinewidth}}%
\pgfpathlineto{\pgfqpoint{\pgf@xa}{.5\pgflinewidth}}%
\pgfpathlineto{\pgfqpoint{\pgf@ya}{+0pt}}%
\pgfpathlineto{\pgfqpoint{\pgf@xb}{-.5\pgflinewidth}}%
\pgfpathlineto{\pgfqpoint{\qrr@tikz@sharp@z@}{-.5\pgflinewidth}}%
\pgfusepathqfill
}
\def\qrr@tikz@sharp@parse#1and#2\@qrr@tikz@sharp@parse{\def\pgf@tempa{#1}\def\pgf@tempb{#2}}
\makeatother
\begin{document}
\begin{tikzpicture}[gridded, sharp protrude]
\path[line width=1cm, sharp angle=45,sharp <-sharp >] (0,0) edge[yellow]                          (4,0)
(4,0) edge[blue]                            (4,4)
(4,4) edge[red, sharp > angle=60 and -45]   (0,4)
(0,4) edge[green, sharp < angle=-45 and 60] (0,0)
(0,4) edge[line width=.3cm,
sharp < angle=75 and 75,
sharp > angle=-60 and -30,
sharp <-sharp >]               (2,2)
;
\end{tikzpicture}
\end{document}


## Output

You really want to draw this as a single path - the joins never look correctly otherwise (in my experience, anyway). Take a look at this answer or try the discussion in section 12.4.2 (page 114) of the manual.

Your question doesn't mention smoothly shading between colors, so the first link may be easiest (although both methods can be used to render joined lines similar to your illustration.)

• Fading needs path to be smooth, like line cap=round or rounded corners. No help for zig lines with line cap=butt, I think. – Leo Liu Feb 13 '11 at 15:26
• That solution doesn't seem to be drawing the line as a single path, but lots of little paths... – Seamus Feb 13 '11 at 16:40
• At which version of the manual are you pointing? In v2.10, 12.4.2 is Using Styles to Manage How Pictures Look (on page 121) and in version v2.00, it is The Syntax of Factors. For both I don't see any connection to the question. – Caramdir Feb 13 '11 at 17:56
• Linked version is 1.18 – Throwback1986 Feb 13 '11 at 18:33

You can use the line cap=round option to produce round line endings:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
[scale = 3,
foo/.style={line width = 5pt, line cap=round}]
\draw[foo,red] (0,0) -- (0,1);
\draw[foo,blue] (0,1) -- (1,2);
\end{tikzpicture}
\end{document}


An alternative is to define custom "arrow" heads (see PGF manual) which simply extend the blue line by the correct amount. However this amount will most likely depend on the angle between the lines.

• @Martin That's neat, but it's not really what I want: I want the red and blue to be "balanced": in this solution, it's still clear that the blue is drawn on top... – Seamus Feb 13 '11 at 13:55
• @Seasmus: I see. This is difficult to get right anyway as long both lines have the same width. You could try to (mis-)use arrow heads for this (see my updated answer). – Martin Scharrer Feb 13 '11 at 14:09
• @Martin: It would be helpful if you fill out the "Edit Summary" after an edit. I just wanted to see what your recent edit was. It just said "edited body" (inserted by the software), and it took me some time to find out that you just enlarged your image. Thanks! – Hendrik Vogt Feb 13 '11 at 15:48
• @Hendrik: Sorry for that. I actually shrunk the image size to reduce the size of the answer. Before it wasn't looking pleasant. However due to the constant DPI in my PDF->PNG script the lines are enlarged. – Martin Scharrer Feb 13 '11 at 15:55
• btw, line extension can simply be done by shorten <=-amount. – Caramdir Feb 13 '11 at 18:14

I think you can draw the whole zig-line first, and then draw half.

\begin{tikzpicture}[scale = 3,foo/.style={line width = 5pt}]
\draw[foo,red] (0,0) -- (0,1) -- (1,2);
\draw[foo,blue] (0,1) -- (1,2);
\end{tikzpicture}


It is not perfect, even bad for right angle. I think it is difficult if you simply use \draw to get proper connection.

A solution of Asymptote. It seems there's some bug of PostScript's algorithm. :-(

import roundedpath;
defaultpen(squarecap+miterjoin);

void drawzig(path p, pen[] pens)
{
real r = linewidth(pens[0]) / 5;
path q = roundedpath(p, r);
pens.cyclic=true;
for (int i = 0; i < length(q); ++i) {
draw(subpath(q, 2*i-0.52, 2*i+1.52), pens[i]);
}
}

path p1 = (0,0) -- (0,1cm) -- (1cm,2cm);
path p2 = (0,0) -- (0,1cm) -- (1cm,1cm); p2 = shift(1.5cm,0) * p2;
path p3 = (0,0) -- (0,1cm) -- (1cm,0); p3 = shift(3cm,0) * p3;
pen[] pens = {blue+5pt, red+5pt};

drawzig(p1, pens);
drawzig(p2, pens);
drawzig(p3, pens);

// compare
draw(p1 ^^ p2 ^^ p3);


• I mention doing this in my question, but the problem is that the angle of how the blue line starts is wrong. – Seamus Feb 13 '11 at 16:40
• I have no good idea. In fact, that's not wrong, but only (really) bad. Obtuse angles seem Okey. If you use line join=bevel, it looks much better for acute angles. – Leo Liu Feb 13 '11 at 17:41