Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 40d4b36

Browse files
committed
Use Python lists rather than linked lists to improve speed
svn path=/trunk/matplotlib/; revision=3654
1 parent 7e3648e commit 40d4b36

1 file changed

Lines changed: 76 additions & 94 deletions

File tree

lib/matplotlib/mathtext.py

Lines changed: 76 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -850,52 +850,41 @@ def get_underline_thickness(self, font, fontsize, dpi):
850850
# Percentage of x-height of additional horiz. space after sub/superscripts
851851
SCRIPT_SPACE = 0.3
852852
# Percentage of x-height that sub/superscripts drop below the baseline
853-
SUBDROP = 0.4
853+
SUBDROP = 0.3
854854
# Percentage of x-height that superscripts drop below the baseline
855855
SUP1 = 0.7
856856
# Percentage of x-height that subscripts drop below the baseline
857857
SUB1 = 0.0
858858
# Percentage of x-height that superscripts are offset relative to the subscript
859-
DELTA = 0.1
859+
DELTA = 0.25
860860

861861
class MathTextWarning(Warning):
862862
pass
863863

864864
class Node(object):
865-
"""A node in a linked list.
865+
"""A node in the TeX box model
866866
@133
867867
"""
868868
def __init__(self):
869-
self.link = None
870869
self.size = 0
871870

872871
def __repr__(self):
873-
s = self.__internal_repr__()
874-
if self.link:
875-
s += ' ' + self.link.__repr__()
876-
return s
872+
return self.__internal_repr__()
877873

878874
def __internal_repr__(self):
879875
return self.__class__.__name__
880876

881877
def get_kerning(self, next):
882878
return 0.0
883879

884-
def set_link(self, other):
885-
self.link = other
886-
887880
def shrink(self):
888881
"""Shrinks one level smaller. There are only three levels of sizes,
889882
after which things will no longer get smaller."""
890-
if self.link:
891-
self.link.shrink()
892883
self.size += 1
893884

894885
def grow(self):
895886
"""Grows one level larger. There is no limit to how big something
896887
can get."""
897-
if self.link:
898-
self.link.grow()
899888
self.size -= 1
900889

901890
def render(self, x, y):
@@ -1027,29 +1016,17 @@ class List(Box):
10271016
def __init__(self, elements):
10281017
Box.__init__(self, 0., 0., 0.)
10291018
self.shift_amount = 0. # An arbitrary offset
1030-
self.list_head = None # The head of a linked list of Nodes in this box
1019+
self.children = elements # The child nodes of this list
10311020
# The following parameters are set in the vpack and hpack functions
10321021
self.glue_set = 0. # The glue setting of this list
10331022
self.glue_sign = 0 # 0: normal, -1: shrinking, 1: stretching
10341023
self.glue_order = 0 # The order of infinity (0 - 3) for the glue
1035-
1036-
# Convert the Python list to a linked list
1037-
if len(elements):
1038-
elem = self.list_head = elements[0]
1039-
for next in elements[1:]:
1040-
elem.set_link(next)
1041-
elem = next
10421024

10431025
def __repr__(self):
1044-
s = '[%s <%d %d %d %d> ' % (self.__internal_repr__(),
1045-
self.width, self.height,
1046-
self.depth, self.shift_amount)
1047-
if self.list_head:
1048-
s += ' ' + self.list_head.__repr__()
1049-
s += ']'
1050-
if self.link:
1051-
s += ' ' + self.link.__repr__()
1052-
return s
1026+
return '[%s <%d %d %d %d> %s]' % (self.__internal_repr__(),
1027+
self.width, self.height,
1028+
self.depth, self.shift_amount,
1029+
' '.join(self.children))
10531030

10541031
def _determine_order(self, totals):
10551032
"""A helper function to determine the highest order of glue
@@ -1071,21 +1048,21 @@ def _set_glue(self, x, sign, totals, error_type):
10711048
self.glue_sign = 0
10721049
self.glue_ratio = 0.
10731050
if o == 0:
1074-
if self.list_head is not None:
1051+
if len(self.children):
10751052
warn("%s %s: %r" % (error_type, self.__class__.__name__, self),
10761053
MathTextWarning)
10771054

10781055
def shrink(self):
1079-
if self.list_head:
1080-
self.list_head.shrink()
1056+
for child in self.children:
1057+
child.shrink()
10811058
Box.shrink(self)
10821059
if self.size < NUM_SIZE_LEVELS:
10831060
self.shift_amount *= SHRINK_FACTOR
10841061
self.glue_set *= SHRINK_FACTOR
10851062

10861063
def grow(self):
1087-
if self.list_head:
1088-
self.list_head.grow()
1064+
for child in self.children:
1065+
child.grow()
10891066
Box.grow(self)
10901067
self.shift_amount *= INV_SHRINK_FACTOR
10911068
self.glue_set *= INV_SHRINK_FACTOR
@@ -1103,15 +1080,21 @@ def kern(self):
11031080
Chars themselves determine the amount of kerning they need
11041081
(in get_kerning), and this function just creates the linked
11051082
list in the correct way."""
1106-
elem = self.list_head
1107-
while elem is not None:
1108-
next = elem.link
1083+
new_children = []
1084+
num_children = len(self.children)
1085+
for i in range(num_children):
1086+
elem = self.children[i]
1087+
if i < num_children - 1:
1088+
next = self.children[i + 1]
1089+
else:
1090+
next = None
1091+
1092+
new_children.append(elem)
11091093
kerning_distance = elem.get_kerning(next)
11101094
if kerning_distance != 0.:
11111095
kern = Kern(kerning_distance)
1112-
elem.link = kern
1113-
kern.link = next
1114-
elem = next
1096+
new_children.append(kern)
1097+
self.children = new_children
11151098

11161099
def hpack(self, w=0., m='additional'):
11171100
"""The main duty of hpack is to compute the dimensions of the
@@ -1136,18 +1119,12 @@ def hpack(self, w=0., m='additional'):
11361119
x = 0.
11371120
total_stretch = [0.] * 4
11381121
total_shrink = [0.] * 4
1139-
p = self.list_head
1140-
while p is not None:
1141-
# Layout characters in a tight inner loop (common case)
1142-
while isinstance(p, Char):
1122+
for p in self.children:
1123+
if isinstance(p, Char):
11431124
x += p.width
11441125
h = max(h, p.height)
11451126
d = max(d, p.depth)
1146-
p = p.link # Go to next node in list
1147-
if p is None:
1148-
break
1149-
1150-
if isinstance(p, Box):
1127+
elif isinstance(p, Box):
11511128
x += p.width
11521129
if p.height is not None and p.depth is not None:
11531130
s = getattr(p, 'shift_amount', 0.)
@@ -1160,7 +1137,6 @@ def hpack(self, w=0., m='additional'):
11601137
total_shrink[glue_spec.shrink_order] += glue_spec.shrink
11611138
elif isinstance(p, Kern):
11621139
x += p.width
1163-
p = p.link # Go to next node in list
11641140
self.height = h
11651141
self.depth = d
11661142

@@ -1207,11 +1183,8 @@ def vpack(self, h=0., m='additional', l=float('inf')):
12071183
x = 0.
12081184
total_stretch = [0.] * 4
12091185
total_shrink = [0.] * 4
1210-
p = self.list_head
1211-
while p is not None:
1212-
if isinstance(p, Char):
1213-
raise RuntimeError("Internal mathtext error: Char node found in Vlist.")
1214-
elif isinstance(p, Box):
1186+
for p in self.children:
1187+
if isinstance(p, Box):
12151188
x += d + p.height
12161189
d = p.depth
12171190
if p.width is not None:
@@ -1227,8 +1200,9 @@ def vpack(self, h=0., m='additional', l=float('inf')):
12271200
elif isinstance(p, Kern):
12281201
x += d + p.width
12291202
d = 0.
1230-
p = p.link
1231-
1203+
elif isinstance(p, Char):
1204+
raise RuntimeError("Internal mathtext error: Char node found in Vlist.")
1205+
12321206
self.width = w
12331207
if d > l:
12341208
x += d - l
@@ -1482,23 +1456,18 @@ def hlist_out(self, box):
14821456
cur_glue = 0.
14831457
glue_order = box.glue_order
14841458
glue_sign = box.glue_sign
1485-
p = box.list_head
14861459
base_line = self.cur_v
14871460
left_edge = self.cur_h
14881461
self.cur_s += 1
14891462
self.max_push = max(self.cur_s, self.max_push)
14901463

1491-
while p:
1492-
while isinstance(p, Char):
1464+
for p in box.children:
1465+
if isinstance(p, Char):
14931466
p.render(self.cur_h + self.off_h, self.cur_v + self.off_v)
14941467
self.cur_h += p.width
1495-
p = p.link
1496-
if p is None:
1497-
break
1498-
1499-
if isinstance(p, List):
1468+
elif isinstance(p, List):
15001469
# @623
1501-
if p.list_head is None:
1470+
if len(p.children) == 0:
15021471
self.cur_h += p.width
15031472
else:
15041473
edge = self.cur_h
@@ -1542,26 +1511,22 @@ def hlist_out(self, box):
15421511
self.cur_h += rule_width
15431512
elif isinstance(p, Kern):
15441513
self.cur_h += p.width
1545-
p = p.link
15461514
self.cur_s -= 1
15471515

15481516
def vlist_out(self, box):
15491517
cur_g = 0
15501518
cur_glue = 0.
15511519
glue_order = box.glue_order
15521520
glue_sign = box.glue_sign
1553-
p = box.list_head
15541521
self.cur_s += 1
15551522
self.max_push = max(self.max_push, self.cur_s)
15561523
left_edge = self.cur_h
15571524
self.cur_v -= box.height
15581525
top_edge = self.cur_v
15591526

1560-
while p:
1561-
if isinstance(p, Char):
1562-
raise RuntimeError("Internal mathtext error: Char node found in vlist")
1563-
elif isinstance(p, List):
1564-
if p.list_head is None:
1527+
for p in box.children:
1528+
if isinstance(p, List):
1529+
if len(p.children) == 0:
15651530
self.cur_v += p.height + p.depth
15661531
else:
15671532
self.cur_v += p.height
@@ -1601,8 +1566,8 @@ def vlist_out(self, box):
16011566
self.cur_v += rule_height
16021567
elif isinstance(p, Kern):
16031568
self.cur_v += p.width
1604-
1605-
p = p.link
1569+
elif isinstance(p, Char):
1570+
raise RuntimeError("Internal mathtext error: Char node found in vlist")
16061571
self.cur_s -= 1
16071572

16081573
ship = Ship()
@@ -1657,14 +1622,16 @@ class Parser(object):
16571622
_punctuation_symbols = Set(r', ; . ! \ldotp \cdotp'.split())
16581623

16591624
_overunder_symbols = Set(r'''
1660-
\sum \prod \int \coprod \oint \bigcap \bigcup \bigsqcup \bigvee
1625+
\sum \prod \coprod \bigcap \bigcup \bigsqcup \bigvee
16611626
\bigwedge \bigodot \bigotimes \bigoplus \biguplus
16621627
'''.split()
16631628
)
16641629

16651630
_overunder_functions = Set(
16661631
r"lim liminf limsup sup max min".split()
16671632
)
1633+
1634+
_dropsub_symbols = Set(r'''\int \oint'''.split())
16681635

16691636
def __init__(self):
16701637
# All forward declarations are here
@@ -1843,6 +1810,7 @@ def __init__(self):
18431810
def clear(self):
18441811
self._expr = None
18451812
self._state_stack = None
1813+
self._em_width_cache = {}
18461814

18471815
def parse(self, s, fonts_object, fontsize, dpi):
18481816
self._state_stack = [self.State(fonts_object, 'default', fontsize, dpi)]
@@ -1898,10 +1866,14 @@ def non_math(self, s, loc, toks):
18981866
def _make_space(self, percentage):
18991867
# All spaces are relative to em width
19001868
state = self.get_state()
1901-
metrics = state.font_output.get_metrics(
1902-
state.font, 'm', state.fontsize, state.dpi)
1903-
em = metrics.advance
1904-
return Kern(em * percentage)
1869+
key = (state.font, state.fontsize, state.dpi)
1870+
width = self._em_width_cache.get(key)
1871+
if width is None:
1872+
metrics = state.font_output.get_metrics(
1873+
state.font, 'm', state.fontsize, state.dpi)
1874+
width = metrics.advance
1875+
self._em_width_cache[key] = width
1876+
return Kern(width * percentage)
19051877

19061878
_space_widths = { r'\ ' : 0.3,
19071879
r'\,' : 0.4,
@@ -1919,17 +1891,19 @@ def space(self, s, loc, toks):
19191891
def symbol(self, s, loc, toks):
19201892
# print "symbol", toks
19211893
c = toks[0]
1894+
try:
1895+
char = Char(c, self.get_state())
1896+
except ValueError:
1897+
raise ParseFatalException("Unknown symbol: %s" % c)
1898+
19221899
if c in self._spaced_symbols:
19231900
return [Hlist( [self._make_space(0.2),
1924-
Char(c, self.get_state()),
1901+
char,
19251902
self._make_space(0.2)] )]
19261903
elif c in self._punctuation_symbols:
1927-
return [Hlist( [Char(c, self.get_state()),
1904+
return [Hlist( [char,
19281905
self._make_space(0.2)] )]
1929-
try:
1930-
return [Char(toks[0], self.get_state())]
1931-
except ValueError:
1932-
raise ParseFatalException("Unknown symbol: %s" % c)
1906+
return [char]
19331907

19341908
_accent_map = {
19351909
r'\hat' : r'\circumflexaccent',
@@ -2004,6 +1978,11 @@ def is_overunder(self, nucleus):
20041978
elif isinstance(nucleus, Hlist) and hasattr(nucleus, 'function_name'):
20051979
return nucleus.function_name in self._overunder_functions
20061980
return False
1981+
1982+
def is_dropsub(self, nucleus):
1983+
if isinstance(nucleus, Char):
1984+
return nucleus.c in self._dropsub_symbols
1985+
return False
20071986

20081987
def subsuperscript(self, s, loc, toks):
20091988
assert(len(toks)==1)
@@ -2079,7 +2058,10 @@ def subsuperscript(self, s, loc, toks):
20792058
return [result]
20802059

20812060
shift_up = nucleus.height - SUBDROP * xHeight
2082-
shift_down = SUBDROP * xHeight
2061+
if self.is_dropsub(nucleus):
2062+
shift_down = nucleus.depth + SUBDROP * xHeight
2063+
else:
2064+
shift_down = SUBDROP * xHeight
20832065
if super is None:
20842066
# @757
20852067
sub.shrink()
@@ -2091,8 +2073,8 @@ def subsuperscript(self, s, loc, toks):
20912073
x.shift_amount = shift_down
20922074
else:
20932075
super.shrink()
2094-
x = Hlist([super])
2095-
x.width += SCRIPT_SPACE * xHeight
2076+
x = Hlist([super, Kern(SCRIPT_SPACE * xHeight)])
2077+
# x.width += SCRIPT_SPACE * xHeight
20962078
clr = SUP1 * xHeight
20972079
shift_up = max(shift_up, clr)
20982080
clr = x.depth + (abs(xHeight) / 4.0)
@@ -2104,11 +2086,11 @@ def subsuperscript(self, s, loc, toks):
21042086
y = Hlist([sub])
21052087
y.width += SCRIPT_SPACE * xHeight
21062088
shift_down = max(shift_down, SUB1 * xHeight)
2107-
clr = 4.0 * rule_thickness - ((shift_up - x.depth) - (y.height - shift_down))
2089+
clr = 2.0 * rule_thickness - ((shift_up - x.depth) - (y.height - shift_down))
21082090
if clr > 0.:
21092091
shift_up += clr
21102092
shift_down += clr
2111-
x.shift_amount = DELTA * xHeight
2093+
x.shift_amount = DELTA * (shift_up + shift_down)
21122094
x = Vlist([x,
21132095
Kern((shift_up - x.depth) - (y.height - shift_down)),
21142096
y])

0 commit comments

Comments
 (0)