Source code for pyfeyn2.render.pyx.deco

"""A couple of classes for decorating diagram elements."""

import math

import pyx

from pyfeyn2.render.pyx import config
from pyfeyn2.render.pyx.diagrams import FeynDiagram
from pyfeyn2.render.pyx.utils import Visible

###########################################################################################
## Added by George S. Williams to allow PyFeyn to work with PyX versions 0.12.x and 0.11.x
## Also see changes in class Arrow and class ParallelArrow


[docs]def getarrowpath( arrowtopath, selfpos, var1, selfsize, var2, selfconstriction, constrictionlen ): arrowpath = pyx.deco._arrowhead( arrowtopath, selfpos, var1, selfsize, var2, selfconstriction, constrictionlen ) return arrowpath
########################################################################################### ## Arrow decorator class
[docs]class Arrow(pyx.deco.deco, pyx.attr.attr): """Arrow for Feynman diagram lines""" def __init__(self, pos=0.5, size=6 * pyx.unit.v_pt, angle=45, constriction=0.8): """Constructor.""" self.pos = pos self.size = size self.angle = angle self.constriction = constriction
[docs] def decorate(self, dp): """Attach arrow to a path (usually a line).""" dp.ensurenormpath() constrictionlen = ( self.size * self.constriction * math.cos(self.angle * math.pi / 360.0) ) arrowtopos = self.pos * dp.path.arclen() + 0.5 * self.size arrowtopath = dp.path.split(arrowtopos)[0] arrowpath = getarrowpath( arrowtopath, self.pos * dp.path.arclen(), 1, self.size, 45, self.constriction, constrictionlen, ) dp.ornaments.fill(arrowpath) return dp
[docs]class FreeArrow(Visible): """Arrow not attached to any line in a diagram.""" def __init__( self, length=0.5 * pyx.unit.v_cm, size=6 * pyx.unit.v_pt, angle=45, constriction=0.8, pos=None, x=None, y=None, direction=0, ): """Constructor.""" self.x, self.y = 0, 0 if x is not None: self.x = x if y is not None: self.y = y if pos is not None: self.x, self.y = pos.getXY() self.direction = direction self.length = length self.size = size self.angle = angle self.constriction = constriction ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[docs] def draw(self, canvas): """Draw this arrow on the supplied canvas.""" endx, endy = self.x - self.length * math.sin( self.direction * math.pi / 180.0 ), self.y - self.length * math.cos(self.direction * math.pi / 180.0) linepath = pyx.deco.decoratedpath( pyx.path.path(pyx.path.moveto(endx, endy), pyx.path.lineto(self.x, self.y)) ) styles = [ pyx.deco.earrow( size=self.size, angle=self.angle, constriction=self.constriction ) ] canvas.stroke(linepath.path, styles)
[docs]class ParallelArrow(Visible): """Arrow running parallel to a line, for momenta, helicities etc.""" def __init__( self, line, 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, ): """Constructor.""" self.line = line self.pos = pos self.displace = pyx.unit.length(displace) self.length = length self.size = size self.angle = angle self.constriction = constriction self.sense = sense self.curved = curved self.stems = stems self.stemsep = stemsep
[docs] def draw(self, canvas): """Draw this arrow on the supplied canvas.""" p = self.line.getPath() posparam = p.begin() + self.pos * p.arclen() x, y = self.line.fracpoint(self.pos).getXY() arrx, arry = self.line.fracpoint( self.pos + self.length / 2.0 / p.arclen() ).getXY() endx, endy = self.line.fracpoint( self.pos - self.length / 2.0 / p.arclen() ).getXY() ## Calculate the displacement from the line displacement = self.displace intrinsicwidth = pyx.unit.length(0.1) if hasattr(self.line, "arcradius"): intrinsicwidth = self.line.arcradius if displacement > 0: displacement += intrinsicwidth else: displacement -= intrinsicwidth if config.getOptions().DEBUG: print("Displacement = ", displacement) ## Position the arrow on the right hand side of lines tangent = p.tangent(posparam, displacement) normal = tangent.transformed(pyx.trafo.rotate(90, x, y)) nx, ny = normal.atend() nxcm, nycm = pyx.unit.tocm(nx - x), pyx.unit.tocm(ny - y) vx, vy = p.atbegin() vxcm, vycm = pyx.unit.tocm(x - vx), pyx.unit.tocm(y - vy) ## If the arrow is on the left, flip it by 180 degrees if (vxcm * nycm - vycm * nxcm) > 0: normal = normal.transformed(pyx.trafo.rotate(180, x, y)) nx, ny = normal.atend() if displacement < 0: normal = normal.transformed(pyx.trafo.rotate(180, x, y)) nx, ny = normal.atend() if config.getOptions().VDEBUG: FeynDiagram.currentDiagram.currentCanvas.stroke(normal) ## Displace the arrow by this normal vector endx, endy = endx + (nx - x), endy + (ny - y) arrx, arry = arrx + (nx - x), arry + (ny - y) if self.sense < 0.0: arrx, arry, endx, endy = endx, endy, arrx, arry if not self.curved: linepath = pyx.path.path( pyx.path.moveto(endx, endy), pyx.path.lineto(arrx, arry) ) styles = [ pyx.deco.earrow( size=self.size, angle=self.angle, constriction=self.constriction ) ] dist = self.stemsep n = self.stems if n > 1: # helicity style arrow arrowtopath = linepath.split(0.8 * linepath.arclen())[0] constrictionlen = ( self.size * self.constriction * math.cos(self.angle * math.pi / 360.0) ) arrowpath = getarrowpath( arrowtopath, linepath.arclen(), 1, self.size, 45, self.constriction, constrictionlen, ) canvas.fill(arrowpath) path = pyx.deformer.parallel(-(n + 1) / 2.0 * dist).deform(arrowtopath) defo = pyx.deformer.parallel(dist) for m in range(n): path = defo.deform(path) canvas.stroke(path, []) else: # ordinary (momentum) arrow canvas.stroke(linepath, styles) else: # curved arrow (always momentum-style) curvepiece = self.line.getPath().split( [ (self.pos * p.arclen() - self.length / 2.0), (self.pos * p.arclen() + self.length / 2.0), ] ) arrpiece = curvepiece[1] if self.sense < 0: arrpiece = arrpiece.reversed() linepath = pyx.deco.decoratedpath( pyx.deformer.parallel(displacement).deform(arrpiece) ) styles = [ pyx.deco.earrow( size=self.size, angle=self.angle, constriction=self.constriction ) ] canvas.stroke(linepath.path, styles)
## Label
[docs]class Label(Visible): """General label, unattached to any diagram elements""" def __init__(self, text, pos=None, x=None, y=None, size=pyx.text.size.normalsize): """Constructor.""" self.x, self.y = 0, 0 if x is not None: self.x = x if y is not None: self.y = y self.size = size self.text = text self.textattrs = [] self.pos = pos ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self)
[docs] def draw(self, canvas): """Draw this label on the supplied canvas.""" textattrs = pyx.attr.mergeattrs( [pyx.text.halign.center, pyx.text.vshift.mathaxis, self.size] + self.textattrs ) t = pyx.text.latexrunner().text(self.x, self.y, self.text, textattrs) canvas.insert(t)
## PointLabel
[docs]class PointLabel(Label): """Label attached to points on the diagram""" def __init__( self, point, text, displace=0.3, angle=0, size=pyx.text.size.normalsize ): """Constructor.""" self.size = size self.displace = pyx.unit.length(displace) self.angle = angle self.text = text self.point = point self.textattrs = []
[docs] def getPoint(self): """Get the point associated with this label.""" return self.point
[docs] def setPoint(self, point): """Set the point associated with this label.""" self.point = point return self
[docs] def draw(self, canvas): """Draw this label on the supplied canvas.""" if config.getOptions().VDEBUG: canvas.fill( pyx.path.circle(self.point.getX(), self.point.getY(), 0.05), [pyx.color.rgb.green], ) x = self.point.getX() + self.displace * math.cos(math.radians(self.angle)) y = self.point.getY() + self.displace * math.sin(math.radians(self.angle)) textattrs = pyx.attr.mergeattrs( [pyx.text.halign.center, pyx.text.vshift.mathaxis, self.size] + self.textattrs ) t = pyx.text.latexrunner().text(x, y, self.text, textattrs) canvas.insert(t)
## LineLabel
[docs]class LineLabel(Label): """Label for Feynman diagram lines""" def __init__( self, line, text, pos=0.5, displace=0.3, angle=0, size=pyx.text.size.normalsize ): """Constructor.""" self.pos = pos self.size = size self.displace = pyx.unit.length(displace) self.angle = angle self.text = text self.line = line self.textattrs = []
[docs] def getLine(self): """Get the associated line.""" return self.line
[docs] def setLine(self, line): """Set the associated line.""" self.line = line return self
[docs] def draw(self, canvas): """Draw this label on the supplied canvas.""" p = self.line.getPath() # x, y = self.line.fracPoint(self.pos).getXY() posparam = p.begin() + self.pos * p.arclen() x, y = p.at(posparam) ## Calculate the displacement from the line displacement = self.displace intrinsicwidth = pyx.unit.length(0.1) if hasattr(self.line, "arcradius"): intrinsicwidth = self.line.arcradius if displacement > 0: displacement += intrinsicwidth else: displacement -= intrinsicwidth if config.getOptions().DEBUG: print("Displacement = ", displacement) ## Position the label on the right hand side of lines tangent = p.tangent(posparam, displacement) normal = tangent.transformed(pyx.trafo.rotate(90, x, y)) nx, ny = normal.atend() nxcm, nycm = pyx.unit.tocm(nx - x), pyx.unit.tocm(ny - y) vx, vy = p.atbegin() vxcm, vycm = pyx.unit.tocm(x - vx), pyx.unit.tocm(y - vy) ## If the label is on the left, flip it by 180 degrees if (vxcm * nycm - vycm * nxcm) > 0: normal = normal.transformed(pyx.trafo.rotate(180, x, y)) nx, ny = normal.atend() if displacement < 0: normal = normal.transformed(pyx.trafo.rotate(180, x, y)) nx, ny = normal.atend() if config.getOptions().VDEBUG: FeynDiagram.currentDiagram.currentCanvas.stroke(normal) ## Displace the label by this normal vector x, y = nx, ny textattrs = pyx.attr.mergeattrs( [pyx.text.halign.center, pyx.text.vshift.mathaxis, self.size] + self.textattrs ) t = pyx.text.latexrunner().text(x, y, self.text, textattrs) # t.linealign(self.displace, # math.cos(self.angle * math.pi/180), # math.sin(self.angle * math.pi/180)) canvas.insert(t)