The plain.mp
implementation of buildcycle
fails to find the correct overlap between two cyclic paths if the beginning (point 0
) of one of the paths lies inside the other path. For example:
beginfig(3);
path A, B; picture p[];
A = fullcircle scaled 2.5cm;
B = fullcircle scaled 2cm shifted (1cm,0);
p1 = image(fill buildcycle(A,B) withcolor .8[blue,white]; drawarrow A; drawarrow B;);
A := A rotated 180;
p2 = image(fill buildcycle(A,B) withcolor .8[blue,white]; drawarrow A; drawarrow B;);
B := B rotatedabout(center B,180);
p3 = image(fill buildcycle(A,B) withcolor .8[blue,white]; drawarrow A; drawarrow B;);
A := A rotated 180;
p4 = image(fill buildcycle(A,B) withcolor .8[blue,white]; drawarrow A; drawarrow B;);
for i=1 upto 4: draw p[i] shifted (120i,0); label(decimal i, (7mm+120i,0)); endfor
endfig;
produces

In each subfigure, the arrow head is pointing at the beginning of the path.
You can see that buildcycle
only works in situation 2 where neither point 0
lies within the other cyclic path. Note that in situation 4 we get the union of the two paths.
Here's a way to correct this fault. First you need a function to determine whether a given point lies within a cyclic path. Following Sedgewick's Algorirthms in C, you can write:
% is point "p" inside cyclic path "ring" ?
vardef inside(expr p, ring) =
save t, count, test_line;
count := 0;
path test_line;
test_line = p -- (infinity, ypart p);
for i = 1 upto length ring:
t := xpart(subpath(i-1,i) of ring intersectiontimes test_line);
if ((0<=t) and (t<1)): count := count + 1; fi
endfor
odd(count)
enddef;
And here is an overlap
function that replaces buildcycle
in this special case of two overlapping cyclic paths.
vardef front_half primary p = subpath(0, 1/2 length p) of p enddef;
vardef back_half primary p = subpath(1/2 length p, length p) of p enddef;
% a and b should be cyclic paths...
vardef overlap(expr a, b) =
save p, q;
boolean p, q;
p = inside(point 0 of a, b);
q = inside(point 0 of b, a);
if ((not p) and (not q)):
buildcycle(a,b)
elseif (not p):
buildcycle(front_half b, a, back_half b)
elseif (not q):
buildcycle(front_half a, b, back_half a)
else:
buildcycle(front_half a, back_half b, front_half b, back_half a)
fi
enddef;
The basic idea is that if neither point 0
is inside the other path, then you just call buildcycle
, otherwise you split the two cycles up into an appropriate sequence of half cycles. Replacing buildcycle
in the above example with overlap
gives this:

And replacing buildcycle
in the OP example with overlap
gives this:

which is probably closer to what was wanted in the first place.
Furthermore, if the two cyclic paths are running in opposite directions the behaviour of buildcycle
is different again. Here's an extended version of the first example; in the second row, the larger path is running backwards:

To deal with this possibility you can make overlap
more robust by using the useful counterclockwise
function (from plain.mp
) which returns a copy of a cyclic path running counterclockwise. Here is the improved version of the overlap
function.
vardef overlap(expr a, b) =
save p, q, A, B;
boolean p, q;
p = not inside(point 0 of a, b);
q = not inside(point 0 of b, a);
path A, B;
A = counterclockwise a;
B = counterclockwise b;
if (p and q):
buildcycle(A,B)
elseif p:
buildcycle(front_half B, A, back_half B)
elseif q:
buildcycle(front_half A, B, back_half A)
else:
buildcycle(front_half A, back_half B, front_half B, back_half A)
fi
enddef;
which produces this in my extended example.

u:=10pt;
pat4=buildcycle(pat0,pat3)
andshow pat4
, I get(-77.10028,51.76262)..controls (-77.10014,51.76274) and (-77.10033,51.76294)..(-77.10046,51.76282)..controls (-77.1006,51.7627) and (-77.10042,51.7625)..cycle
. I can't say why, but this explains why you don't see the filling.