Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib annotation arrow with TikZ style path decoration

Tags:

I have recently switched from making illutrations with TeX (PGF/TikZ) to using Matplotlib for the same purpose. The main reason is that a lot of my scientific code is in python and some of the illustrations should directly use the output of python calculations.

I have been using annotate and the fancy arrow patch, which already does a lot of what I need, such as drawing curved arrows (see e.g. here). Compared to TikZ, there is one particular thing I have been missing: path decorations (see e.g. https://tex.stackexchange.com/a/193451/96546 for an easy TikZ example). My question is if there is a way to do such TikZ-style path decorations in matplotlib?

Since this is rather broad I will set a particular task (taken from the TeX.SE question https://tex.stackexchange.com/questions/193444/sketching-simple-arrows-with-different-direction): How would I draw the following picture in Matplotlib?

enter image description here

I am aware of this question, but the implementation is not really a decoration of a line between endpoints, but rather a plotted function.

like image 285
Wolpertinger Avatar asked Feb 11 '20 15:02

Wolpertinger


People also ask

How do you draw an arrow in TikZ?

To scale the arrow head in TikZ, we add the option scale=<value> to the square brackets as follows: \draw [-{Stealth[scale=2]}] (0,0) -- (1,0); The result will be an arrow head that is twice of the size of a standard Stealth Arrowhead. We can also scale individual components of length and width.

How do I change the style in Matplotlib?

Another way to change the visual appearance of plots is to set the rcParams in a so-called style sheet and import that style sheet with matplotlib. style. use . In this way you can switch easily between different styles by simply changing the imported style sheet.


1 Answers

I created this in matplotlib with the help of the feynman package by GkAntonius (docs). This has capabilities for neat plotting of various arrow types using vertices and lines, including a style='wiggly' line style. I also added a straight start and end segment to these wiggly lines, as in the example diagram in the OP. In addition, given the comments/bounty asks for a curved/arch wiggly line, I have also included a demonstration of this functionality.

Diagram in question

enter image description here

Code:

import matplotlib.pyplot as plt
from feynman import Diagram

# Set up diagram
fig = plt.figure(figsize=(10.,10.))
ax = fig.add_axes([0,0,1,1], frameon=False)
diagram = Diagram(ax)

# Mirror line
m1 = diagram.vertex(xy=(.1,.5), marker='')
m2 = diagram.vertex(xy=(.9,.5), marker='')
mirror = diagram.line(m1, m2, arrow=False, style='double')


# Top left P-line
p1 = diagram.vertex(xy=(.2, .8), marker='')
p2 = diagram.vertex(xy=(.5, .5), marker='')
P1 = diagram.line(p1, p2, arrow=True, arrow_param={'t':0.91})
P1.text("$P$", fontsize=60, y=-.1, t=.1)


# Bottom right P-line
p3 = diagram.vertex(xy=(.5, .5), marker='')
p4 = diagram.vertex(xy=(.8, .2), marker='')
P2 = diagram.line(p3, p4, arrow=True, arrow_param={'t':0.91})
P2.text("$P$", fontsize=60, y=.05, t=.9)


# Top right P-line
p5 = diagram.vertex(xy=(.5, .5), marker='')
p6 = diagram.vertex(xy=(.8, .8), marker='')
P3 = diagram.line(p5, p6, arrow=True, arrow_param={'t':0.91})
P3.text("$P$", fontsize=60, y=-.05, t=.9)


# Top right SV-line start
sv1_s1 = diagram.vertex(xy=(.5, .5), marker='')
sv1_s2 = diagram.vertex(xy=(.51, .5175), marker='')
SV1_start = diagram.line(sv1_s1, sv1_s2, arrow=False)

# Top right SV-line middle
sv1_1 = diagram.vertex(xy=(.51, .5175), marker='')
sv1_2 = diagram.vertex(xy=(.69, .8325), marker='')
SV1 = diagram.line(sv1_1, sv1_2, arrow=False, style='wiggly', nwiggles=5)
SV1.text("$SV$", fontsize=60, y=.15, t=.9)

# Top right SV-line end
sv1_e1 = diagram.vertex(xy=(.69, .8325), marker='')
sv1_e2 = diagram.vertex(xy=(.7, .85), marker='')
SV1_end = diagram.line(sv1_e1, sv1_e2, arrow=True, arrow_param={'t':0.91})


# Bottom right SV-line start
sv2_s1 = diagram.vertex(xy=(.5, .5), marker='')
sv2_s2 = diagram.vertex(xy=(.51, .4825), marker='')
SV2_start = diagram.line(sv2_s1, sv2_s2, arrow=False)

# Bottom right SV-line middle
sv2_1 = diagram.vertex(xy=(.51, .4825), marker='')
sv2_2 = diagram.vertex(xy=(.69, .1675), marker='')
SV2 = diagram.line(sv2_1, sv2_2, arrow=False, style='wiggly', nwiggles=5)
SV2.text("$SV$", fontsize=60, y=-.15, t=.9)

# Bottom right SV-line end
sv2_e1 = diagram.vertex(xy=(.69, .1675), marker='')
sv2_e2 = diagram.vertex(xy=(.7, .15), marker='')
SV2_end = diagram.line(sv2_e1, sv2_e2, arrow=True, arrow_param={'t':0.91})

diagram.plot()
plt.show()

Curved/arched lines

enter image description here

Code

import matplotlib.pyplot as plt
from feynman import Diagram

# Set up diagram
fig = plt.figure(figsize=(8,8))
ax = fig.add_axes([0,0,1,1], frameon=False)
diagram = Diagram(ax)

# Curvy wiggly line
sv1_1 = diagram.vertex(xy=(.205, .205), marker='')
sv1_2 = diagram.vertex(xy=(.79, .79), marker='')
SV1 = diagram.line(sv1_1, sv1_2, arrow=False, style='wiggly elliptic ', nwiggles=8)
SV1.text("$1$", fontsize=50, y=.15, t=.5)

# Curvy wiggly line end
sv1_e1 = diagram.vertex(xy=(.79, .79), marker='')
sv1_e2 = diagram.vertex(xy=(.8, .8), marker='')
SV1_end = diagram.line(sv1_e1, sv1_e2, arrow=True, arrow_param={'t':0.91})


# Circular wiggly line
sv2_1 = diagram.vertex(xy=(.75, .25), marker='')
SV2 = diagram.line(sv2_1, sv2_1, arrow=False, style='wiggly circular', nwiggles=8)
SV2.text("$2$", fontsize=50, y=.15, t=.5)

diagram.plot()
plt.show()
like image 145
CDJB Avatar answered Sep 30 '22 21:09

CDJB