/*****************************************************************************
* feynman.asy -- An Asymptote library for drawing Feynman diagrams. *
* *
* by: Martin Wiebusch <
[email protected]> *
* last change: 2007/04/13 *
*****************************************************************************/
/* default parameters ********************************************************/
// default ratio of width (distance between two loops) to amplitude for a gluon
// line. The gluon function uses this ratio, if the width parameter is
// negative.
real gluonratio;
// default ratio of width (distance between two crests) to amplitude for a
// photon line. The photon function uses this ratio, if the width parameter is
// negative.
real photonratio;
// default gluon amplitude
real gluonamplitude;
// default photon amplitude
real photonamplitude;
// default pen for drawing the background. Usually white.
pen backgroundpen;
// default pen for drawing gluon lines
pen gluonpen;
// default pen for drawing photon lines
pen photonpen;
// default pen for drawing fermion lines
pen fermionpen;
// default pen for drawing scalar lines
pen scalarpen;
// default pen for drawing ghost lines
pen ghostpen;
// default pen for drawing double lines
pen doublelinepen;
// default pen for drawing vertices
pen vertexpen;
// default pen for drawing big vertices (drawVertexOX and drawVertexBoxX)
pen bigvertexpen;
// inner spacing of a double line
real doublelinespacing;
// default arrow for propagators
arrowbar currentarrow;
// if true, each of the drawSomething commands blots out the background
// (with pen backgroundpen) before drawing.
bool overpaint;
// margin around lines. If one line is drawn over anoter, a white margin
// of size linemargin is kept around the top one.
real linemargin;
// at vertices, where many lines join, the last line drawn should not blot
// out the others. By not erasing the background near the ends of lines,
// this is prevented for lines with an angle greater than minvertexangle to
// each other. Note, that small values for minvertexangle mean that the
// background is only erased behind a small segment of every line. Setting
// minvertexangle = 0 effectively disables background erasing for lines.
real minvertexangle;
// size (radius) of vertices
real vertexsize;
// size (radius) of big vertices (drawVertexOX and drawVertexBoxX)
real bigvertexsize;
/* defaults for momentum arrows **********************************************/
// (momentum arrows are small arrows parallel to particle lines indicating the
// direction of momentum)
// default size of the arrowhead of momentum arrows
arrowbar currentmomarrow;
// default length of momentum arrows
real momarrowlength;
// default pen for momentum arrows
pen momarrowpen;
// default offset between momentum arrow and related particle line
real momarrowoffset;
// default margin for momentum arrows
real momarrowmargin;
// factor for determining the size of momentum arrowheads. After changing it,
// you still have to update currentmomarrow manually.
real momarrowfactor;
// size function for momentum arrowheads
real momarrowsize(pen p=momarrowpen) { return momarrowfactor*linewidth(p); }
/* defaults for texshipout ***************************************************/
// tex command for including graphics. It takes one argument, which is the
// name of the graphics (eps or pdf) file.
string includegraphicscommand;
// Determines whether the suffix (.eps or .pdf) should be appended to the stem
// of the file name in the \includegraphics command.
bool appendsuffix;
/* helper functions **********************************************************/
// internal function for overpainting
private void do_overpaint(picture pic, path p, pen bgpen,
real halfwidth, real vertexangle)
{
real tanvertexangle = tan(vertexangle*pi/180);
if(tanvertexangle != 0) {
real t1 = arctime(p, halfwidth/tanvertexangle+halfwidth);
real t2 = arctime(p, arclength(p)-halfwidth/tanvertexangle-halfwidth);
draw(pic, subpath(p, t1, t2),
bgpen+linewidth(2*halfwidth));
}
}
// returns the path of a gluon line along path p, with amplitude amp and width
// width (distance between two loops). If width is negative, the width is
// set to amp*gluonratio
path gluon(path p, real amp = gluonamplitude, real width=-1)
{
if(width < 0) width = abs(gluonratio*amp);
real pathlen = arclength(p);
int ncurls = floor(pathlen/width);
real firstlen = (pathlen - width*(ncurls-1))/2;
real firstt = arctime(p, firstlen);
pair firstv = dir(p, firstt);
guide g = point(p, 0)..{firstv}( point(p, firstt)
+amp*unit(rotate(90)*firstv));
real t1;
pair v1;
real t2;
pair v2;
pathlen -= firstlen;
for(real len = firstlen+width/2; len < pathlen; len += width) {
t1 = arctime(p, len);
v1 = dir(p, t1);
t2 = arctime(p, len + width/2);
v2 = dir(p, t2);
g=g..{-v1}(point(p, t1)+amp*unit(rotate(-90)*v1))
..{+v2}(point(p, t2)+amp*unit(rotate(+90)*v2));
}
g = g..point(p, size(p));
return g;
}
// returns the path of a photon line along path p, with amplitude amp and width
// width (distance between two crests). If width is negative, the width is
// set to amp*photonratio
path photon(path p, real amp = photonamplitude, real width=-1)
{
if(width < 0)
width = abs(photonratio*amp)/2;
else
width = width/2;
real pathlen = arclength(p);
int ncurls = floor(pathlen/width);
real firstlen = (pathlen - width*ncurls)/2;
real firstt = arctime(p, firstlen+width);
guide g = point(p, 0){unit(point(p, firstt)-point(p, 0))};
real t;
pair v;
pathlen -= firstlen;
for(real len = firstlen+width; len < pathlen; len += width) {
t = arctime(p, len);
v = dir(p, t);
g=g..{v}(point(p, t)+amp*unit(rotate(90)*v));
amp = -amp;
}
g = g..{unit(point(p, size(p))-point(p, t))}point(p, size(p));
return g;
}
// returns the path of a momentum arrow along path p, with length length,
// an offset offset from the path p and at position position. position will
// usually be one of the predefined pairs left or right. Making adjust
// nonzero shifts the momentum arrow along the path.
path momArrowPath(path p,
align align,
position pos,
real offset = momarrowoffset,
real length = momarrowlength)
{
real pathlen = arclength(p);
real t1, t2;
if(pos.relative) {
t1 = arctime(p, (pathlen-length)*pos.position.x);
t2 = arctime(p, (pathlen-length)*pos.position.x+length);
} else {
t1 = arctime(p, (pathlen-length)/2 + pos.position.x);
t2 = arctime(p, (pathlen+length)/2+ pos.position.x);
}
pair v1 = dir(p, t1);
pair v2 = dir(p, t2);
pair p1, p2;
if(align.relative) {
p1 = point(p, t1) + offset*abs(align.dir)
*unit(rotate(degrees(align.dir)-90)*v1);
p2 = point(p, t2) + offset*abs(align.dir)
*unit(rotate(degrees(align.dir)-90)*v2);
} else {
p1 = point(p, t1) + offset*align.dir;
p2 = point(p, t2) + offset*align.dir;
}
return p1{v1}..{v2}p2;
}
/* drawing functions *********************************************************/
// draw a gluon line on picture pic, along path p, with amplitude amp, width
// width (distance between loops) and with pen fgpen. If erasebg is true,
// bgpen is used to erase the background behind the line and at a margin
// margin around it. The background is not erased at a certain distance to
// the endpoints, which is determined by vertexangle (see comments to the
// default parameter minvertexangle). For negative values of width, the width
// is set to gluonratio*amp.
void drawGluon(picture pic = currentpicture,
path p,
real amp = gluonamplitude,
real width = -1,
pen fgpen = gluonpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real vertexangle = minvertexangle,
real margin = linemargin)
{
if(width < 0) width = abs(2*amp);
if(erasebg) do_overpaint(pic, p, bgpen, amp+margin, vertexangle);
draw(pic, gluon(p, amp, width), fgpen);
}
// draw a photon line on picture pic, along path p, with amplitude amp, width
// width (distance between loops) and with pen fgpen. If erasebg is true,
// bgpen is used to erase the background behind the line and at a margin
// margin around it. The background is not erased at a certain distance to
// the endpoints, which is determined by vertexangle (see comments to the
// default parameter minvertexangle). For negative values of width, the width
// is set to photonratio*amp.
void drawPhoton(picture pic = currentpicture,
path p,
real amp = photonamplitude,
real width = -1,
pen fgpen = currentpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real vertexangle = minvertexangle,
real margin = linemargin)
{
if(width < 0) width = abs(4*amp);
if(erasebg) do_overpaint(pic, p, bgpen, amp+margin, vertexangle);
draw(pic, photon(p, amp, width), fgpen);
}
// draw a fermion line on picture pic, along path p with pen fgpen and an
// arrowhead arrow. If erasebg is true, bgpen is used to erase the background
// at a margin margin around the line. The background is not erased at a
// certain distance to the endpoints, which is determined by vertexangle
// (see comments to the default parameter minvertexangle).
void drawFermion(picture pic = currentpicture,
path p,
pen fgpen = currentpen,
arrowbar arrow = currentarrow,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real vertexangle = minvertexangle,
real margin = linemargin)
{
if(erasebg) do_overpaint(pic, p, bgpen,
linewidth(fgpen)+margin, vertexangle);
draw(pic, p, fgpen, arrow);
}
// draw a scalar line on picture pic, along path p with pen fgpen and an
// arrowhead arrow. If erasebg is true, bgpen is used to erase the background
// at a margin margin around the line. The background is not erased at a
// certain distance to the endpoints, which is determined by vertexangle
// (see comments to the default parameter minvertexangle).
void drawScalar(picture pic = currentpicture,
path p,
pen fgpen = scalarpen,
arrowbar arrow = currentarrow,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real vertexangle = minvertexangle,
real margin = linemargin)
{
if(erasebg) do_overpaint(pic, p, bgpen,
linewidth(fgpen)+margin, vertexangle);
draw(pic, p, fgpen, arrow);
}
// draw a ghost line on picture pic, along path p with pen fgpen and an
// arrowhead arrow. If erasebg is true, bgpen is used to erase the background
// at a margin margin around the line. The background is not erased at a
// certain distance to the endpoints, which is determined by vertexangle
// (see comments to the default parameter minvertexangle).
void drawGhost(picture pic = currentpicture,
path p,
pen fgpen = ghostpen,
arrowbar arrow = currentarrow,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real vertexangle = minvertexangle,
real margin = linemargin)
{
if(erasebg) do_overpaint(pic, p, bgpen,
linewidth(fgpen)+margin, vertexangle);
draw(pic, p, fgpen, arrow);
}
arrowbar DoubleLineMidArrow=MidArrow(Fill(doublelinepen));
// draw a double line on picture pic, along path p with pen fgpen and
// an inner spacing of dlspacint. An optional arrowhead DoubleLineMidArrow
// can be specified. If erasebg is true, bgpen is used to erase the
// background at a margin margin around the line. The background is
// not erased at a certain distance to the endpoints, which is
// determined by vertexangle (see comments to the default parameter
// minvertexangle).
void drawDoubleLine(picture pic = currentpicture,
path p,
pen fgpen = doublelinepen,
real dlspacing = doublelinespacing,
arrowbar arrow = None,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real vertexangle = minvertexangle,
real margin = linemargin)
{
if(erasebg) do_overpaint(pic, p, bgpen,
linewidth(fgpen)+margin, vertexangle);
real htw = linewidth(fgpen)+dlspacing/2;
draw(pic, p, fgpen+2*htw);
draw(pic, p, bgpen+(linewidth(dlspacing)));
path rect = (-htw,-htw)--(-htw,htw)--(0,htw)--(0,-htw)--cycle;
fill(shift(point(p,0))*rotate(degrees(dir(p,0)))*rect, bgpen);
fill(shift(point(p,size(p)))*scale(-1)*rotate(degrees(dir(p,size(p))))*
rect,bgpen);
draw(pic, p, invisible, arrow);
}
// draw a vertex dot on picture pic, at position xy with radius r and pen
// fgpen
void drawVertex(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen)
{
fill(pic, circle(xy, r), fgpen);
}
// draw an empty vertex dot on picture pic, at position xy with radius r
// and pen fgpen. If erasebg is true, the background is erased in the inside
// of the circle.
void drawVertexO(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen)
{
if(erasebg)
filldraw(pic, circle(xy, r), bgpen, fgpen);
else
draw(pic, circle(xy, r), fgpen);
}
// draw a vertex triangle on picture pic, at position xy with radius r and pen
// fgpen
void drawVertexTriangle(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen)
{
real cospi6 = cos(pi/6);
real sinpi6 = sin(pi/6);
path triangle = (cospi6,-sinpi6)--(0,1)--(-cospi6,-sinpi6)--cycle;
fill(pic, shift(xy)*scale(r)*triangle, fgpen);
}
// draw an empty vertex triangle on picture pic, at position xy with size r
// and pen fgpen. If erasebg is true, the background is erased in the inside
// of the triangle.
void drawVertexTriangleO(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen)
{
real cospi6 = cos(pi/6);
real sinpi6 = sin(pi/6);
path triangle = (cospi6,-sinpi6)--(0,1)--(-cospi6,-sinpi6)--cycle;
if(erasebg)
filldraw(pic, shift(xy)*scale(r)*triangle, bgpen, fgpen);
else
draw(pic, shift(xy)*scale(r)*triangle, fgpen);
}
// draw a vertex box on picture pic, at position xy with radius r and pen
// fgpen
void drawVertexBox(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen)
{
path box = (1,1)--(-1,1)--(-1,-1)--(1,-1)--cycle;
fill(pic, shift(xy)*scale(r)*box, fgpen);
}
// draw an empty vertex box on picture pic, at position xy with size r
// and pen fgpen. If erasebg is true, the background is erased in the inside
// of the box.
void drawVertexBoxO(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen)
{
path box = (1,1)--(-1,1)--(-1,-1)--(1,-1)--cycle;
if(erasebg)
filldraw(pic, shift(xy)*scale(r)*box, bgpen, fgpen);
else
draw(pic, shift(xy)*scale(r)*box, fgpen);
}
// draw an X on picture pic, at position xy with size r and pen
// fgpen
void drawVertexX(picture pic = currentpicture,
pair xy,
real r = vertexsize,
pen fgpen = vertexpen)
{
draw(pic, shift(xy)*scale(r)*((-1,-1)--(1,1)), fgpen);
draw(pic, shift(xy)*scale(r)*((1,-1)--(-1,1)), fgpen);
}
// draw a circle with an X in the middle on picture pic, at position xy with
// size r and pen fgpen. If erasebg is true, the background is erased in the
// inside of the circle.
void drawVertexOX(picture pic = currentpicture,
pair xy,
real r = bigvertexsize,
pen fgpen = vertexpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen)
{
if(erasebg)
filldraw(pic, circle(xy, r), bgpen, fgpen);
else
draw(pic, circle(xy, r), fgpen);
draw(pic, shift(xy)*scale(r)*(NW--SE), fgpen);
draw(pic, shift(xy)*scale(r)*(SW--NE), fgpen);
}
// draw a box with an X in the middle on picture pic, at position xy with
// size r and pen fgpen. If erasebg is true, the background is erased in the
// inside of the box.
void drawVertexBoxX(picture pic = currentpicture,
pair xy,
real r = bigvertexsize,
pen fgpen = vertexpen,
bool erasebg = overpaint,
pen bgpen = backgroundpen)
{
path box = (1,1)--(-1,1)--(-1,-1)--(1,-1)--cycle;
box = shift(xy)*scale(r)*box;
if(erasebg)
filldraw(pic, box, bgpen, fgpen);
else
draw(pic, box, fgpen);
draw(pic, shift(xy)*scale(r)*((-1,-1)--(1,1)), fgpen);
draw(pic, shift(xy)*scale(r)*((1,-1)--(-1,1)), fgpen);
}
// draw a momentum arrow on picture pic, along path p, at position position
// (use one of the predefined pairs left or right), with an offset offset
// from the path, a length length, a pen fgpen and an arrowhead arrow. Making
// adjust nonzero shifts the momentum arrow along the path. If erasebg is true,
// the background is erased inside a margin margin around the momentum arrow.
// Make sure that offset and margin are chosen in such a way that the momentum
// arrow does not overdraw the particle line.
void drawMomArrow(picture pic = currentpicture,
path p,
align align,
position pos = MidPoint,
real offset = momarrowoffset,
real length = momarrowlength,
pen fgpen = momarrowpen,
arrowbar arrow = currentmomarrow,
bool erasebg = overpaint,
pen bgpen = backgroundpen,
real margin = momarrowmargin)
{
path momarrow = momArrowPath(p, align, pos, offset, length);
if(erasebg) do_overpaint(pic, momarrow, bgpen,
linewidth(fgpen)+margin, 90);
draw(pic, momarrow, fgpen, arrow);
}
/* initialisation ************************************************************/
// The function fmdefaults() tries to guess reasonable values for the
// default parameters above by looking at the default parameters of plain.asy
// (essentially, currentpen, arrowfactor and dotfactor). After customising the
// default parameters of plain.asy, you may call fmdefaults to adjust the
// parameters of feynman.asy.
void fmdefaults()
{
real arrowsize=arrowsize(currentpen);
real linewidth=linewidth(currentpen);
gluonratio = 2;
photonratio = 4;
gluonamplitude = arrowsize/3;
photonamplitude = arrowsize/4;
backgroundpen = white;
gluonpen = currentpen;
photonpen = currentpen;
fermionpen = currentpen;
scalarpen = dashed+linewidth;
ghostpen = dotted+linewidth;
doublelinepen = currentpen;
vertexpen = currentpen;
bigvertexpen = currentpen;
currentarrow = MidArrow;
doublelinespacing = 2*linewidth;
linemargin = 0.5*arrowsize;
minvertexangle = 30;
overpaint = true;
vertexsize = 0.5*dotfactor*linewidth;
bigvertexsize = 0.4*arrowsize;
momarrowfactor = 1.5*arrowfactor;
momarrowlength = 2.5*arrowsize;
momarrowpen = currentpen+0.5*linewidth;
momarrowoffset = 0.8*arrowsize;
momarrowmargin = 0.25*arrowsize;
currentmomarrow = EndArrow(momarrowsize());
includegraphicscommand = "\includegraphics";
appendsuffix = false;
}
// We call fmdefaults once, when the module is loaded.
fmdefaults();
/* shipout *******************************************************************/
bool YAlign = false;
bool XYAlign = true;
// texshipout("filename", pic) creates two files: filename.eps holding the
// picture pic and filename.tex holding some LaTeX code that includes the
// picture from filename.eps and shifts it vertically in such a way that the
// point (0,0) lies on the baseline.
void texshipout(string stem,
picture pic = currentpicture,
bool xalign = YAlign)
{
file tf = output(stem + ".tex");
pair min=pic.min();
real depth = min.y;
real xoffset = min.x;
if(xalign) {
write(tf, "\makebox[0pt][l]{\kern");
write(tf, xoffset);
write(tf, "bp\relax");
}
write(tf, "\raisebox{");
write(tf, depth);
write(tf, "bp}{"+includegraphicscommand+"{");
write(tf, stem);
string suffix="."+nativeformat();
if(appendsuffix)
write(tf, suffix);
write(tf, "}}");
if(xalign)
write(tf, "}");
close(tf);
shipout(stem+suffix, pic);
}