summaryrefslogtreecommitdiff
path: root/fonts/feyn/feyn.mf
diff options
context:
space:
mode:
Diffstat (limited to 'fonts/feyn/feyn.mf')
-rw-r--r--fonts/feyn/feyn.mf811
1 files changed, 811 insertions, 0 deletions
diff --git a/fonts/feyn/feyn.mf b/fonts/feyn/feyn.mf
new file mode 100644
index 0000000000..60a64f4d01
--- /dev/null
+++ b/fonts/feyn/feyn.mf
@@ -0,0 +1,811 @@
+% This is the metafont for the feynfont font, release 0.4.1, 2017 November 03
+%
+% Copyright 1991, 1994, 2001, 2002, 2005, 2008-10, Norman Gray.
+% See the file LICENCE for licence details.
+%
+% Mercurial revision 558acb5f2e14, 2017-11-03 14:43 +0000, tag 0.4.1 + 0
+%
+%
+%
+% When I look at the property list of the generated TFM (using
+% tftopl) it starts with `(FAMILY CMR)': but this isn't in the cmr
+% family. I can't find anything in cmbase.mf which would cause
+% this. Perhaps I need to read the Metafont Book more closely.
+
+
+mode_setup; font_setup;
+
+% a# is the position of the characters above the baseline.
+a# := on_math_axis * math_axis#; % math_axis is defined by cmr
+
+% Feyn macros
+input feynmac;
+
+define_pixels (module,bigarrow,littlearrow,blobr,a);
+define_blacker_pixels (linewidth,thinlinewidth);
+
+pickup pencircle scaled linewidth;
+diagram_pen := savepen;
+
+% general definitions
+phangle := 75; % photon angle
+
+squash := 0.75; % the amount by which some loops are squashed
+
+% The caller must set the following parameters to values such as these
+%boson_wiggles := 4; %this must be an even number
+%boson_wiggles_loop_small := 4;
+%boson_wiggles_loop_normal := 6;
+%boson_wiggles_loop_large := 8;
+
+
+% All the character positions between 0 and hex"7F"
+% have characters in them. Not all of these characters are ones
+% which should appear on paper; some (such as `s' or `l') only
+% appear in ligatures, and others shouldn't appear at all. If,
+% however, there are no characters in these positions, some DVI
+% readers (including TFtoPL and OzTeX for example) complain about a
+% `bad TFM file'. These locations are therefore occupied by the
+% character generated by the errorchar macro, which is 0pt wide, and
+% 1module# high. This character doesn't generate any real error,
+% but the glyph will be manifestly peculiar on paper.
+%
+% Add code to extra_beginchar so that we keep a record of all the
+% characters that we generate, so that we can generate an errochar
+% character for each of the codepoints that we've skipped. `charcode'
+% is defined in the expansion of beginchar()
+
+numeric donechar[];
+string donecharerror;
+donecharerror := "Duplicate character";
+extra_beginchar := extra_beginchar & "if known donechar[charcode]: errmessage donecharerror; fi donechar[charcode] := 1;";
+
+path charpath, charpathb;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Digits 0x30--0x39. Obtain these from the cmr source file: romand.mf.
+input romand;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Fermions
+
+def path_fermion (expr scale_sharp, ang) =
+ begingroup
+ save scale;
+ scale := hround(scale_sharp*hppp);
+ if ang > 0: % slopes upward
+ (0,a) .. (scale*(cosd ang), scale*(abs sind ang)+a)
+ else :
+ (0,scale*(abs sind ang)+a) .. (scale*(cosd ang), a)
+ fi
+ endgroup
+enddef;
+
+threechars ("f", "fermion",
+ path_fermion(2module#, 0), 0.5, 2module#, 0, 0,
+ (0,0.1module), "top");
+
+begingroup % make closed fermion loop, for phi^4 theory
+ clearxy;
+ save height;
+ height# := 2module#;
+ define_pixels(height);
+ z2 = (0, height); %top of loop
+ y1 = y3 = 2y2/3;
+ x1+x3 = 0;
+ x1-x3 = 2height/3;
+ charpath := origin .. z1 .. z2 .. z3 .. origin;
+ threechars ("k", "fermion loop",
+ charpath shifted (0,a), 2,
+ 0, 2module#, 0,
+ 0, "top");
+endgroup;
+
+beginchar (hex"21", module#, 0, 0);
+ "short fermion";
+ pen;
+ draw path_fermion(module#, 0);
+endchar;
+
+
+% The following characters are in boxes the same vertical size as the black
+% bit of the character, but they project out of the top by an amount equal to
+% the height of the math axis, and are that same height clear of the bottom
+% of the box.
+%
+%%%% -90 <= ang <= +90, or all hell will break loose
+
+threechars ("e", "upward fermion",
+ path_fermion(2module#, 45), 0.5,
+ 2module#*(cosd 45), 2module#*(abs sind 45), 0,
+ 0, "ulft");
+
+threechars ("d", "downward fermion",
+ path_fermion(2module#, -45), 0.5,
+ 2module#*(cosd -45), 2module#*(abs sind -45), 0,
+ 0, "urt");
+
+threechars ("b", "vertically upward fermion",
+ path_fermion(2module#, 90), 0.5,
+ 2module#*(cosd 90), 2module#*(abs sind 90), 0,
+ 0, "urt");
+
+charpath := path_fermion(2module#, 0);
+beginchar("m", 2module#, 0, 0); "massive fermion";
+ pen;
+ draw charpath shifted (0,linewidth);
+ draw charpath shifted (0,-linewidth);
+ annotate_at(point 0.5 of charpath shifted (0,linewidth), "top");
+endchar;
+
+beginchar("M", 2module#, 0, 0); "r-arrowed massive fermion";
+ pen;
+ draw charpath shifted (0,linewidth);
+ draw charpath shifted (0,-linewidth);
+ drawarrow (bigarrow, point 0.5 of charpath, 0);
+ annotate_at(point 0.5 of charpath shifted (0,linewidth), "top");
+endchar;
+
+beginchar(byte"m"-hex"60", 2module#, 0, 0); "l-arrowed massive fermion";
+ pen;
+ draw charpath shifted (0,linewidth);
+ draw charpath shifted (0,-linewidth);
+ drawarrow (bigarrow, point 0.5 of charpath, 180);
+ annotate_at(point 0.5 of charpath shifted (0,linewidth), "top");
+endchar;
+
+charpath := path_fermion(module#, 0);
+beginchar(hex"23", module#, 0, 0); "short massive fermion";
+ pen;
+ draw charpath shifted (0,linewidth);
+ draw charpath shifted (0,-linewidth);
+ %drawarrow (littlearrow, point 0.5 of charpath, 0);
+ annotate_at(point 0.5 of charpath shifted (0,linewidth), "top");
+endchar;
+
+begingroup
+ clearxy;
+ save width;
+ width# := 4module#;
+ define_pixels(width);
+ x2-x0 = 2(x1-x0) = width;
+ x1 = 0;
+ y0 = y2 = 0;
+ y1 = 2squash*module;
+ charpath := z0 .. z1 .. z2;
+
+ threechars("l", "fermion loop",
+ charpath shifted (0,a), 1,
+ 0, 2squash*module#+a#, 0,
+ 0, "top");
+ threechars("n", "fermion loop, inverted",
+ charpath yscaled -1 shifted (0,a), 1,
+ 0, a#, 0,
+ 0, "bot");
+ threechars("o", "fermion loop, small",
+ charpath scaled 0.66667 shifted (0,0.66667a), 1,
+ 0, module#+a#, 0,
+ 0, "top");
+ threechars("w", "fermion loop, small, inverted",
+ charpath xscaled 0.66667 yscaled -0.66667 shifted(0,0.66667a), 1,
+ 0, module#+a#, 0,
+ 0, "bot");
+endgroup;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Gauge bosons
+%
+
+def path_boson (expr scale_sharp, ang, taper) =
+ begingroup
+ save scale;
+ clearxy;
+ scale := hround(scale_sharp*hppp);
+ x1 = 0;
+ x99 = scale*(cosd ang);
+ if ang > 0 : % slopes upward
+ y1 = a;
+ y99 = scale*(abs sind ang) + a;
+ else :
+ y1 = scale*(abs sind ang) + a;
+ y99 = a;
+ fi
+ for i = 3 upto boson_wiggles:
+ z[i] - z[i-1] = z2-z1;
+ endfor
+ z99 - z[boson_wiggles] = z2-z1;
+ def ::(expr b) = {dir (ang+b)} looselink {dir (ang-b)} enddef;
+
+ z1 ::(phangle)z2
+ for i = 3 step 2 until boson_wiggles:
+ ::(-phangle) z[i] ::(phangle)z[i+1]
+ endfor
+ if taper <> 0 :
+ {dir (ang-phangle)} .. tension 1 and 0.8 .. {dir (ang+phangle/2)}
+ else :
+ ::(-phangle)
+ fi
+ z99
+ endgroup
+enddef;
+
+% the following is a simpler version of the above, without the
+% boson_wiggles, to document what it's supposed to be doing
+def path_boson_orig (expr scale_sharp, ang, taper) =
+ begingroup
+ save scale;
+ clearxy;
+ scale := hround(scale_sharp*hppp);
+ x1 = 0; x5 = scale*(cosd ang);
+ if ang > 0 : % slopes upward
+ y1 = a;
+ y5 = scale*(abs sind ang) + a;
+ else :
+ y1 = scale*(abs sind ang) + a;
+ y5 = a;
+ fi
+ z2-z1 = z3-z2 = z4-z3 = z5-z4;
+ def ::(expr b) = {dir (ang+b)} looselink {dir (ang-b)} enddef;
+
+ z1 ::(phangle) z2 ::(-phangle) z3 ::(phangle) z4
+ if taper <> 0 :
+ {dir (ang-phangle)} .. tension 1 and 0.8 .. {dir (ang+phangle/2)}
+ else :
+ ::(-phangle)
+ fi
+ z5
+ endgroup
+enddef;
+
+threechars ("g", "gluon",
+ path_boson(2module#, 0, 0), boson_wiggles/2,
+ 2module#, 0, 0,
+ (0, 0.2module), "top");
+threechars ("u", "upward gluon",
+ path_boson(2module#, 45, 0), boson_wiggles/2,
+ 2module#*(cosd 45), 2module#*(abs sind 45), 0,
+ 0, "ulft");
+threechars ("v", "downward gluon",
+ path_boson(2module#, -45, 0), boson_wiggles/2,
+ 2module#*(cosd -45), 2module#*(abs sind -45), 0,
+ 0, "urt");
+threechars (hex"7D",
+ "vertical gluon",
+ path_boson(2module#, 90, 1), boson_wiggles/2,
+ 0, 2module#, 0,
+ 0, "urt");
+
+% plus or minus phangle, depending on whether n is odd or even
+def pm_angle(expr n) =
+ if odd n: -1 else: 1 fi*phangle
+enddef;
+
+% a gluon quarter loop, in the upper-right quadrant:
+% wid: radius of loop in modules
+% nopts: number of points
+% inv: if 1, the loop goes from the axis to the top;
+% if 0, from the top down to the axis
+% negang: if 1, negate the photon angle (so it wiggles oppositely);
+% if 0, don't
+% taper: if 1 or 3, the line tapers as it approaches the axis
+% if 2 or 3, the line tapers as it approaches the top
+% if 0, the line does not taper
+% (I can't help feeling this macro has become more complicated than it
+% really need be...)
+def path_quadloop (expr wid, nopts, inv, negang, taper) =
+ begingroup
+ save halfwidth, myangle;
+ clearxy;
+ halfwidth# := wid*module#;
+ define_pixels(halfwidth);
+ myangle = if negang <> 0: -phangle else: phangle fi;
+ z0 = (halfwidth, 0); % z0 is on the axis
+ for x = 1 upto nopts:
+ z[x] = z0 rotated (x*90/nopts);
+ endfor
+ if inv = 0:
+ def ::(expr p,ang) = p{p rotated(-90-ang)} looselink enddef;
+ if (taper = 2) or (taper = 3):
+ z[nopts]{dir(0-myangle/2)} .. tension 0.8 and 1 ..
+ else:
+ ::(z[nopts], pm_angle(nopts+negang))
+ fi
+ for x=nopts-1 downto 2:
+ ::(z[x], pm_angle(x+negang))
+ endfor
+ if (taper = 1) or (taper = 3):
+ z[1]{z[1] rotated(-90+myangle)} .. tension 1 and 0.8 .. {dir -(90+myangle/2)}
+ else:
+ ::(z[1], pm_angle(1+negang))
+ fi
+ z0
+ else:
+ def ::(expr p,ang) = looselink {p rotated (90+ang)}p enddef;
+ if (taper = 1) or (taper = 3):
+ z0 {dir (90+myangle/2)} .. tension 0.8 and 1 .. z[1]{z[1] rotated(90-myangle)}
+ else:
+ z0{dir (90+myangle)}
+ ::(z[1], pm_angle(1+negang))
+ fi
+ for x=1 upto nopts-1:
+ ::(z[x], pm_angle(x+negang))
+ endfor
+ if (taper = 2) or (taper = 3):
+ .. tension 0.8 and 1 .. {z[nopts] rotated (90+myangle/2)}z[nopts]
+ else:
+ ::(z[nopts], pm_angle(nopts+negang))
+ fi
+ fi
+ endgroup
+enddef;
+
+% next_odd: evaluate to the next odd number greater than or equal to n
+def next_odd(expr n) =
+ (2*floor(n/2)+1)
+enddef;
+
+threechars ("q", "gluon, quadrant 1",
+ path_quadloop(2, boson_wiggles_loop_normal, 1, 0, 3) xscaled -1 shifted (0,a),
+ next_odd(2boson_wiggles_loop_normal/3),
+ 0, 2module#, 0, % height is 2module#, not 2module#+a#, so that
+ % they line up OK in \Diagram
+ 0, "ulft");
+threechars ("r", "gluon, quadrant 2",
+ path_quadloop(2, boson_wiggles_loop_normal, 0, 0, 3) shifted (0,a),
+ next_odd(boson_wiggles_loop_normal/2),
+ 0, 2module#, 0,
+ 0, "urt");
+threechars ("s", "gluon, quadrant 3",
+ path_quadloop(2, boson_wiggles_loop_normal, 0, 1, 3) yscaled -1 shifted (0,a),
+ next_odd(boson_wiggles_loop_normal/2),
+ 0, a#, a#-2module#,
+ 0, "lrt");
+threechars ("t", "gluon, quadrant 4",
+ path_quadloop(2, boson_wiggles_loop_normal, 1, 1, 3) scaled -1 shifted (0,a),
+ next_odd(2boson_wiggles_loop_normal/3),
+ 0, a#, a#-2module#,
+ 0, "llft");
+
+% The two loops following have the annotation off-centre, to keep
+% it clear of the wiggles. Would it look better being "top" and "bot"
+% again, but with a non-zero offset in the second-last argument?
+% These two are vertically squashed so that they take up less than 2
+% modules vertically, which means in turn that {gl glu} can sit in a
+% two-loop diagram without crashing into the baseline.
+threechars ("y", "gluon half loop",
+ ((path_quadloop(2, boson_wiggles_loop_normal, 1, 0, 1) xscaled -1)
+ .. path_quadloop(2, boson_wiggles_loop_normal, 0, 0, 1)) yscaled squash shifted (0,a),
+ boson_wiggles_loop_normal+1,
+ 0, 2squash*module#+a#, 0,
+ 0, "urt");
+threechars ("z", "gluon half loop, inverted",
+ ((path_quadloop(2, boson_wiggles_loop_normal, 1, 1, 1) scaled -1)
+ .. (path_quadloop(2, boson_wiggles_loop_normal, 0, 1, 1) yscaled -1)) yscaled squash shifted (0,a),
+ boson_wiggles_loop_normal+1,
+ 0, a#, a#-2module#,
+ 0, "llft");
+
+% Small and large boson half loops are at positions which are not characters.
+% That's OK -- they can still be accessed by ligatures.
+% 7b='{', 7c='|'
+threechars (hex"7B", "large gluon half loop",
+ ((path_quadloop(2.66667, boson_wiggles_loop_large, 1, 0, 1) xscaled -1)
+ .. path_quadloop(2.66667, boson_wiggles_loop_large, 0, 0, 1)) shifted (0,a),
+ 1.5boson_wiggles_loop_large,
+ 0, 2.66667module#+a#, 0,
+ 0, "urt");
+threechars (hex"7C", "small gluon half loop",
+ ((path_quadloop(1.3333, boson_wiggles_loop_small, 1, 0, 1) xscaled -1)
+ .. path_quadloop(1.3333, boson_wiggles_loop_small, 0, 0, 1)) shifted (0,a),
+ 1.5boson_wiggles_loop_small,
+ 0, 1.3333module#+a#, 0,
+ 0, "llft");
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Ghosts
+
+ghost_points = 2(boson_wiggles + 1); % should be at least 8?
+def draw_ghost(expr a, b, apos) =
+ begingroup
+ % pattern for ghost_points=10: "-_--_--_--_-"
+ clearxy;
+ z1 = a; z[ghost_points] = b;
+ z2-z1 = z[ghost_points]-z[ghost_points-1];
+ for i = 2 step 2 until (ghost_points-1):
+ z2-z1 = z[i+1]-z[i];
+ endfor
+ z4-z3 = 2(z2-z1);
+ for i = 5 step 2 until (ghost_points-2):
+ z4-z3 = z[i+1]-z[i];
+ endfor
+ pen;
+ draw z1--z2; draw z[ghost_points-1]--z[ghost_points];
+ for i = 3 step 2 until (ghost_points-2):
+ draw z[i]--z[i+1];
+ endfor
+ annotate_at(0.5[z1,z[ghost_points]], apos);
+ endgroup
+enddef;
+% the following is a simpler version of the above, without the
+% ghost_points parameter, to document what it's supposed to be doing
+def draw_ghost_orig(expr a, b, apos) =
+ begingroup
+ clearxy;
+ z1 = a; z10 = b;
+ z4-z3 = 2(z2-z1);
+ z4-z3 = z6-z5 = z8-z7;
+ z2-z1 = z3-z2 = z5-z4 = z7-z6 = z9-z8 = z10-z9;
+ pen;
+ draw z1..z2; draw z3..z4; draw z5..z6; draw z7..z8; draw z9..z10;
+ annotate_at(0.5[z5,z6], apos);
+ endgroup
+enddef;
+
+beginchar ("h", 2module#, 0, 0); "ghost";
+ pen;
+ draw_ghost((0,a), (w,a), "top");
+endchar;
+beginchar (byte "h"-hex"20", 2module#, 0, 0); "r-arrowed ghost";
+ pen;
+ draw_ghost((0,a), (w,a), "top");
+ drawarrow (littlearrow, .5[(0,a), (w,a)], 0);
+endchar;
+beginchar (byte "h"-hex"60", 2module#, 0, 0); "l-arrowed ghost";
+ pen;
+ draw_ghost((0,a), (w,a), "top");
+ drawarrow (littlearrow, .5[(0,a), (w,a)], 180);
+endchar;
+beginchar ("i", 2module#*(cosd 45), 2module#*(sind 45), 0); "upward ghost";
+ pen;
+ draw_ghost((0,a), (w,h+a), "ulft");
+endchar;
+beginchar (byte"i"-hex"20", 2module#*(cosd 45), 2module#*(sind 45), 0);
+ "r-arrowed upward ghost";
+ pen;
+ draw_ghost((0,a), (w,h+a), "ulft");
+ drawarrow (littlearrow, .5[(0,a), (w,h+a)], 45);
+endchar;
+beginchar (byte"i"-hex"60", 2module#*(cosd 45), 2module#*(sind 45), 0);
+ "l-arrowed upward ghost";
+ pen;
+ draw_ghost((0,a), (w,h+a), "ulft");
+ drawarrow (littlearrow, .5[(0,a), (w,h+a)], 45+180);
+endchar;
+beginchar ("j", 2module#*(cosd 45), 2module#*(sind 45), 0);
+ "downward ghost";
+ pen;
+ draw_ghost((0,h+a), (w,a), "urt");
+endchar;
+beginchar (byte"j"-hex"20", 2module#*(cosd 45), 2module#*(sind 45), 0);
+ "r-arrowed downward ghost";
+ pen;
+ draw_ghost((0,h+a), (w,a), "urt");
+ drawarrow (littlearrow, .5[(0,a), (w,h+a)], -45);
+endchar;
+beginchar (byte"j"-hex"60", 2module#*(cosd 45), 2module#*(sind 45), 0);
+ "l-arrowed downward ghost";
+ pen;
+ draw_ghost((0,h+a), (w,a), "urt");
+ drawarrow (littlearrow, .5[(0,a), (w,h+a)], -45+180);
+endchar;
+
+% ghost loops
+begingroup
+ clearxy; clearit; % clear current picture
+ picture ghostloop;
+ pair refpt, arrowpoint;
+ refpt := (-2module, 0);
+ for i=0 upto 23:
+ z[i] = refpt rotated -(i/24*180) yscaled squash shifted (0,a);
+ endfor
+ arrowpoint := z[12];
+ transform below;
+ below := identity rotatedaround ((0,a), 180);
+ pen;
+ for i=0 step 2 until 22:
+ draw z[i]--z[i+1];
+ endfor
+ ghostloop := currentpicture; clearit;
+ beginchar (hex"7E", 0, 2squash*module#+a#, 0); "ghost loop";
+ addto currentpicture also ghostloop;
+ annotate_at(arrowpoint, "top");
+ endchar;
+ beginchar (hex"7E"-hex"20", 0, 2squash*module#+a#, 0); "r-arrowed ghost loop";
+ addto currentpicture also ghostloop;
+ drawarrow(littlearrow, arrowpoint, 0);
+ annotate_at(arrowpoint, "top");
+ endchar;
+ beginchar (hex"7E"-hex"60", 0, 2squash*module#+a#, 0); "l-arrowed ghost loop";
+ addto currentpicture also ghostloop;
+ drawarrow(littlearrow, arrowpoint, 180);
+ annotate_at(arrowpoint, "top");
+ endchar;
+ beginchar (hex"7F", 0, a#, 0); "ghost loop, inverted";
+ addto currentpicture also ghostloop transformed below;
+ annotate_at(arrowpoint transformed below, "top");
+ endchar;
+ beginchar (hex"7F"-hex"20", 0, a#, 0); "r-arrowed ghost loop, inverted";
+ addto currentpicture also ghostloop transformed below;
+ drawarrow(littlearrow, arrowpoint transformed below, 0);
+ annotate_at(arrowpoint transformed below, "top");
+ endchar;
+ beginchar (hex"7F"-hex"60", 0, a#, 0); "l-arrowed ghost loop, inverted";
+ addto currentpicture also ghostloop transformed below;
+ drawarrow(littlearrow, arrowpoint transformed below, 180);
+ annotate_at(arrowpoint transformed below, "top");
+ endchar;
+endgroup;
+
+beginchar (hex"22", module#, 0, 0); "short ghost";
+ pen;
+ z1 = (0,a); z6 = (w,a);
+ z4-z3 = 2(z2-z1);
+ z2-z1 = z3-z2 = 1/2(z4-z3) = z5-z4 = z6-z5;
+ draw z1..z2; draw z3..z4; draw z5..z6;
+ annotate_at(0.5[z3,z4], "top")
+endchar;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Odds and sods
+
+beginchar (hex"60", 2module#, 0, 0);
+ "spacer";
+endchar;
+
+beginchar (hex"40", module#, 0, 0);
+ "short spacer";
+endchar;
+
+beginchar ("c", 2blobr#, blobr# + a#, blobr#); "complete vertex";
+ pen;
+ draw (0,a) .. (w,a) .. cycle;
+ annotate_at((w/2,w/2+a), "top");
+endchar;
+
+%beginchar ("d", module#/2, 0, 0); "weeny fermion";
+% pen;
+% draw (0,a)..(w,a);
+%endchar;
+%
+%beginchar ("k", module#/2, 0, 0); "weeny massive fermion";
+% pen;
+% y1 - a = y2 - a = a - y3 = a - y4 = module#/3.5;
+% x1 = x3 = 0;
+% x2 = x4 = w;
+% draw z1--z2;
+% draw z3--z4;
+%endchar;
+%
+%beginchar ("l", module#, 0, 0); "short massive fermion";
+% pen;
+% y1 - a = y2 - a = a - y3 = a - y4 = module#/3.5;
+% x1 = x3 = 0;
+% x2 = x4 = w;
+% draw z1--z2;
+% draw z3--z4;
+%endchar;
+
+%beginchar ("n", 4module#, 2module#-a#, 2module#-a#);
+% pen; "fermion loop";
+% z0 = (0,a); z1 = (w,a);
+% draw z0{dir phangle} ..tension 1.15.. {dir -phangle}z1;
+% draw z0{dir -phangle} ..tension 1.15.. {dir phangle}z1;
+%endchar;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Other symbols
+
+garrow("a", 0, 1);
+garrow(hex"03", 180, 1);
+
+beginchar ("p", 2blobr#, blobr# + a#, blobr# - a#);
+ pen; "proper vertex";
+ picture p[];
+ pair t[];
+ fill (-blobr, 0) .. (blobr, 0) .. cycle;
+ p1 := currentpicture; clearit;
+ draw (-blobr, 0) .. (blobr, 0) .. cycle;
+ p2 := currentpicture; clearit;
+ t1 = 5/6[origin, (0,blobr)] shifted (-blobr, 0);
+ t7 = 5/6[origin, (0,blobr)] shifted (+blobr, 0);
+ forsuffixes s = 2,3,4,5,6 :
+ t[s] = t[s-1] shifted (0, -blobr/3);
+ t[s+6] = t[s+5] shifted (0, -blobr/3);
+ endfor
+ forsuffixes s = 1,2,3,4,5,6,7,8,9,10,11,12 :
+ z[s] = t[s] rotated 45;
+ endfor
+ forsuffixes s = 1,2,3,4,5,6 :
+ draw z[s] -- z[s+6];
+ endfor
+ addto currentpicture also p1;
+ cull currentpicture keeping (2,2); % & the two pictures
+ addto currentpicture also p2;
+ currentpicture := currentpicture shifted (blobr,a);
+ labels (range 1 thru 12);
+ annotate_at((blobr,blobr+a), "top");
+endchar;
+
+beginchar ("P", 2blobr#, blobr#+a#, blobr#-a#); % see complete vertex "c"
+ pen; "proper vertex 2";
+ fill (0,a) .. (w,a) .. cycle;% withcolor black;
+ annotate_at((w/2,w/2+a), "top");
+endchar;
+
+beginchar ("x", 0, a#, -a#); "counterterm";
+ pen;
+ -x1 = -x2 = x3 = x4;
+ y1 = -y2 = y3 = -y4;
+ z3 = (2blobr/3, 0) rotated 45;
+ draw z1 .. z4;
+ draw z2 .. z3;
+ currentpicture := currentpicture shifted (0,a);
+ annotate_at(0.5[z1,z3] shifted (0,a), "top");
+endchar;
+
+beginchar (hex"20", module#, a#, 0); "space block";
+ pen;
+ message "spacer: width=" & decimal w & ", height=" & decimal h;
+ draw origin--(w,h);
+ draw (0,h)--(w,0);
+endchar;
+
+
+% Now generate an errorchar() in each of the code positions we've skipped
+for n = 0 upto hex"7F":
+ if not known(donechar[n]):
+ beginchar (n, 0, module#, 0); errorchar(h); endchar;
+ fi
+endfor
+
+
+% Construct the ligature tables.
+ligtable "a" : % arrow
+ "A" =: "a", % right-arrow, for symmetry
+ "V" =: hex"03"; % left-arrow
+
+ligtable "f" : % fermion
+"A" =: "F", % right-arrowed
+"V" =: hex"06", % left-arrowed
+"u" =: "e", % upward
+"d" =: "d", % downward
+"v" =: "b", % vertical
+"s" =: hex"21", % short
+"l" =: "l", % loop
+"0" =: hex"60"; % spacer
+
+ligtable "e" : % upward fermion, fe
+"A" =: "E", % arrowed
+"V" =: hex"05";
+
+ligtable "d" : % downward fermion, fd
+"A" =: "D",
+"V" =: hex"04";
+
+ligtable "b" : % vertical fermion, fv
+"A" =: "B",
+"V" =: hex"02";
+
+ligtable hex"21" : % short fermion
+"0" =: hex"40"; % short spacer
+
+ligtable "k" : % fermion closed loop
+"A" =: "K", % arrowed
+"V" =: hex"0b";
+
+ligtable "l" : % fermion loop
+"u" =: "n", % upside down
+"o" =: "k", % closed loop (for scalar theory)
+"S" =: "o", % small
+"A" =: "L",
+"V" =: hex"0C";
+
+ligtable "g" : % gluon
+"A" =: "G",
+"V" =: hex"07",
+"u" =: "u",
+"d" =: "v",
+"v" =: oct"175",
+"l" =: "y", % gluon half-loop
+"1" =: "q", % gluon quarter-loop, 1st quadrant (upper-left)
+"2" =: "r", % 2nd
+"3" =: "s", % 3rd
+"4" =: "t"; % 4th
+
+ligtable "m" : % massive fermion
+"A" =: "M",
+"V" =: hex"0D",
+"s" =: hex"23";
+
+ligtable "n" : % inverted fermion
+"A" =: "N",
+"V" =: hex"0E";
+
+ligtable "o" : % fermion loop small
+"u" =: "w", % upside down
+"A" =: "O",
+"V" =: hex"0F";
+
+ligtable "q" : % gluon, 1st quadrant
+"A" =: "Q",
+"V" =: hex"11";
+
+ligtable "r" : % gluon, 2nd quadrant
+"A" =: "R",
+"V" =: hex"12";
+
+ligtable "s" : % gluon, 3rd quadrant
+"A" =: "S",
+"V" =: hex"13";
+
+ligtable "t" : % gluon, 4th quadrant
+"A" =: "T",
+"V" =: hex"14";
+
+ligtable "u" : % upward gluon
+"A" =: "U",
+"V" =: hex"15";
+
+ligtable "v" : % downward gluon
+"A" =: "V",
+"V" =: hex"16";
+
+ligtable "w" : % fermion loop, small, upside-down
+"A" =: "W",
+"V" =: oct"027";
+
+ligtable oct"175" : % vertical gluon
+"A" =: oct"135",
+"V" =: oct"035";
+
+ligtable "y" : % gluon half-loop
+"A" =: "Y",
+"V" =: hex"19",
+"B" =: hex"7B", % large
+"S" =: hex"7C", % small
+"u" =: "z"; % ...upside down
+
+ligtable hex"7B" : % large gluon half loop
+"A" =: hex"5B",
+"V" =: hex"1B";
+
+ligtable hex"7C" : % small gluon half loop
+"A" =: hex"5C",
+"V" =: hex"1C";
+
+ligtable "z" : % upside-down gluon loop
+"A" =: "Z",
+"V" =: hex"1A";
+
+ligtable "h" : % ghost
+"A" =: "H",
+"V" =: hex"08",
+"u" =: "i", % upward
+"d" =: "j", % downward
+"s" =: hex"22", % short
+"l" =: hex"7E"; % ghost loop
+
+ligtable hex"7E" : % ghost loop
+"A" =: hex"5E", % r-arrowed
+"V" =: hex"1E", % l-arrowed
+"u" =: hex"7F"; % upside down
+
+ligtable hex"7F" : % ghost loop inverted
+"A" =: hex"5F", % r-arrowed
+"V" =: hex"1F"; % l-arrowed
+
+ligtable "i" : % upward ghost
+"A" =: "I",
+"V" =: hex"09";
+
+ligtable "j" : % downward ghost
+"A" =: "J",
+"V" =: hex"0A";