from pyfeyn2.feynmandiagram import Point
from pyfeyn2.render.render import Render
from pyfeyn2.render.text.label import Label
from pyfeyn2.render.text.line import ASCIILine
from pyfeyn2.render.text.style import Cross
[docs]class Gluon(ASCIILine):
def __init__(self):
super().__init__(style=Cross(vert=["O"], horz=["O"]), begin="*", end="*")
[docs]class Photon(ASCIILine):
def __init__(self):
super().__init__(begin="*", end="*", style=Cross(vert=["(", ")"], horz=["~"]))
[docs]class Fermion(ASCIILine):
def __init__(self):
super().__init__(
begin="*",
end="*",
style=Cross(
left="--<--",
right="-->--",
up="||^||",
down="||v||",
),
)
[docs]class Scalar(ASCIILine):
def __init__(self):
super().__init__(
begin="*",
end="*",
style=Cross(
left="..<..",
right="..>..",
up="::^::",
down="::v::",
),
)
[docs]class Ghost(ASCIILine):
def __init__(self):
super().__init__(
begin="*",
end="*",
style=Cross(
vert=":",
horz=".",
),
)
[docs]class Higgs(ASCIILine):
def __init__(self):
super().__init__(
begin="*",
end="*",
style=Cross(
vert="=",
horz="H",
),
)
[docs]class Gluino(ASCIILine):
def __init__(self):
super().__init__(begin="*", end="*", style=Cross(vert=["&"], horz=["&"]))
[docs]class Gaugino(ASCIILine):
def __init__(self):
super().__init__(begin="*", end="*", style=Cross(vert=["$"], horz=["$"]))
[docs]class Phantom(ASCIILine):
def __init__(self):
super().__init__(begin=None, end=None, style=Cross(vert="", horz=""))
[docs] def draw(self, pane, isrc, itar, scalex=1, scaley=1, kickx=0, kicky=0):
pass
[docs]class ASCIIRender(Render):
"""Renders Feynman diagrams to ASCII art."""
[docs] namedlines = {
"gluon": Gluon,
"photon": Photon,
"vector": Photon,
"boson": Photon,
"fermion": Fermion,
"ghost": Ghost,
"higgs": Higgs,
"scalar": Scalar,
"slepton": Scalar,
"squark": Scalar,
"gluino": Gluino,
"gaugino": Gaugino,
"phantom": Phantom,
"label": Label,
}
def __init__(self, fd=None, *args, **kwargs):
super().__init__(fd, *args, **kwargs)
[docs] def render(
self,
file=None,
show=True,
resolution=100,
width=None,
height=None,
clean_up=True,
):
minx, miny, maxx, maxy = self.fd.get_bounding_box()
shift = 2
# maxx = maxx + shift
maxy = maxy + shift
# minx = minx - shift
miny = miny - shift
if width is None:
width = int((maxx - minx) * resolution / 10)
if height is None:
height = int(
(maxy - miny) * resolution / 10 / 2
) # divide by two to make it look better due to aspect ratio
pane = []
for _ in range(height):
pane.append([" "] * width)
scalex = (width - 1) / (maxx - minx)
scaley = -(height - 1) / (maxy - miny)
kickx = -minx
kicky = -maxy
fmt = {"scalex": scalex, "kickx": kickx, "scaley": scaley, "kicky": kicky}
for p in self.fd.propagators:
src = self.fd.get_vertex(p.source)
tar = self.fd.get_vertex(p.target)
self.namedlines[p.type]().draw(pane, src, tar, **fmt)
if p.label is not None:
self.namedlines["label"](p.label).draw(pane, src, tar, **fmt)
for l in self.fd.legs:
tar = self.fd.get_vertex(l.target)
if l.sense[:2] == "in" or l.sense[:8] == "anti-out":
self.namedlines[l.type]().draw(pane, Point(l.x, l.y), tar, **fmt)
if l.label is not None:
self.namedlines["label"](l.label).draw(
pane, Point(l.x, l.y), tar, **fmt
)
elif l.sense[:3] == "out" or l.sense[:9] == "anti-in":
self.namedlines[l.type]().draw(pane, tar, Point(l.x, l.y), **fmt)
if l.label is not None:
self.namedlines["label"](l.label).draw(
pane, tar, Point(l.x, l.y), **fmt
)
joined = "\n".join(["".join(row) for row in pane]) + "\n"
self.set_src_txt(joined)
if show:
print(joined)
return joined
[docs] def get_src_txt(self):
return self.src_txt
[docs] def set_src_txt(self, src_txt):
self.src_txt = src_txt
@staticmethod
[docs] def valid_attribute(attr: str) -> bool:
return super(ASCIIRender, ASCIIRender).valid_attribute(attr) or attr in [
"x",
"y",
"label",
]
@staticmethod
[docs] def valid_type(typ: str) -> bool:
if typ.lower() in ASCIIRender.namedlines:
return True
return False