Performance issue when drawing a dotted Rectangle patch then zooming

Hello

I have a performance issue when using a Rectangle patch with linestyle 'dotted'. Here is some code showing it:

from matplotlib import gridspec

gs = gridspec.GridSpec(1, 2)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, 1])

data = [0, 1]

r1 = Rectangle([10, 0.25], 100000, 0.5, facecolor='None', edgecolor='red')
r2 = Rectangle([10, 0.25], 100000, 0.5, facecolor='None', edgecolor='red', linestyle='dotted')

ax1.add_patch(r1)
ax2.add_patch(r2)

ax1.plot(data)
ax2.plot(data)

The steps to reproduce:
- %paste the code in pylab
- select the zoom tool
- zoom on the left plot to the left of the figure until you see the data within the [0, 1] range, and zoom some more (no performance issue)
- zoom on the right plot to the left of the figure until you see the data within the [0, 1] range, the more you try zooming, the longer it takes to render
- try zooming on the left plot again, performance is now poor

So I understand I have three performance issues:
- behaviour is different depending on linestyle
- performance issue on second plot impacts first plot
- data outside of the view limits are taken into account for the rendering (performance hit even if Rectangle starts from x=10 but xlim was reduced by zooming to eg [0, 1])

I initially observed the problem in a wx application using WxAgg, I can reproduce it in pylab with TkAgg, on two separate computers.

I've tracked this down to an increasingly slow call in backend_agg.py (l.145, "self._renderer.draw_path(gc, path, transform, rgbFace)" in matplotlib 1.3.0). It then goes to native code, I stopped there.

Python 2.7.5, matplotlib 1.3.0 (also observed on 1.2.1).

(I have another issue if commenting out the two last lines and %paste-ing it to pylab, I then get an OverflowError, I don't know if this is related)

Thanks for your help

···

--
Sylvain

Hello

I have a performance issue when using a Rectangle patch with linestyle
'dotted'. Here is some code showing it:

from matplotlib import gridspec

gs = gridspec.GridSpec(1, 2)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, 1])

data = [0, 1]

r1 = Rectangle([10, 0.25], 100000, 0.5, facecolor='None',
edgecolor='red')
r2 = Rectangle([10, 0.25], 100000, 0.5, facecolor='None',
edgecolor='red', linestyle='dotted')

ax1.add_patch(r1)
ax2.add_patch(r2)

ax1.plot(data)
ax2.plot(data)

The steps to reproduce:
- %paste the code in pylab
- select the zoom tool
- zoom on the left plot to the left of the figure until you see the data
within the [0, 1] range, and zoom some more (no performance issue)
- zoom on the right plot to the left of the figure until you see the
data within the [0, 1] range, the more you try zooming, the longer it
takes to render
- try zooming on the left plot again, performance is now poor

So I understand I have three performance issues:
- behaviour is different depending on linestyle

Agg uses trapezoid rendering. To render a regular solid rectangle the trapezoid renderer only needs to manage 8 points. For a dotted line, it's (at least) 4 points per dot, and the number of dots goes into the thousands. These each must be stored in memory and repeatedly sorted as the shape is rendered.

- performance issue on second plot impacts first plot

That's not surprising. Each frame is drawn in full.

- data outside of the view limits are taken into account for the
rendering (performance hit even if Rectangle starts from x=10 but xlim
was reduced by zooming to eg [0, 1])

Yes. Generally, it is much faster to just let the renderer perform culling outside the bounds than to do it upfront, so that's why it's done that way. However, the case of dotted lines on a solid object is a degenerate case. You could try drawing each side of the rectangle as a separate line -- this would bring the line clipping algorithm into effect. (matplotlib has a line-clipping algorithm, but it does not have a solid polygon clipping algorithm).

Mike

···

On 09/04/2013 12:47 PM, Sylvain L�V�QUE wrote:

I initially observed the problem in a wx application using WxAgg, I can
reproduce it in pylab with TkAgg, on two separate computers.

I've tracked this down to an increasingly slow call in backend_agg.py
(l.145, "self._renderer.draw_path(gc, path, transform, rgbFace)" in
matplotlib 1.3.0). It then goes to native code, I stopped there.

Python 2.7.5, matplotlib 1.3.0 (also observed on 1.2.1).

(I have another issue if commenting out the two last lines and
%paste-ing it to pylab, I then get an OverflowError, I don't know if
this is related)

Thanks for your help