import logging
import warnings
from dataclasses import dataclass, field
from typing import List, Optional, Union
import cssutils
import smpl_doc.doc as doc
from cssselect import GenericTranslator, SelectorError
from lxml import etree
from smpl_util.util import withify
from feynml.head import Head
from feynml.id import Identifiable
from feynml.leg import Leg
from feynml.propagator import Propagator
from feynml.sheet import SheetHandler
from feynml.styled import CSSSheet, Styled
from feynml.vertex import Vertex
from feynml.xml import XML
from .type import get_default_sheet
# We don't want to see the cssutils warnings, since we have custom properties
cssutils.log.setLevel(logging.CRITICAL)
[docs]@withify()
@dataclass
class FeynmanDiagram(SheetHandler, XML, Styled, Identifiable):
class Meta:
name = "diagram"
propagators: List[Propagator] = field(
default_factory=list,
metadata={"name": "propagator", "type": "Element", "namespace": ""},
)
vertices: List[Vertex] = field(
default_factory=list,
metadata={"name": "vertex", "type": "Element", "namespace": ""},
)
legs: List[Leg] = field(
default_factory=list,
metadata={"name": "leg", "type": "Element", "namespace": ""},
)
parent_fml = None
sheet: CSSSheet = field(
default_factory=lambda: cssutils.parseString(""),
metadata={
"name": "sheet",
"xml_attribute": True,
"type": "Attribute",
"namespace": "",
},
)
def add(self, *fd_all: List[Union[Propagator, Vertex, Leg]]):
for a in fd_all:
if isinstance(a, Propagator):
self.propagators.append(a)
elif isinstance(a, Vertex):
self.vertices.append(a)
elif isinstance(a, Leg):
self.legs.append(a)
else:
raise Exception("Unknown type: " + str(type(a)) + " " + str(a))
return self
def has_id(self, id):
for l in [self.propagators, self.vertices, self.legs]:
for a in l:
if a.id == id:
return True
return False
def get_point(self, idd):
for v in self.vertices:
if v.id == idd:
return v
for leg in self.legs:
if leg.id == idd:
return leg
return None
def get_vertex(self, idd):
for v in self.vertices:
if v.id == idd:
return v
return None
def get_leg(self, idd):
for leg in self.legs:
if leg.id == idd:
return leg
return None
def get_connections(self, vertex):
return [
p
for p in self.propagators
if p.source == vertex.id or p.target == vertex.id
] + [leg for leg in self.legs if leg.target == vertex.id]
def remove_propagator(self, propagator):
self.propagators.remove(propagator)
return self
def get_bounding_box(self):
min_x = 0
min_y = 0
max_x = 0
max_y = 0
for v in self.vertices:
if v.x is None or v.y is None:
warnings.warn(
"Vertex " + v.id + " has no position, skipping it in bounding box"
)
continue
min_x = min(min_x, v.x)
min_y = min(min_y, v.y)
max_x = max(max_x, v.x)
max_y = max(max_y, v.y)
for leg in self.legs:
if leg.x is None or leg.y is None:
warnings.warn(
"Leg " + leg.id + " has no position, skipping it in bounding box"
)
continue
min_x = min(min_x, leg.x)
min_y = min(min_y, leg.y)
max_x = max(max_x, leg.x)
max_y = max(max_y, leg.y)
return min_x, min_y, max_x, max_y
[docs] @doc.append_doc(Head.get_style)
def get_style(self, obj, xml: XML = None) -> cssutils.css.CSSStyleDeclaration:
if self.parent_fml is not None:
return super().get_style(obj, self.parent_fml)
elif xml is not None:
return super().get_style(obj, xml)
else:
warnings.warn("No parent fml, returning default style")
return super().get_style(obj, self)
[docs] def get_sheets(self):
if self.parent_fml is not None:
return self.parent_fml.get_sheets() + [self.sheet]
else:
warnings.warn("No parent fml, returning default sheet")
return super().get_sheets() + [self.sheet]
[docs] def get_sheet(self):
return self.sheet
[docs] def with_sheet(self, sheet):
self.sheet = sheet
return self