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.paint import CENTER
from pyfeyn2.render.pyx.utils import Visible


[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"""
[docs] def __init__( self, pos=0.5, size=6 * pyx.unit.v_pt, angle=45, constriction=0.8, sense=1 ): """Constructor.""" self.pos = pos self.size = size self.angle = angle self.constriction = constriction self.sense = sense
[docs] def decorate(self, dp, textengine=None): """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(), self.sense, 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."""
[docs] 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."""
[docs] 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"""
[docs] def __init__( self, text, pos=None, x=None, y=None, size=pyx.text.size.normalsize, halign=CENTER, valign=None, ): """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.textattrs.append(halign) if valign is not None: self.textattrs.append(valign) 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.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"""
[docs] def __init__( self, point, text, displace=0.3, angle=0, size=pyx.text.size.normalsize, halign=CENTER, valign=None, ): """Constructor.""" self.size = size self.displace = pyx.unit.length(displace) self.angle = angle self.text = text self.point = point self.textattrs = [] self.textattrs.append(halign) if valign is not None: self.textattrs.append(valign)
[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.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"""
[docs] def __init__( self, line, text, pos=0.5, displace=0.3, angle=0, size=pyx.text.size.normalsize, halign=CENTER, valign=None, ): """Constructor.""" self.pos = pos self.size = size self.displace = pyx.unit.length(displace) self.angle = angle self.text = text self.line = line self.textattrs = [] self.textattrs.append(halign) if valign is not None: self.textattrs.append(valign)
[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.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)