8000 Sends paths to backend only once, and after that uses the "native" pa… · matplotlib/matplotlib@5f3177b · GitHub
[go: up one dir, main page]

Skip to content

Commit 5f3177b

Browse files
committed
Sends paths to backend only once, and after that uses the "native" path by
reference with a changing transform. Started recongfiguring patches.py to use only Paths under the hood (to take advantage of this caching). Removed many methods from backend_agg that should eventually be replaced by draw_path, at least in theory. svn path=/branches/transforms/; revision=3852
1 parent 7376a55 commit 5f3177b

File tree

8 files changed

+403
-186
lines changed

8 files changed

+403
-186
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
from __future__ import division
7-
import os, sys, warnings, copy
7+
import os, sys, warnings, copy, weakref
88

99
import numpy as npy
1010
import matplotlib.numerix.npyma as ma
@@ -17,7 +17,10 @@
1717
class RendererBase:
1818
"""An abstract base class to handle drawing/rendering operations
1919
"""
20-
20+
# This will cache paths across rendering instances
21+
# Each subclass of RenderBase should define this -->
22+
# _paths = weakref.WeakKeyDictionary()
23+
2124
def __init__(self):
2225
self._texmanager = None
2326

@@ -33,7 +36,35 @@ def close_group(self, s):
3336
"""
3437
pass
3538

36-
39+
def draw_path(self, gc, path, transform, rgbFace = None):
40+
"""
41+
Handles the caching of the native path associated with the
42+
given path and calls the underlying backend's _draw_path to
43+
actually do the drawing.
44+
"""
45+
native_path = self._native_paths.get(path)
46+
if native_path is None:
47+
import matplotlib.patches
48+
print "CACHE MISS", path
49+
native_path = self.convert_to_native_path(path)
50+
self._native_paths[path] = native_path
51+
self._draw_path(gc, native_path, transform, rgbFace)
52+
53+
def _draw_path(self, gc, native_path, transform, rgbFace):
54+
"""
55+
Draw the native path object with the given GraphicsContext and
56+
transform. The transform passed in will always be affine.
57+
"""
58+
raise NotImplementedError
59+
60+
def convert_to_native_path(self, path):
61+
"""
62+
Backends will normally will override this, but if they don't need any
63+
special optimizations, they can just have the generic path data
64+
passed to them in draw_path.
65+
"""
66+
return path
67+
3768
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
3869
rotation):
3970
"""
@@ -75,6 +106,9 @@ def option_image_nocomposite(self):
75106
"""
76107
return False
77108

109+
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
110+
pass
111+
78112
def _draw_markers(self, bgc, path, rgbFace, x, y, trans):
79113
"""
80114
This method is currently underscore hidden because the

lib/matplotlib/backends/backend_agg.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
7070
"""
7171
from __future__ import division
72-
import os, sys
72+
import os, sys, weakref
7373

7474
import numpy as npy
7575

@@ -95,7 +95,15 @@ class RendererAgg(RendererBase):
9595
The renderer handles all the drawing primitives using a graphics
9696
context instance that controls the colors/styles
9797
"""
98-
98+
# MGDTODO: Renderers seem to get created and destroyed fairly
99+
# often so the paths are cached at the class (not instance) level.
100+
# However, this dictionary is only directly used by RendererBase,
101+
# so it seems funny to define it here. However, if we didn't, the
102+
# native paths would be shared across renderers, which is
103+
# obviously bad. Seems like a good use of metaclasses, but that
104+
# also seems like a heavy solution for a minor problem.
105+
_native_paths = weakref.WeakKeyDictionary()
106+
99107
debug=1
100108
texd = {} # a cache of tex image rasters
101109
def __init__(self, width, height, dpi):
@@ -129,6 +137,12 @@ def __init__(self, width, height, dpi):
129137
if __debug__: verbose.report('RendererAgg.__init__ done',
130138
'debug-annoying')
131139

140+
def convert_to_native_path(self, path):
141+
return self._renderer.convert_to_native_path(path.vertices, path.codes)
142+
143+
def _draw_path(self, gc, native_path, transform, rgbFace):
144+
return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace)
145+
132146
def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation):
133147
"""
134148
Draw an arc centered at x,y with width and height and angles

lib/matplotlib/lines.py

Expand all lines: lib/matplotlib/lines.py
Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010

1111
import numpy as npy
1212

13-
import agg
1413
import numerix.ma as ma
1514
from matplotlib import verbose
1615
import artist
1716
from artist import Artist, setp
1817
from cbook import iterable, is_string_like, is_numlike
1918
from colors import colorConverter
20-
from transforms import Bbox
19+
from path import Path
20+
from transforms import Affine2D, Bbox
2121

2222
from matplotlib import rcParams
2323

@@ -284,9 +284,6 @@ def __init__(self, xdata, ydata,
284284
self.set_data(xdata, ydata)
285285
self._logcache = None
286286

287-
# TODO: do we really need 'newstyle'
288-
self._newstyle = False
289-
290287
def contains(self, mouseevent):
291288
"""Test whether the mouse event occurred on the line. The pick radius determines
292289
the precision of the location test (usually within five points of the value). Use
@@ -427,6 +424,7 @@ def recache(self):
427424
if len(x) != len(y):
428425
raise RuntimeError('xdata and ydata must be the same length')
429426

427+
# MGDTODO: Deal with segments
430428
mx = ma.getmask(x)
431429
my = ma.getmask(y)
432430
mask = ma.mask_or(mx, my)
@@ -439,7 +437,9 @@ def recache(self):
439437

440438
self._x = npy.asarray(x, float)
441439
self._y = npy.asarray(y, float)
442-
440+
self._path = Path(npy.vstack((self._x, self._y)).transpose(),
441+
closed=False)
442+
443443
self._logcache = None
444444

445445

@@ -508,38 +508,27 @@ def draw(self, renderer):
508508
gc.set_joinstyle(join)
509509
gc.set_capstyle(cap)
510510

511-
if self._newstyle:
512-
# transform in backend
513-
xt = self._x
514-
yt = self._y
515-
else:
516-
x, y = self._get_plottable()
517-
if len(x)==0: return
518-
xt, yt = self.get_transform().numerix_x_y(x, y)
519-
520-
521-
522511
funcname = self._lineStyles.get(self._linestyle, '_draw_nothing')
523512
lineFunc = getattr(self, funcname)
524513

514+
# MGDTODO: Deal with self._segments
525515
if self._segments is not None:
526516
for ii in self._segments:
527517
lineFunc(renderer, gc, xt[ii[0]:ii[1]], yt[ii[0]:ii[1]])
528518

529519
else:
530-
lineFunc(renderer, gc, xt, yt)
531-
532-
533-
if self._marker is not None:
534-
520+
lineFunc(renderer, gc, self._path)
521+
522+
# MGDTODO: Deal with markers
523+
if self._marker is not None and False:
535524
gc = renderer.new_gc()
536525
self._set_gc_clip(gc)
537526
gc.set_foreground(self.get_markeredgecolor())
538527
gc.set_linewidth(self._markeredgewidth)
539528
gc.set_alpha(self._alpha)
540529
funcname = self._markers.get(self._marker, '_draw_nothing')
541530
markerFunc = getattr(self, funcname)
542-
markerFunc(renderer, gc, xt, yt)
531+
markerFunc(renderer, gc, self._path)
543532

544533
#renderer.close_group('line2d')
545534

@@ -720,7 +709,7 @@ def set_dashes(self, seq):
720709
self.set_linestyle('--')
721710
self._dashSeq = seq # TODO: offset ignored for now
722711

723-
def _draw_nothing(self, renderer, gc, xt, yt):
712+
def _draw_nothing(self, renderer, gc, path):
724713
pass
725714

726715
def _draw_steps(self, renderer, gc, xt, yt):
@@ -737,13 +726,10 @@ def _draw_steps(self, renderer, gc, xt, yt):
737726
else:
738727
renderer.draw_lines(gc, xt2, yt2)
739728

740-
def _draw_solid(self, renderer, gc, xt, yt):
741-
if len(xt)<2: return
729+
def _draw_solid(self, renderer, gc, path):
730+
# if len(xt)<2: return
742731
gc.set_linestyle('solid')
743-
if self._newstyle:
744-
renderer.draw_lines(gc, xt, yt, self.get_transform())
745-
else:
746-
renderer.draw_lines(gc, xt, yt)
732+
renderer.draw_path(gc, path, self.get_transform())
747733

748734

749735
def _draw_dashed(self, renderer, gc, xt, yt):
@@ -1103,16 +1089,12 @@ def _draw_tickright(self, renderer, gc, xt, yt):
11031089
for (x,y) in zip(xt, yt):
11041090
renderer.draw_line(gc, x, y, x+offset, y)
11051091

1092+
_tickup_path = Path([[-0.5, 0.0], [-0.5, 1.0]])
11061093
def _draw_tickup(self, renderer, gc, xt, yt):
11071094
offset = renderer.points_to_pixels(self._markersize)
1108-
if self._newstyle:
1109-
path = agg.path_storage()
1110-
path.move_to(-0.5, 0)
1111-
path.line_to(-0.5, offset)
1112-
renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
1113-
else:
1114-
for (x,y) in zip(xt, yt):
1115-
renderer.draw_line(gc, x, y, x, y+offset)
1095+
marker_transform = Affine2D().scale(1.0, offset)
1096+
renderer.draw_markers(gc, self._tickup_path, marker_transform,
1097+
self._path, self.get_transform())
11161098

11171099
def _draw_tickdown(self, renderer, gc, xt, yt):
11181100
offset = renderer.points_to_pixels(self._markersize)

0 commit comments

Comments
 (0)
0