Source code for pyfeyn2.render.pyx.lines

"""Various particle line types."""

import math

import pyx
from pyx import color

from pyfeyn2.render.pyx import config
from pyfeyn2.render.pyx.deco import Arrow, LineLabel, ParallelArrow, PointLabel
from pyfeyn2.render.pyx.diagrams import FeynDiagram
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" def __init__(self, point1, point2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.arrows = [] self.labels = [] ## 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 ): """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 ) ) 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, choice): """Make this line display in '3D'.""" self.is3D = choice 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, bs = 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): """Drwa 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
[docs]Fermion = Line
[docs]class MultiLine(Line): """A class for drawing multiple parallel straight lines.""" def __init__(self, point1, point2, n=5, dist=0.2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.arrows = [] self.labels = [] self.n = n self.dist = dist ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[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 m in range(0, n): path = defo.deform(path) paths.append(path) styles = self.styles + self.arrows for p in paths: canvas.stroke(p, styles)
[docs]class Scalar(Line): """A scalar particle line, like a Higgs boson."""
[docs] def draw(self, canvas): """Draw this scalar line on the given canvas.""" path = self.getVisiblePath() styles = self.styles + [pyx.style.linestyle.dashed] + 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
[docs]Higgs = Scalar
## Sfermion is also an alias for Scalar
[docs]Sfermion = Scalar
[docs]class Ghost(Line): """A dotted scalar particle line, like a Yang-Mills ghost particle."""
[docs] def draw(self, canvas): """Draw this scalar line on the given canvas.""" path = self.getVisiblePath() styles = self.styles + [pyx.style.linestyle.dotted] + 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)
## DecoratedLine base class
[docs]class DecoratedLine(Line): """Base class for spring and sine-like lines"""
[docs] def invert(self): """Reflect the line decoration about the line.""" pass
[docs] def getNumHalfCycles(self): """Get the number of half cycles in this line.""" pass
[docs] def getDeformedPath(self): """Get the deformed path.""" return getVisiblePath()
[docs]class Gluon(DecoratedLine): """A line with a cycloid deformation""" def __init__(self, point1, point2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.skipsize3D = pyx.unit.length(0.04) self.parity3D = 0 self.arrows = [] self.labels = [] self.arcradius = pyx.unit.length(0.15) self.frequency = 1.3 self.extras = 0 self.inverted = False self.linetype = "gluon" ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[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): """Flip the line decoration around the line.""" self.inverted = not self.inverted return self
[docs] def getFrequency(self): """Get the rate of occurence of the coil decoration.""" return self.frequency
[docs] def setFrequency(self, freq): """Set the rate of occurence of the coil decoration.""" self.frequency = freq return self
[docs] def getAmplitude(self): """Get the radius of the coil decoration.""" return self.arcradius
[docs] def setAmplitude(self, amplitude): """Set the radius of the coil decoration.""" self.arcradius = amplitude return self
[docs] def setExtraCycles(self, extras): """Add some extra (possibly negative) oscillations to the coil decoration.""" self.extras = extras return self
[docs] def getDeformedPath(self): """Get the path modified by the coil warping.""" needwindings = ( self.frequency * pyx.unit.tocm(self.getVisiblePath().arclen()) / pyx.unit.tocm(self.arcradius) ) ## Get the whole number of windings and make sure that it's odd so we ## don't get a weird double-back thing intwindings = int(needwindings) intwindings += 2 * self.extras if intwindings % 2 == 0: intwindings -= 1 deficit = needwindings - intwindings sign = 1 if self.inverted: sign = -1 defo = pyx.deformer.cycloid( self.arcradius, intwindings, curvesperhloop=10, skipfirst=0.0, skiplast=0.0, sign=sign, ) return defo.deform(self.getVisiblePath())
[docs] def draw(self, canvas): """Draw the line on the supplied canvas.""" styles = self.styles + self.arrows # TODO repair # 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) # para.deform(mypath) # bs, _ = para.normpath_selfintersections(mypath.normpath(), epsilon=0.01,eps_comparepairs=0.001) # 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""" def __init__(self, point1, point2, amplitude=0.25, frequency=1.0): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.arrows = [] self.labels = [] self.inverted = False self.arcradius = amplitude # pyx.unit.length(0.25) self.linetype = "photon" self.frequency = frequency self.extrahalfs = 0 ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(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 occurance of the oscillation.""" return self.frequency
[docs] def setFrequency(self, freq): """Set the rate of occurance of the oscillation.""" self.frequency = freq return self
[docs] def getAmplitude(self): """Get the size of the oscillation.""" return self.arcradius
[docs] def setAmplitude(self, amplitude): """Set the size of the oscillation.""" self.arcradius = amplitude return self
[docs] def setExtraHalfCycles(self, extras): """Add some extra half cycles to the oscillation on top of those determined from the frequency.""" self.extrahalfs = extras return self
[docs] def getDeformedPath(self): """Get the path with the decorative deformation.""" intwindings = int( self.frequency * pyx.unit.tocm(self.getVisiblePath().arclen()) / pyx.unit.tocm(self.arcradius) ) intwindings += self.extrahalfs sign = 1 if self.inverted: sign = -1 defo = pyx.deformer.cycloid( self.arcradius, intwindings, curvesperhloop=5, skipfirst=0.0, skiplast=0.0, turnangle=0, sign=sign, ) return defo.deform(self.getVisiblePath())
[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
[docs]Photon = Vector
[docs]class Graviton(DecoratedLine): """A line with a double sinoid deformation""" def __init__(self, point1, point2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.skipsize3D = pyx.unit.length(0.04) self.parity3D = 0 self.inverted = False self.arrows = [] self.labels = [] self.arcradius = pyx.unit.length(0.25) self.linetype = "graviton" ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[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 getDeformedPath(self, sign=1): """Get the path with the decorative deformation.""" intwindings = int( 0.6 * pyx.unit.tocm(self.getVisiblePath().arclen()) / pyx.unit.tocm(self.arcradius) ) vispath = self.getVisiblePath() curveradii = vispath.curveradius([i / 10.0 for i in range(0, 11)]) mincurveradius = None for curveradius in curveradii: try: curveradius = abs(curveradius / pyx.unit.m) # if config.getOptions().DEBUG: # print self.__class__, "- curve radius = ", curveradius if mincurveradius is None or curveradius < mincurveradius: mincurveradius = curveradius except: pass numhloopcurves = 5 if mincurveradius is not None: numhloopcurves += int(0.1 / mincurveradius) if config.getOptions().DEBUG: print( self.__class__, "- min curvature radius = ", mincurveradius, "->", numhloopcurves, "curves/hloop", ) defo = pyx.deformer.cycloid( self.arcradius, intwindings, curvesperhloop=numhloopcurves, skipfirst=0.0, skiplast=0.0, turnangle=0, sign=sign, ) return defo.deform(vispath)
[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""" def __init__(self, point1, point2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.skipsize3D = pyx.unit.length(0.04) self.parity3D = 0 self.inverted = False self.arrows = [] self.labels = [] self.arcradius = pyx.unit.length(0.25) self.linetype = "gaugino" ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[docs] def set3D(self, is3D=True, skipsize=pyx.unit.length(0.04), parity=0): """Make the line look 3-dimensional by 'cutting' one line where self-intersections occur.""" 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 getDeformedPath(self): """Get the path with the decorative deformation.""" intwindings = int( pyx.unit.tocm(self.getVisiblePath().arclen()) / pyx.unit.tocm(self.arcradius) ) sign = 1 if self.inverted: sign = -1 vispath = self.getVisiblePath() curveradii = vispath.curveradius([i / 10.0 for i in range(0, 11)]) mincurveradius = None for curveradius in curveradii: try: curveradius = abs(mincurveradius / pyx.unit.m) # if config.getOptions().DEBUG: # print self.__class__, "- curvature radius = ", curveradius if mincurveradius is None or curveradius < mincurveradius: mincurveradius = curveradius except: pass ## Use curvature info to increase number of curve sections numhloopcurves = 5 if mincurveradius is not None: numhloopcurves += int(0.1 / mincurveradius) if config.getOptions().DEBUG: print( self.__class__, "- min curve radius = ", mincurveradius, "->", numhloopcurves, "curves/hloop", ) defo = pyx.deformer.cycloid( self.arcradius, intwindings, curvesperhloop=numhloopcurves, skipfirst=0.0, skiplast=0.0, turnangle=0, sign=sign, ) return defo.deform(vispath)
[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""" def __init__(self, point1, point2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.skipsize3D = pyx.unit.length(0.04) self.parity3D = 0 self.inverted = False self.arrows = [] self.labels = [] self.arcradius = pyx.unit.length(0.25) self.linetype = "susygluon" ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[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 getDeformedPath(self): """Get the path with the decorative deformation.""" needwindings = ( 1.2 * pyx.unit.tocm(self.getVisiblePath().arclen()) / pyx.unit.tocm(self.arcradius) ) ## Get the whole number of windings and make sure that it's odd so we ## don't get a weird double-back thing intwindings = int(needwindings) if intwindings % 2 == 0: intwindings -= 1 deficit = needwindings - intwindings sign = 1 if self.inverted: sign = -1 ## Get list of curvature radii in the visible path vispath = self.getVisiblePath() curveradii = vispath.curveradius([i / 10.0 for i in range(0, 11)]) mincurveradius = None ## Find the maximum curvature (set None if straight line) for curveradius in curveradii: try: curveradius = abs(curvature / pyx.unit.m) # if config.getOptions().DEBUG: # print self.__class__, "- curvature radius = ", curveradius if mincurveradius is None or curveradius < mincurveradius: mincurveradius = curveradius except: pass ## Use curvature info to increase number of curve sections numhloopcurves = 10 if mincurveradius is not None: numhloopcurves += int(0.2 / mincurveradius) if config.getOptions().DEBUG: print( self.__class__, "- min curve radius = ", mincurveradius, "->", numhloopcurves, "curves/hloop", ) defo = pyx.deformer.cycloid( self.arcradius, intwindings, curvesperhloop=numhloopcurves, skipfirst=0.0, skiplast=0.0, sign=sign, ) return defo.deform(vispath)
[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) sas, sbs, scs = 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""" def __init__(self, point1, point2): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = [] self.arcthrupoint = None self.is3D = False self.skipsize3D = pyx.unit.length(0.04) self.parity3D = 0 self.inverted = False self.arrows = [] self.labels = [] self.arcradius = pyx.unit.length(0.25) self.linetype = "gravitino" ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[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 getDeformedPath(self, sign=1): """Get the path with the decorative deformation.""" intwindings = int( 0.6 * pyx.unit.tocm(self.getVisiblePath().arclen()) / pyx.unit.tocm(self.arcradius) ) vispath = self.getVisiblePath() curveradii = vispath.curveradius([i / 10.0 for i in range(0, 11)]) mincurveradius = None for curveradius in curveradii: try: curveradius = abs(curveradius / pyx.unit.m) # if config.getOptions().DEBUG: # print self.__class__, "- curve radius = ", curveradius if mincurveradius is None or curveradius < mincurveradius: mincurveradius = curveradius except: pass numhloopcurves = 5 if mincurveradius is not None: numhloopcurves += int(0.1 / mincurveradius) if config.getOptions().DEBUG: print( self.__class__, "- min curvature radius = ", mincurveradius, "->", numhloopcurves, "curves/hloop", ) defo = pyx.deformer.cycloid( self.arcradius, intwindings, curvesperhloop=numhloopcurves, skipfirst=0.0, skiplast=0.0, turnangle=0, sign=sign, ) return defo.deform(vispath)
[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.""" def __init__(self, point1, point2): """Constructor.""" DecoratedLine.__init__(self, point1, point2) self.linetype = "phantom"
[docs] def draw(self, canvas): """Draw the line on the supplied canvas (does nothing for a phantom).""" return
# A dictionary for mapping FeynML line types to line classes
[docs]NamedLine = { "higgs": Higgs, "photon": Photon, "vector": Photon, "gluon": Gluon, "fermion": Fermion, "graviton": Graviton, "gaugino": Gaugino, "gluino": Gluino, "gravitino": Gravitino, "scalar": Higgs, "ghost": Ghost, "phantom": Phantom, }