"""Various particle line types."""
import pyx
from pyx import color
from pyfeyn2.render.pyx import config
from pyfeyn2.render.pyx.deco import Arrow, LineLabel, ParallelArrow
from pyfeyn2.render.pyx.diagrams import FeynDiagram
from pyfeyn2.render.pyx.paint import CENTER
from pyfeyn2.render.pyx.points import Point
from pyfeyn2.render.pyx.utils import Visible, defunit
## Line base class
[docs]class Line(Visible):
"Base class for all objects which connect points in Feynman diagrams"
[docs] def __init__(
self,
point1,
point2,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
self.p1 = point1
self.p2 = point2
self.styles = styles if styles is not None else []
self.arcthrupoint = arcthrupoint
self.is3D = is3D
self.arrows = arrows if arrows is not None else []
self.labels = labels if labels is not None else []
## Add this to the current diagram automatically
FeynDiagram.currentDiagram.add(self)
[docs] def addLabel(
self,
text,
pos=0.5,
displace=-0.25,
angle=0,
size=pyx.text.size.normalsize,
halign=CENTER,
valign=None,
**kwargs
):
"""Add a LaTeX label to this line, either via parameters or actually as
a TeXLabel object."""
if text is None:
return self
if config.getOptions().DEBUG:
print("Adding label: " + text)
# if text.__class__ == "Label":
# self.labels.append(label)
# else:
self.labels.append(
LineLabel(
text=text,
line=self,
pos=pos,
displace=displace,
angle=angle,
size=size,
halign=halign,
valign=valign,
)
)
if config.getOptions().DEBUG:
print("Labels = " + str(self.labels))
return self
[docs] def addParallelArrow(
self,
pos=0.5,
displace=0.3,
length=0.5 * pyx.unit.v_cm,
size=6 * pyx.unit.v_pt,
angle=45,
constriction=0.8,
sense=+1,
curved=False,
stems=1,
stemsep=0.03,
):
"""Add an arrow pointing along the line."""
self.labels.append(
ParallelArrow(
self,
pos=pos,
displace=displace,
length=length,
size=size,
angle=angle,
constriction=constriction,
sense=sense,
curved=curved,
stems=stems,
stemsep=stemsep,
)
)
return self
[docs] def removeLabels(self):
"""Remove the labels from this line."""
self.labels = []
return self
[docs] def fracpoint(self, frac):
"""
Get a new Point representing the point at the given fraction along
the fundamental line (i.e. no truncation or deformation).
TODO: Handle units properly.
"""
p = self.getPath() ## no truncation or deformation
x, y = p.at(p.begin() + frac * p.arclen())
return Point(x / defunit, y / defunit)
[docs] def setArrows(self, arrows):
"""Define the arrows on this line."""
## TODO: Check that the arg is a list
self.arrows = []
for i in arrows:
if i.__class__ == "deco.Arrow":
self.arrows.append(i)
else:
self.arrows.append(Arrow(pos=i))
return self
[docs] def addArrow(self, position=0.53, arrow=None):
"""Add an arrow to the line at the specified position, which is a number
between 0 and 1, representing the fraction along the line at which the
arrow should be placed. The default arrow style can be overridden by
explicitly supplying an arrow object as the 'arrow' argument, in which
case the position argument will be ignored."""
if arrow:
self.arrows.append(arrow)
else:
self.arrows.append(Arrow(pos=position))
return self
[docs] def removeArrows(self):
"""Remove all arrows from this line."""
self.arrows = []
return self
[docs] def arcThru(self, arcpoint=None, x=None, y=None):
"""Set the point through which this line will arc. Either pass a Point
or set x, y as floats."""
if arcpoint is not None:
self.arcthrupoint = arcpoint
elif x is not None and y is not None:
self.arcthrupoint = Point(x, y)
else:
raise Exception("Tried to set an arcpoint with invalid arguments")
return self
[docs] def straighten(self):
"""Make this line a straight line between start and end."""
self.arcthrupoint = None
return self
[docs] def bend(self, amount):
"""Bend the line to the right by a given distance."""
if amount is None:
return self
if amount == 0:
self.arcthrupoint = None
return self
middle = self.p1.midpoint(self.p2)
nx = (middle.y() - self.p1.y()) / abs(self.p1.distance(middle))
ny = (self.p1.x() - middle.x()) / abs(self.p1.distance(middle))
vx = middle.x() - self.p1.x()
vy = middle.y() - self.p1.y()
if (vx * ny - vy * nx) > 0:
nx *= -1
ny *= -1
arcpoint = Point(middle.x() + amount * nx, middle.y() + amount * ny)
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(
pyx.path.line(middle.x(), middle.y(), arcpoint.x(), arcpoint.y()),
[color.rgb.blue],
)
self.arcThru(arcpoint)
if config.getOptions().DEBUG:
print(self.getVisiblePath())
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(
self.getVisiblePath(), [color.rgb.blue]
)
return self
[docs] def set3D(self, is3d):
"""Make this line display in '3D'."""
self.is3D = is3d
return self
[docs] def getStyles(self, stylelist):
"""Get the styles associated with this line."""
return self.styles
[docs] def setStyles(self, stylelist):
"""Set the styles associated with this line."""
self.styles = stylelist
return self
[docs] def addStyle(self, style):
"""Add a style to this line."""
self.styles.append(style)
return self
[docs] def addStyles(self, stylelist):
"""Add some styles to this line."""
self.styles = self.styles + stylelist
return self
[docs] def getPath(self):
"""Get the path taken by this line."""
if self.arcthrupoint is None:
## This is a simple straight line
return pyx.path.path(
pyx.path.moveto(*(self.p1.getXY())), pyx.path.lineto(*(self.p2.getXY()))
)
elif self.p1.x() == self.p2.x() and self.p1.y() == self.p2.y():
## This is a tadpole-type loop and needs special care;
## We shall assume that the arcthrupoint is meant to be
## the antipode of the basepoint
arccenter = self.p1.midpoint(self.arcthrupoint)
arcradius = self.p1.distance(self.arcthrupoint) / 2.0
## TODO Why does a circle work and an arc doesn't?
cargs = (arccenter.x(), arccenter.y(), arcradius)
circle = pyx.path.circle(*cargs)
line = pyx.path.line(self.p1.x(), self.p1.y(), arccenter.x(), arccenter.y())
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(line, [color.rgb.green])
ass, _ = circle.intersect(line)
subpaths = circle.split(ass[0])
cpath = subpaths[0]
return cpath
## or, with an arc...
# arcangle1 = arccenter.arg(self.p1)
# arcangle2 = arccenter.arg(self.p1) + 360
# arcargs = (arccenter.x(), arccenter.y(), arcradius, arcangle1, arcangle2)
# return pyx.path.path(pyx.path.arc(*arcargs))
else:
n13, n23 = None, None
## Work out line gradients
try:
n13 = (self.p1.y() - self.arcthrupoint.y()) / (
self.p1.x() - self.arcthrupoint.x()
)
except ZeroDivisionError:
if config.getOptions().DEBUG:
print("Grad 1 diverges")
n13 = 1e100
try:
n23 = (self.p2.y() - self.arcthrupoint.y()) / (
self.p2.x() - self.arcthrupoint.x()
)
except ZeroDivisionError:
if config.getOptions().DEBUG:
print("Grad 2 diverges")
n23 = 1e100
## If gradients match,
## then we have a straight line, so bypass the complexity
if n13 == n23:
return pyx.path.path(
pyx.path.moveto(*(self.p1.getXY())),
pyx.path.lineto(*(self.p2.getXY())),
)
## Otherwise work out conjugate gradients and midpoints
m13, m23 = None, None
try:
m13 = -1.0 / n13
except ZeroDivisionError:
m13 = 1e100
try:
m23 = -1.0 / n23
except ZeroDivisionError:
m23 = 1e100
mid13 = self.p1.midpoint(self.arcthrupoint)
mid23 = self.p2.midpoint(self.arcthrupoint)
## Line y-intercepts
c13 = mid13.y() - m13 * mid13.x()
c23 = mid23.y() - m23 * mid23.x()
## Find the centre of the arc
xcenter = -(c23 - c13) / (m23 - m13)
ycenter = m13 * xcenter + c13
arccenter = Point(xcenter, ycenter)
## Get the angles required for drawing the arc
arcradius = arccenter.distance(self.arcthrupoint)
arcangle1 = arccenter.arg(self.p1)
arcangle2 = arccenter.arg(self.p2)
# arcangle3 = arccenter.arg(self.arcthrupoint)
arcargs = (arccenter.x(), arccenter.y(), arcradius, arcangle1, arcangle2)
if config.getOptions().DEBUG and arcangle1 == arcangle2:
print("Arc angles are the same - not drawing anything")
## Calculate cross product to determine direction of arc
vec12 = [self.p2.x() - self.p1.x(), self.p2.y() - self.p1.y(), 0.0]
vec13 = [
self.arcthrupoint.x() - self.p1.x(),
self.arcthrupoint.y() - self.p1.y(),
0.0,
]
crossproductZcoord = vec12[0] * vec13[1] - vec12[1] * vec13[0]
if crossproductZcoord < 0:
return pyx.path.path(
pyx.path.moveto(*(self.p1.getXY())), pyx.path.arc(*arcargs)
)
else:
return pyx.path.path(
pyx.path.moveto(*(self.p1.getXY())), pyx.path.arcn(*arcargs)
)
[docs] def getVisiblePath(self):
"""Find the subpath between the endpoints which isn't overshadowed by a blob of some kind"""
p1path = self.p1.getPath()
p2path = self.p2.getPath()
vispath = self.getPath()
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(vispath, [color.rgb.green])
if p1path:
ass, bs = p1path.intersect(vispath)
for b in bs:
subpaths = vispath.split(b)
if len(subpaths) > 1:
if config.getOptions().DEBUG:
print("Num subpaths 1 = %d" % len(subpaths))
subpaths.sort(key=lambda x: pyx.unit.tocm(x.arclen()))
vispath = subpaths[-1]
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(
subpaths[0], [color.rgb.blue]
)
if config.getOptions().VDEBUG:
for a in ass:
ix, iy = p1path.at(a)
FeynDiagram.currentDiagram.currentCanvas.fill(
pyx.path.circle(ix, iy, 0.05), [color.rgb.green]
)
if p2path:
ass, bs = p2path.intersect(vispath)
for b in bs:
subpaths = vispath.split(b)
if len(subpaths) > 1:
if config.getOptions().DEBUG:
print("Num subpaths 2 = %d" % len(subpaths))
subpaths.sort(key=lambda x: pyx.unit.tocm(x.arclen()))
vispath = subpaths[-1]
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(
subpaths[0], [color.rgb.red]
)
if config.getOptions().VDEBUG:
for a in ass:
ix, iy = p2path.at(a)
FeynDiagram.currentDiagram.currentCanvas.fill(
pyx.path.circle(ix, iy, 0.05), [color.rgb.blue]
)
if config.getOptions().VDEBUG:
FeynDiagram.currentDiagram.currentCanvas.stroke(vispath, [color.rgb.red])
# return pyx.path.circle(-2,-1,0.2)
return vispath
[docs] def draw(self, canvas):
"""Draw this line on the given canvas."""
path = self.getVisiblePath()
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
print(path)
canvas.stroke(path, styles)
for l in self.labels:
l.draw(canvas)
## Fermion is an alias for Line
Fermion = Line
[docs]class MultiLine(Line):
"""A class for drawing multiple parallel straight lines."""
[docs] def __init__(
self,
point1,
point2,
n=5,
dist=0.2,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
Line.__init__(self, point1, point2, styles, arcthrupoint, is3D, arrows, labels)
self.n = n
self.dist = dist
[docs] def draw(self, canvas):
"""Draw this multiline on the supplied canvas."""
dist = self.dist
n = self.n
path = pyx.deformer.parallel(-n / 2.0 * dist).deform(self.getPath())
paths = [path]
defo = pyx.deformer.parallel(dist)
for _ in range(0, n):
path = defo.deform(path)
paths.append(path)
styles = self.styles + self.arrows
for p in paths:
canvas.stroke(p, styles)
for l in self.labels:
l.draw(canvas)
[docs]class Scalar(Line):
"""A scalar particle line, like a Higgs boson."""
[docs] def __init__(
self,
point1,
point2,
linestyle=pyx.style.linestyle.dashed,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
Line.__init__(self, point1, point2, styles, arcthrupoint, is3D, arrows, labels)
self.linestyle = linestyle
[docs] def draw(self, canvas):
"""Draw this scalar line on the given canvas."""
path = self.getVisiblePath()
styles = self.styles + [self.linestyle] + self.arrows
## TODO: call base class method?
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
print(path)
canvas.stroke(path, styles)
for l in self.labels:
l.draw(canvas)
## Higgs is an alias for Scalar
Higgs = Scalar
## Sfermion is also an alias for Scalar
Sfermion = Scalar
[docs]class Ghost(Scalar):
"""A dotted scalar particle line, like a Yang-Mills ghost particle."""
# same as scalar, but default style is dotted
[docs] def __init__(
self,
point1,
point2,
linestyle=pyx.style.linestyle.dotted,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
Scalar.__init__(
self,
point1=point1,
point2=point2,
styles=styles,
arcthrupoint=arcthrupoint,
is3D=is3D,
arrows=arrows,
labels=labels,
linestyle=linestyle,
)
## DecoratedLine base class
[docs]class DecoratedLine(Line):
"""Base class for spring and sine-like lines"""
[docs] def __init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
linetype,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
Line.__init__(self, point1, point2, styles, arcthrupoint, is3D, arrows, labels)
self.inverted = invert
self.arcradius = amplitude
self.frequency = frequency
self.extras = extras
self.linetype = linetype
[docs] def set3D(self, is3D=True, skipsize=pyx.unit.length(0.04), parity=0):
"""Make this line display in '3D'."""
self.is3D = is3D
self.skipsize3D = skipsize
self.parity3D = parity
return self
[docs] def invert(self):
"""Reflect the decoration in the line itself."""
self.inverted = not self.inverted
return self
[docs] def getFrequency(self):
"""Get the rate of occurence of the oscillation."""
return self.frequency
[docs] def setFrequency(self, freq):
"""Set the rate of occurence of the oscillation."""
self.frequency = freq
return self
[docs] def getAmplitude(self):
"""Get the radius of the oscillation."""
return self.arcradius
[docs] def setAmplitude(self, amplitude):
"""Set the radius of the oscillation."""
self.arcradius = amplitude
return self
[docs]class Gluon(DecoratedLine):
"""A line with a cycloid deformation"""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.15,
frequency=1.3,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"gluon",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas."""
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
mypath = self.getDeformedPath()
if config.getOptions().DRAFT or not self.is3D:
canvas.stroke(mypath, styles)
else:
para = pyx.deformer.parallel(0.001)
_, bs, _ = para.normpath_selfintersections(mypath.normpath(), epsilon=0.01)
coil_params = []
for b in bs:
coil_params.append(b[self.parity3D] - self.skipsize3D)
coil_params.append(b[self.parity3D] + self.skipsize3D)
pathbits = mypath.split(coil_params)
on = True
for pathbit in pathbits:
if on:
canvas.stroke(pathbit, styles)
on = not on
## Labels
for l in self.labels:
l.draw(canvas)
[docs]class Vector(DecoratedLine):
"""A line with a sinoid deformation"""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.25,
frequency=1.0,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"photon",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas."""
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
canvas.stroke(self.getDeformedPath(), styles)
for l in self.labels:
l.draw(canvas)
## Photon is an alias for Vector
Photon = Vector
[docs]class Graviton(DecoratedLine):
"""A line with a double sinoid deformation"""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.25,
frequency=0.6,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"graviton",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas."""
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
mypath1 = self.getDeformedPath(+1)
mypath2 = self.getDeformedPath(-1)
if self.inverted:
mypathtmp = mypath1
mypath1 = mypath2
mypath2 = mypathtmp
if config.getOptions().DRAFT or not self.is3D:
canvas.stroke(mypath1, styles)
canvas.stroke(mypath2, styles)
else:
ass, bs = mypath1.intersect(mypath2)
params1, params2 = [], []
parity1 = True
if self.parity3D == 0:
parity1 = False
for a in ass[1:]: ## TODO: better endpoint cut vetoing
if parity1:
params1.append(a - self.skipsize3D)
params1.append(a + self.skipsize3D)
parity1 = not parity1
pathbits1 = mypath1.split(params1)
on = True
for pathbit in pathbits1:
if on:
canvas.stroke(pathbit, styles)
on = not on
parity2 = False
if self.parity3D == 0:
parity2 = True
for b in bs[1:]: ## TODO: better endpoint cut vetoing
if parity2:
params2.append(b - self.skipsize3D)
params2.append(b + self.skipsize3D)
parity2 = not parity2
pathbits2 = mypath2.split(params2)
on = True
for pathbit in pathbits2:
if on:
canvas.stroke(pathbit, styles)
on = not on
for l in self.labels:
l.draw(canvas)
[docs]class Gaugino(DecoratedLine):
"""A line with a sinoid deformation and a normal line"""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.25,
frequency=1.0,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"gaugino",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas."""
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
mypath1 = self.getVisiblePath()
mypath2 = self.getDeformedPath()
if config.getOptions().DRAFT or not self.is3D:
canvas.stroke(mypath1, styles)
canvas.stroke(mypath2, styles)
else:
ass, bs = mypath1.intersect(mypath2)
params1, params2 = [], []
parity1 = True
if self.parity3D == 0:
parity1 = False
for a in ass:
if parity1:
params1.append(a - self.skipsize3D)
params1.append(a + self.skipsize3D)
parity1 = not parity1
pathbits1 = mypath1.split(params1)
on = True
for pathbit in pathbits1:
if on:
canvas.stroke(pathbit, styles)
on = not on
parity2 = False
if self.parity3D == 0:
parity2 = True
for b in bs:
if parity2:
params2.append(b - self.skipsize3D)
params2.append(b + self.skipsize3D)
parity2 = not parity2
pathbits2 = mypath2.split(params2)
on = True
for pathbit in pathbits2:
if on:
canvas.stroke(pathbit, styles)
on = not on
for l in self.labels:
l.draw(canvas)
[docs]class Gluino(DecoratedLine):
"""A line with a cycloid deformation and a normal line"""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.25,
frequency=1.2,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"susygluon",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas."""
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
mypath1 = self.getVisiblePath()
mypath2 = self.getDeformedPath()
if config.getOptions().DRAFT or not self.is3D:
canvas.stroke(mypath1, styles)
canvas.stroke(mypath2, styles)
else:
ass, bs = mypath1.intersect(mypath2)
params1, params2 = [], []
parity1 = True
if self.parity3D == 0:
parity1 = False
for a in ass:
if parity1:
params1.append(a - self.skipsize3D)
params1.append(a + self.skipsize3D)
parity1 = not parity1
pathbits1 = mypath1.split(params1)
on = True
for pathbit in pathbits1:
if on:
canvas.stroke(pathbit, styles)
on = not on
parity2 = False
if self.parity3D == 0:
parity2 = True
for b in bs:
if parity2:
params2.append(b - self.skipsize3D)
params2.append(b + self.skipsize3D)
parity2 = not parity2
para = pyx.deformer.parallel(0.001)
_, sbs, _ = para.normpath_selfintersections(
mypath2.normpath(), epsilon=0.01
)
coil_params = []
for b in sbs:
coil_params.append(b[self.parity3D] - self.skipsize3D)
coil_params.append(b[self.parity3D] + self.skipsize3D)
params2 += coil_params
params2.sort()
pathbits2 = mypath2.split(params2)
on = True
for pathbit in pathbits2:
if on:
canvas.stroke(pathbit, styles)
on = not on
for l in self.labels:
l.draw(canvas)
[docs]class Gravitino(DecoratedLine):
"""A line with a double sinoid deformation and a simple line"""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.25,
frequency=0.6,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"gravitino",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas."""
styles = self.styles + self.arrows
if config.getOptions().DEBUG:
print("Drawing " + str(self.__class__) + " with styles = " + str(styles))
mypath = [
self.getVisiblePath(),
self.getDeformedPath(+1),
self.getDeformedPath(-1),
]
if self.inverted:
mypath = mypath[::-1]
if config.getOptions().DRAFT or not self.is3D:
for i in range(3):
canvas.stroke(mypath[i], styles)
else:
ass, bs = mypath[0].intersect(mypath[1])
ass, cs = mypath[0].intersect(mypath[2])
ps = [ass[:], bs[:], cs[:]]
for i in range(3):
params = []
for a in range(len(ps[i])): ## TODO: better endpoint cut vetoing
if (a % 3) != i:
params.append(ps[i][a] - self.skipsize3D)
params.append(ps[i][a] + self.skipsize3D)
pathbits = mypath[i].split(params)
on = True
for pathbit in pathbits:
if on:
canvas.stroke(pathbit, styles)
on = not on
for l in self.labels:
l.draw(canvas)
[docs]class Phantom(DecoratedLine):
"""An invisible line."""
[docs] def __init__(
self,
point1,
point2,
amplitude=0.25,
frequency=1.0,
extras=0,
invert=False,
styles=None,
arcthrupoint=None,
is3D=False,
arrows=None,
labels=None,
**kwargs
):
"""Constructor."""
DecoratedLine.__init__(
self,
point1,
point2,
amplitude,
frequency,
extras,
invert,
"phantom",
styles,
arcthrupoint,
is3D,
arrows,
labels,
)
[docs] def draw(self, canvas):
"""Draw the line on the supplied canvas (does nothing for a phantom apart from labels)."""
for l in self.labels:
l.draw(canvas)
return
# A dictionary for mapping FeynML line types to line classes
NamedLine = {
"line": Line,
"higgs": Scalar,
"photon": Vector,
"vector": Vector,
"gluon": Gluon,
"fermion": Line,
"anti fermion": Line,
"graviton": Graviton,
"gaugino": Gaugino,
"gluino": Gluino,
"gravitino": Gravitino,
"scalar": Scalar,
"anti scalar": Scalar,
"ghost": Ghost,
"phantom": Phantom,
"boson": Vector,
}