Skip to content

Commit b78bd36

Browse files
committed
some log optimizations
svn path=/trunk/matplotlib/; revision=933
1 parent 3c125d6 commit b78bd36

File tree

10 files changed

+189
-76
lines changed

10 files changed

+189
-76
lines changed

API_CHANGES

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ API CHANGES in matplotlib-0.72
1515
old: contour(Z, x=Y, y=Y)
1616
new: contour(X, Y, Z)
1717

18-
see http://matplotlib.sf.net/matplotlib.pylab.html#-contour
18+
see http://matplotlib.sf.net/matplotlib.pylab.html#-contour
19+
20+
21+
Increased the default resolution for save command.
22+
23+
Renamed the base attribute of the ticker classes to _base to avoid conflict
24+
with the base method. Sitt for subs
1925

2026
API CHANGES in matplotlib-0.71
2127

CHANGELOG

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
New entries should be added at the top
22

3+
2005-02-05 Some superscript text optimizations for ticking log plots
4+
35
2005-02-05 Added some default key press events to pylab figures: 'g'
46
toggles grid - JDH
57

TODO

+2
Original file line numberDiff line numberDiff line change
@@ -681,3 +681,5 @@ buttons as well for simple things? Circle/ellipse, box, arrows, etc.
681681
plot(x,y)
682682
#set(gca(), xscale='log')
683683
show()
684+
685+
--pyparsing a performance bottleneck for log ticks

examples/backend_driver.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ def drive(backend, python='python2.3'):
104104

105105
times = {}
106106
#backends = ['Agg', 'Cairo', 'GDK', 'PS', 'SVG', 'Template']
107-
backends = ['Agg', 'PS', 'SVG', 'Template']
107+
#backends = ['Agg', 'PS', 'SVG', 'Template']
108108
#backends = [ 'GTK', 'WX', 'TkAgg']
109-
#backends = ['Agg']
109+
backends = ['Agg']
110110
python = 'python2.3'
111111
for backend in backends:
112112
print 'testing %s' % backend

lib/matplotlib/axes.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -1820,11 +1820,13 @@ def loglog(self, *args, **kwargs):
18201820
18211821
* basex: base of the x logarithm
18221822
1823-
* subsx: the location of the minor ticks; None defaults to range(2,basex)
1823+
* subsx: the location of the minor ticks; None defaults to autosubs,
1824+
which depend on the number of decades in the plot
18241825
18251826
* basey: base of the y logarithm
18261827
1827-
* subsy: the location of the minor yticks; None defaults to range(2,basey)
1828+
* subsy: the location of the minor yticks; None defaults to autosubs,
1829+
which depend on the number of decades in the plot
18281830
"""
18291831
if not self._hold: self.cla()
18301832

@@ -2674,8 +2676,8 @@ def semilogx(self, *args, **kwargs):
26742676
26752677
* basex: base of the logarithm
26762678
2677-
* subsx: the location of the minor ticks; None defaults to
2678-
range(2,basex)
2679+
* subsx: the location of the minor ticks; None defaults to autosubs,
2680+
which depend on the number of decades in the plot
26792681
26802682
"""
26812683

@@ -2702,8 +2704,8 @@ def semilogy(self, *args, **kwargs):
27022704
27032705
* basey: base of the logarithm
27042706
2705-
* subsy: the location of the minor ticks; None defaults to
2706-
range(2,basey)
2707+
* subsy: the location of the minor ticks; None defaults to autosubs,
2708+
which depend on the number of decades in the plot
27072709
27082710
"""
27092711
d = {'basey': kwargs.get('basey', 10),
@@ -2796,12 +2798,13 @@ def set_xscale(self, value, basex = 10, subsx=None):
27962798
27972799
* basex: base of the logarithm
27982800
2799-
* subsx: the location of the minor ticks; None defaults to range(2,basex)
2801+
* subsx: the location of the minor ticks; None defaults to autosubs,
2802+
which depend on the number of decades in the plot
28002803
28012804
ACCEPTS: str
28022805
"""
28032806

2804-
if subsx is None: subsx = range(2, basex)
2807+
#if subsx is None: subsx = range(2, basex)
28052808
assert(value.lower() in ('log', 'linear', ))
28062809
if value == 'log':
28072810
self.xaxis.set_major_locator(LogLocator(basex))
@@ -2877,12 +2880,12 @@ def set_yscale(self, value, basey=10, subsy=None):
28772880
* basey: base of the logarithm
28782881
28792882
* subsy: the location of the minor ticks; None are the default
2880-
range(2,basex)
2883+
is to autosub
28812884
28822885
ACCEPTS: str
28832886
"""
28842887

2885-
if subsy is None: subsy = range(2, basey)
2888+
#if subsy is None: subsy = range(2, basey)
28862889
assert(value.lower() in ('log', 'linear', ))
28872890

28882891
if value == 'log':

lib/matplotlib/backends/backend_ps.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -277,18 +277,16 @@ def draw_line(self, gc, x0, y0, x1, y1):
277277
"""
278278
Draw a single line from x0,y0 to x1,y1
279279
"""
280-
ps = '%s m\n' % _nums_to_str(x0,y0)
281-
ps += '%s l' % _nums_to_str(x1,y1)
280+
ps = '%1.3f %1.3f m %1.3f %1.3f l'%(x0, y0, x1, y1)
282281
self._draw_ps(ps, gc, None, "line")
283282

284283
def _draw_lines(self, gc, points):
285284
"""
286285
Draw many lines. 'points' is a list of point coordinates.
287286
"""
288-
ps=[]
289-
ps.append("%s m"%_nums_to_str(*points[0]))
290-
for x,y in points[1:]:
291-
ps.append("%s l"%_nums_to_str(x,y))
287+
# inline this for performance
288+
ps = ["%1.3f %1.3f m" % points[0]]
289+
ps.extend(["%1.3f %1.3f l"%point for point in points[1:] ])
292290
self._draw_ps("\n".join(ps), gc, None)
293291

294292
def draw_lines(self, gc, x, y):

lib/matplotlib/mathtext.py

+16-12
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ def math_parse_s_ps(s, dpi, fontsize):
11421142

11431143
math_parse_s_ps.cache = {}
11441144

1145-
if __name__=='___main__':
1145+
if 0: #__name__=='___main__':
11461146

11471147
stests = [
11481148
r'$dz/dt \/ = \/ \gamma x^2 \/ + \/ \rm{sin}(2\pi y+\phi)$',
@@ -1174,15 +1174,19 @@ def math_parse_s_ps(s, dpi, fontsize):
11741174

11751175

11761176
if __name__=='__main__':
1177-
s = 'i'
1178-
11791177
Element.fonts = DummyFonts()
1180-
handler.clear()
1181-
expression.parseString( s )
1182-
1183-
handler.expr.set_size_info(12, 72)
1184-
1185-
# set the origin once to allow w, h compution
1186-
handler.expr.set_origin(0, 0)
1187-
for e in handler.symbols:
1188-
assert(hasattr(e, 'metrics'))
1178+
for i in range(5,20):
1179+
s = '$10^{%02d}$'%i
1180+
print 'parsing', s
1181+
w, h, fonts = math_parse_s_ft2font(s, dpi=27, fontsize=12, angle=0)
1182+
if 0:
1183+
Element.fonts = DummyFonts()
1184+
handler.clear()
1185+
expression.parseString( s )
1186+
1187+
handler.expr.set_size_info(12, 72)
1188+
1189+
# set the origin once to allow w, h compution
1190+
handler.expr.set_origin(0, 0)
1191+
for e in handler.symbols:
1192+
assert(hasattr(e, 'metrics'))

lib/matplotlib/pylab.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ def rcdefaults():
999999
draw_if_interactive()
10001000
rcdefaults.__doc__ = matplotlib.rcdefaults.__doc__
10011001

1002-
def save(fname, X, fmt='%1.4f'):
1002+
def save(fname, X, fmt='%.18e'):
10031003
"""
10041004
Save the data in X to file fname using fmt string to convert the
10051005
data to strings

lib/matplotlib/text.py

+92-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Figure and Axes text
33
"""
44
from __future__ import division
5+
import re
56
from matplotlib import verbose
67
import matplotlib
78
import math
@@ -10,9 +11,11 @@
1011
from font_manager import FontProperties
1112
from matplotlib import rcParams
1213
from patches import bbox_artist
13-
from numerix import sin, cos, pi, Matrix, cumsum
14+
from numerix import sin, cos, pi, cumsum, dot, array
1415
from transforms import lbwh_to_bbox, bbox_all
1516

17+
18+
1619
def _process_text_args(override, fontdict=None, **kwargs):
1720
"Return an override dict. See 'text' docstring for info"
1821

@@ -30,6 +33,8 @@ class Text(Artist):
3033
Handle storing and drawing of text in window or data coordinates
3134
3235
"""
36+
# special case superscripting to speedup logplots
37+
_rgxsuper = re.compile('\$([0-9]+)\^\{(-?[0-9]+)\}\$')
3338

3439
zorder = 3
3540
def __init__(self,
@@ -60,6 +65,7 @@ def __init__(self,
6065
self._fontproperties = fontproperties
6166
self._bbox = None
6267
self._renderer = None
68+
6369
def _get_multialignment(self):
6470
if self._multialignment is not None: return self._multialignment
6571
else: return self._horizontalalignment
@@ -145,7 +151,8 @@ def _get_layout(self, renderer):
145151
offsetLayout = [ (thisx, thisy) for line, thisx, thisy, w, h in horizLayout]
146152

147153
# now rotate the bbox
148-
cornersRotated = [M*Matrix([[thisx],[thisy],[1]]) for thisx, thisy in cornersHoriz]
154+
155+
cornersRotated = [dot(M,array([[thisx],[thisy],[1]])) for thisx, thisy in cornersHoriz]
149156

150157
txs = [float(v[0][0]) for v in cornersRotated]
151158
tys = [float(v[1][0]) for v in cornersRotated]
@@ -181,7 +188,7 @@ def _get_layout(self, renderer):
181188

182189

183190
# now rotate the positions around the first x,y position
184-
xys = [M*Matrix([[thisx],[thisy],[1]]) for thisx, thisy in offsetLayout]
191+
xys = [dot(M,array([[thisx],[thisy],[1]])) for thisx, thisy in offsetLayout]
185192

186193

187194
tx = [float(v[0][0])+offsetx for v in xys]
@@ -223,6 +230,23 @@ def draw(self, renderer):
223230
if self._bbox:
224231
bbox_artist(self, renderer, self._bbox)
225232
angle = self.get_rotation()
233+
234+
if angle==0:
235+
m = self._rgxsuper.match(self._text)
236+
if m is not None:
237+
bbox, info = self._get_layout_super(self._renderer, m)
238+
base, xt, yt = info[0]
239+
renderer.draw_text(gc, xt, yt, base,
240+
self._fontproperties, angle,
241+
ismath=False)
242+
243+
exponent, xt, yt, fp = info[1]
244+
renderer.draw_text(gc, xt, yt, exponent,
245+
fp, angle,
246+
ismath=False)
247+
return
248+
249+
226250
bbox, info = self._get_layout(renderer)
227251

228252
for line, wh, x, y in info:
@@ -318,10 +342,21 @@ def get_verticalalignment(self):
318342
return self._verticalalignment
319343

320344
def get_window_extent(self, renderer=None):
345+
if self._text == '':
346+
tx, ty = self._transform.xy_tup( (self._x, self._y) )
347+
return lbwh_to_bbox(tx,ty,0,0)
348+
321349
if renderer is not None:
322350
self._renderer = renderer
323351
if self._renderer is None:
324352
raise RuntimeError('Cannot get window extent w/o renderer')
353+
354+
angle = self.get_rotation()
355+
if angle==0:
356+
m = self._rgxsuper.match(self._text)
357+
if m is not None:
358+
bbox, tmp = self._get_layout_super(self._renderer, m)
359+
return bbox
325360
bbox, info = self._get_layout(self._renderer)
326361
return bbox
327362

@@ -331,22 +366,22 @@ def get_rotation_matrix(self, x0, y0):
331366

332367
theta = pi/180.0*self.get_rotation()
333368
# translate x0,y0 to origin
334-
Torigin = Matrix([ [1, 0, -x0],
369+
Torigin = array([ [1, 0, -x0],
335370
[0, 1, -y0],
336371
[0, 0, 1 ]])
337372

338373
# rotate by theta
339-
R = Matrix([ [cos(theta), -sin(theta), 0],
374+
R = array([ [cos(theta), -sin(theta), 0],
340375
[sin(theta), cos(theta), 0],
341376
[0, 0, 1]])
342377

343378
# translate origin back to x0,y0
344-
Tback = Matrix([ [1, 0, x0],
379+
Tback = array([ [1, 0, x0],
345380
[0, 1, y0],
346381
[0, 0, 1 ]])
347382

348383

349-
return Tback*R*Torigin
384+
return dot(dot(Tback,R), Torigin)
350385

351386
def set_backgroundcolor(self, color):
352387
"""
@@ -545,3 +580,53 @@ def set_fontproperties(self, fp):
545580

546581

547582

583+
584+
def _get_layout_super(self, renderer, m):
585+
"""
586+
a special case optimization if a log super and angle = 0
587+
Basically, mathtext is slow and we can do simple superscript layout "by hand"
588+
"""
589+
590+
key = self.get_prop_tup()
591+
if self.cached.has_key(key): return self.cached[key]
592+
593+
base, exponent = m.group(1), m.group(2)
594+
size = self._fontproperties.get_size_in_points()
595+
fpexp = self._fontproperties.copy()
596+
fpexp.set_size(0.7*size)
597+
wb,hb = renderer.get_text_width_height(base, self._fontproperties, False)
598+
we,he = renderer.get_text_width_height(exponent, fpexp, False)
599+
600+
w = wb+we
601+
602+
xb, yb = self._transform.xy_tup((self._x, self._y))
603+
xe = xb+wb
604+
ye = yb+0.5*hb
605+
h = ye+he-yb
606+
607+
608+
609+
610+
if self._horizontalalignment=='center': xo = -w/2.
611+
elif self._horizontalalignment=='right': xo = -w
612+
else: xo = 0
613+
if self._verticalalignment=='center': yo = -hb/2.
614+
elif self._verticalalignment=='top': yo = -hb
615+
else: yo = 0
616+
617+
xb += xo
618+
yb += yo
619+
xe += xo
620+
ye += yo
621+
bbox = lbwh_to_bbox(xb, yb, w, h)
622+
623+
if renderer.flipy():
624+
canvasw, canvash = renderer.get_canvas_width_height()
625+
yb = canvash-yb
626+
ye = canvash-ye
627+
628+
629+
val = ( bbox, ((base, xb, yb), (exponent, xe, ye, fpexp)))
630+
self.cached[key] = val
631+
632+
return val

0 commit comments

Comments
 (0)