def bend(self, amount): """Bend the line to the right by a given distance.""" 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.currenDiagram.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.currenDiagram.currentCanvas.stroke( self.getVisiblePath(), [color.rgb.blue]) return self
def bend(self, amount): """Bend the line to the right by a given distance.""" 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.currenDiagram.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.currenDiagram.currentCanvas.stroke(self.getVisiblePath(), [color.rgb.blue]) return self
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.currenDiagram.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))
class Line(Visible): "Base class for all objects which connect points in Feynman diagrams" def __init__(self, point1, point2, styles=[], arcthrupoint=None, is3D=False, arrows=[], labels=[], **kwargs): """Constructor.""" self.p1 = point1 self.p2 = point2 self.styles = styles self.arcthrupoint = arcthrupoint self.is3D = is3D self.arrows = arrows self.labels = labels ## Add this to the current diagram automatically FeynDiagram.currentDiagram.add(self) def addLabel(self, text, pos=0.5, displace=-0.25, angle = 0, size=pyx.text.size.normalsize, halign=CENTER, valign=None, **kwargs): """Add a LaTeX label to this line, either via parameters or actually as a TeXLabel object.""" 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, halign=halign, valign=valign)) if config.getOptions().DEBUG: print("Labels = " + str(self.labels)) return self 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 def removeLabels(self): """Remove the labels from this line.""" self.labels = [] return self 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) 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 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 def removeArrows(self): """Remove all arrows from this line.""" self.arrows = [] return self 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 def straighten(self): """Make this line a straight line between start and end.""" self.arcthrupoint = None return self def bend(self, amount): """Bend the line to the right by a given distance.""" 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.currenDiagram.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.currenDiagram.currentCanvas.stroke(self.getVisiblePath(), [color.rgb.blue]) return self def set3D(self, choice): """Make this line display in '3D'.""" self.is3D = choice return self def getStyles(self, stylelist): """Get the styles associated with this line.""" return self.styles def setStyles(self, stylelist): """Set the styles associated with this line.""" self.styles = stylelist return self def addStyle(self, style): """Add a style to this line.""" self.styles.append(style) return self def addStyles(self, stylelist): """Add some styles to this line.""" self.styles = self.styles + stylelist return self 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.currenDiagram.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)) 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.currenDiagram.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.currenDiagram.currentCanvas.stroke(subpaths[0], [color.rgb.blue]) if config.getOptions().VDEBUG: for a in ass: ix, iy = p1path.at(a) FeynDiagram.currenDiagram.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.currenDiagram.currentCanvas.stroke(subpaths[0], [color.rgb.red]) if config.getOptions().VDEBUG: for a in ass: ix, iy = p2path.at(a) FeynDiagram.currenDiagram.currentCanvas.fill(pyx.path.circle(ix, iy, 0.05), [color.rgb.blue]) if config.getOptions().VDEBUG: FeynDiagram.currenDiagram.currentCanvas.stroke(vispath, [color.rgb.red]) #return pyx.path.circle(-2,-1,0.2) return vispath 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)
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.currenDiagram.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))
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) 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 TeXLable object.""" 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 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, rotation=0): """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, rotation=rotation)) return self def removeLabels(self): """Remove the labels from this line.""" self.labels = [] return self 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) 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 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 def removeArrows(self): """Remove all arrows from this line.""" self.arrows = [] return self 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 def straighten(self): """Make this line a straight line between start and end.""" self.arcthrupoint = None def bend(self, amount): """Bend the line to the right by a given distance.""" 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.currenDiagram.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.currenDiagram.currentCanvas.stroke(self.getVisiblePath(), [color.rgb.blue]) return self def set3D(self, choice): """Make this line display in '3D'.""" self.is3D = choice return self def getStyles(self, stylelist): """Get the styles associated with this line.""" return self.styles def setStyles(self, stylelist): """Set the styles associated with this line.""" self.styles = stylelist return self def addStyle(self, style): """Add a style to this line.""" self.styles.append(style) return self def addStyles(self, stylelist): """Add some styles to this line.""" self.styles = self.styles + stylelist return self 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.currenDiagram.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)) 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.currenDiagram.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( lambda x, y : int(pyx.unit.tocm(x.arclen() - y.arclen()) / math.fabs(pyx.unit.tocm(x.arclen() - y.arclen()))) ) vispath = subpaths[-1] if config.getOptions().VDEBUG: FeynDiagram.currenDiagram.currentCanvas.stroke(subpaths[0], [color.rgb.blue]) if config.getOptions().VDEBUG: for a in ass: ix, iy = p1path.at(a) FeynDiagram.currenDiagram.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( lambda x, y : int(pyx.unit.tocm(x.arclen() - y.arclen()) / math.fabs(pyx.unit.tocm(x.arclen() - y.arclen()))) ) vispath = subpaths[-1] if config.getOptions().VDEBUG: FeynDiagram.currenDiagram.currentCanvas.stroke(subpaths[0], [color.rgb.red]) if config.getOptions().VDEBUG: for a in ass: ix, iy = p2path.at(a) FeynDiagram.currenDiagram.currentCanvas.fill(pyx.path.circle(ix, iy, 0.05), [color.rgb.blue]) if config.getOptions().VDEBUG: FeynDiagram.currenDiagram.currentCanvas.stroke(vispath, [color.rgb.red]) #return pyx.path.circle(-2,-1,0.2) return vispath 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)