You can subscribe to this list here.
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(115) |
Aug
(120) |
Sep
(137) |
Oct
(170) |
Nov
(461) |
Dec
(263) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2008 |
Jan
(120) |
Feb
(74) |
Mar
(35) |
Apr
(74) |
May
(245) |
Jun
(356) |
Jul
(240) |
Aug
(115) |
Sep
(78) |
Oct
(225) |
Nov
(98) |
Dec
(271) |
2009 |
Jan
(132) |
Feb
(84) |
Mar
(74) |
Apr
(56) |
May
(90) |
Jun
(79) |
Jul
(83) |
Aug
(296) |
Sep
(214) |
Oct
(76) |
Nov
(82) |
Dec
(66) |
2010 |
Jan
(46) |
Feb
(58) |
Mar
(51) |
Apr
(77) |
May
(58) |
Jun
(126) |
Jul
(128) |
Aug
(64) |
Sep
(50) |
Oct
(44) |
Nov
(48) |
Dec
(54) |
2011 |
Jan
(68) |
Feb
(52) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2018 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
|
|
|
1
(3) |
2
(3) |
3
|
4
(2) |
5
(9) |
6
(4) |
7
(9) |
8
(7) |
9
(2) |
10
(3) |
11
(2) |
12
|
13
(2) |
14
(10) |
15
(24) |
16
(17) |
17
(21) |
18
(3) |
19
(23) |
20
(6) |
21
(4) |
22
(14) |
23
(11) |
24
(15) |
25
(6) |
26
(1) |
27
(4) |
28
(3) |
29
(9) |
30
(6) |
31
(2) |
|
From: <jr...@us...> - 2008-10-31 14:54:45
|
Revision: 6360 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6360&view=rev Author: jrevans Date: 2008-10-31 14:54:42 +0000 (Fri, 31 Oct 2008) Log Message: ----------- Added unit handling for axhspan and axvspan. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-10-31 13:06:59 UTC (rev 6359) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-10-31 14:54:42 UTC (rev 6360) @@ -2876,9 +2876,16 @@ .. plot:: mpl_examples/pylab_examples/axhspan_demo.py """ - # convert y axis units trans = mtransforms.blended_transform_factory( self.transAxes, self.transData) + + # process the unit information + self._process_unit_info( [xmin, xmax], [ymin, ymax], **kwargs ) + + # first we need to strip away the units + xmin, xmax = self.convert_xunits( [xmin, xmax] ) + ymin, ymax = self.convert_yunits( [ymin, ymax] ) + verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) @@ -2923,9 +2930,16 @@ :meth:`axhspan`: for example plot and source code """ - # convert x axis units trans = mtransforms.blended_transform_factory( self.transData, self.transAxes) + + # process the unit information + self._process_unit_info( [xmin, xmax], [ymin, ymax], **kwargs ) + + # first we need to strip away the units + xmin, xmax = self.convert_xunits( [xmin, xmax] ) + ymin, ymax = self.convert_yunits( [ymin, ymax] ) + verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-31 13:07:10
|
Revision: 6359 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6359&view=rev Author: mdboom Date: 2008-10-31 13:06:59 +0000 (Fri, 31 Oct 2008) Log Message: ----------- Added custom colorbar ticking example, contributed by Scott Sinclair Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/colorbar_tick_labelling_demo.py Added: trunk/matplotlib/examples/pylab_examples/colorbar_tick_labelling_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/colorbar_tick_labelling_demo.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/colorbar_tick_labelling_demo.py 2008-10-31 13:06:59 UTC (rev 6359) @@ -0,0 +1,36 @@ +"""Produce custom labelling for a colorbar. + +Contributed by Scott Sinclair +""" + +import matplotlib.pyplot as plt +import numpy as np + +from numpy.random import randn + +# Make plot with vertical (default) colorbar +fig = plt.figure() +ax = fig.add_subplot(111) + +data = np.clip(randn(250, 250), -1, 1) + +cax = ax.imshow(data, interpolation='nearest') +ax.set_title('Gaussian noise with vertical colorbar') + +# Add colorbar, make sure to specify tick locations to match desired ticklabels +cbar = fig.colorbar(cax, ticks=[-1, 0, 1]) +cbar.ax.set_yticklabels(['< -1', '0', '> 1'])# vertically oriented colorbar + +# Make plot with horizontal colorbar +fig = plt.figure() +ax = fig.add_subplot(111) + +data = np.clip(randn(250, 250), -1, 1) + +cax = ax.imshow(data, interpolation='nearest') +ax.set_title('Gaussian noise with horizontal colorbar') + +cbar = fig.colorbar(cax, ticks=[-1, 0, 1], orientation='horizontal') +cbar.ax.set_xticklabels(['Low', 'Medium', 'High'])# horizontal colorbar + +plt.show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-30 15:26:58
|
Revision: 6358 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6358&view=rev Author: mdboom Date: 2008-10-30 15:26:48 +0000 (Thu, 30 Oct 2008) Log Message: ----------- Fix bug in font_manager.py that was printing math junk on the docbuild machine. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/font_manager.py Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-30 14:58:47 UTC (rev 6357) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-30 15:26:48 UTC (rev 6358) @@ -1200,6 +1200,7 @@ best_score = 1e64 best_font = None + for font in fontlist: # Matching family should have highest priority, so it is multiplied # by 10.0 @@ -1216,7 +1217,7 @@ if score == 0: break - if best_font is None or best_score > 10.0: + if best_font is None or best_score >= 10.0: verbose.report('findfont: Could not match %s. Returning %s' % (prop, self.defaultFont)) result = self.defaultFont This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-10-30 14:58:51
|
Revision: 6357 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6357&view=rev Author: jswhit Date: 2008-10-30 14:58:47 +0000 (Thu, 30 Oct 2008) Log Message: ----------- add /usr to GEOS search dirs Modified Paths: -------------- trunk/toolkits/basemap/setup.py Modified: trunk/toolkits/basemap/setup.py =================================================================== --- trunk/toolkits/basemap/setup.py 2008-10-30 14:56:51 UTC (rev 6356) +++ trunk/toolkits/basemap/setup.py 2008-10-30 14:58:47 UTC (rev 6357) @@ -45,7 +45,7 @@ if GEOS_dir is None: # if GEOS_dir not set, check a few standard locations. - GEOS_dirs = ['/usr/local','/sw','/opt','/opt/local',os.path.expanduser('~')] + GEOS_dirs = ['/usr','/usr/local','/sw','/opt','/opt/local',os.path.expanduser('~')] for direc in GEOS_dirs: geos_version = checkversion(direc) print 'checking for GEOS lib in %s ....' % direc This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-30 14:56:54
|
Revision: 6356 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6356&view=rev Author: mdboom Date: 2008-10-30 14:56:51 +0000 (Thu, 30 Oct 2008) Log Message: ----------- Improve appearance of markers in Agg backend. Modified Paths: -------------- trunk/matplotlib/src/_backend_agg.cpp Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-10-30 14:33:30 UTC (rev 6355) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-10-30 14:56:51 UTC (rev 6356) @@ -490,7 +490,9 @@ trans *= agg::trans_affine_translation(0.0, (double)height); PathIterator marker_path(marker_path_obj); - bool marker_snap = should_snap(marker_path, marker_trans); + // The built-in markers look better if snapping is turned on. + const bool marker_snap = true; + // bool marker_snap = should_snap(marker_path, marker_trans); transformed_path_t marker_path_transformed(marker_path, marker_trans); simplify_t marker_path_simplified(marker_path_transformed, marker_snap, false, width, height); curve_t marker_path_curve(marker_path_simplified); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-10-30 14:33:34
|
Revision: 6355 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6355&view=rev Author: jdh2358 Date: 2008-10-30 14:33:30 +0000 (Thu, 30 Oct 2008) Log Message: ----------- removed py2.6 incompat Modified Paths: -------------- trunk/matplotlib/doc/_templates/gallery.html trunk/matplotlib/doc/_templates/indexsidebar.html trunk/matplotlib/examples/pylab_examples/annotation_demo2.py trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/doc/_templates/gallery.html =================================================================== --- trunk/matplotlib/doc/_templates/gallery.html 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/doc/_templates/gallery.html 2008-10-30 14:33:30 UTC (rev 6355) @@ -63,6 +63,10 @@ <a href="examples/pylab_examples/alignment_test.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/alignment_test.png" border="0" alt="alignment_test"/></a> +<a href="examples/pylab_examples/annotation_demo2.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo2_00.png" border="0" alt="annotation_demo2"/></a> + +<a href="examples/pylab_examples/annotation_demo2.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo2_01.png" border="0" alt="annotation_demo2"/></a> + <a href="examples/pylab_examples/annotation_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo_00.png" border="0" alt="annotation_demo"/></a> <a href="examples/pylab_examples/annotation_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo_01.png" border="0" alt="annotation_demo"/></a> Modified: trunk/matplotlib/doc/_templates/indexsidebar.html =================================================================== --- trunk/matplotlib/doc/_templates/indexsidebar.html 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/doc/_templates/indexsidebar.html 2008-10-30 14:33:30 UTC (rev 6355) @@ -11,7 +11,7 @@ and mapping toolkit <a href="http://matplotlib.sf.net/basemap/doc/html">basemap</a>.</p> -<p>Please <a href="https://sourceforge.net/my/donations.php">donate</a> +<p>Please <a href="http://sourceforge.net/project/project_donations.php?group_id=80706">donate</a> to support matplotlib development.</p> <h3>Need help?</h3> Modified: trunk/matplotlib/examples/pylab_examples/annotation_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/annotation_demo2.py 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/examples/pylab_examples/annotation_demo2.py 2008-10-30 14:33:30 UTC (rev 6355) @@ -72,7 +72,7 @@ - fig.savefig('annotation_connection') + #fig.savefig('annotation_connection') if 1: @@ -82,7 +82,7 @@ el = Ellipse((2, -1), 0.5, 0.5) ax.add_patch(el) - + ax.annotate('$->$', xy=(2., -1), xycoords='data', xytext=(-150, -140), textcoords='offset points', bbox=dict(boxstyle="round", fc="0.8"), @@ -146,6 +146,6 @@ ) ) - fig.savefig('annotation_arrowstyle') + #fig.savefig('annotation_arrowstyle') show() Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-10-30 14:33:30 UTC (rev 6355) @@ -1512,10 +1512,10 @@ (x0, y0)] com = [Path.MOVETO, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, Path.CLOSEPOLY] path = Path(cp, com) @@ -1536,8 +1536,8 @@ BboxTransmuterBase.__init__(self) def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): - + # padding pad = mutation_size * self.pad @@ -1590,16 +1590,16 @@ left_saw_y = [y1] + \ [y1 - tooth_size2 - dsy*.5* i for i in range(dsy_n*2)] + \ [y0 + tooth_size2] - + saw_vertices = zip(bottom_saw_x, bottom_saw_y) + \ zip(right_saw_x, right_saw_y) + \ zip(top_saw_x, top_saw_y) + \ zip(left_saw_x, left_saw_y) + \ [(bottom_saw_x[0], bottom_saw_y[0])] - + return saw_vertices - + def transmute(self, x0, y0, width, height, mutation_size): saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) @@ -1926,7 +1926,7 @@ patch. Additionally the path can be shirnked by a fixed size (given in points) with shrinkA and shrinkB. """ - + class SimpleEvent: def __init__(self, xy): self.x, self.y = xy @@ -1938,7 +1938,7 @@ methods of each patch object is utilized to test if the point is inside the path. """ - + if patchA: def insideA(xy_display): #xy_display = patchA.get_data_transform().transform_point(xy_data) @@ -1949,7 +1949,7 @@ left, right = split_path_inout(path, insideA) except ValueError: right = path - + path = right if patchB: @@ -1969,10 +1969,10 @@ #def insideB(xy_data): # return ppp.contains_point(xy_data) ##return patchB.contains(ConnectorBase.SimpleEvent(xy))[0] - + return path - + def _shrink(self, path, shrinkA, shrinkB): """ Shrink the path by fixed size (in points) with shrinkA and shrinkB @@ -1992,7 +1992,7 @@ path = left return path - + def __call__(self, posA, posB, shrinkA=2., shrinkB=2., patchA=None, patchB=None): @@ -2000,7 +2000,7 @@ clipped_path = self._clip(path, patchA, patchB) shrinked_path = self._shrink(clipped_path, shrinkA, shrinkB) - + return shrinked_path @@ -2013,7 +2013,7 @@ """ def __init__(self, rad=0.): self.rad = rad - + def connect(self, posA, posB): x1, y1 = posA x2, y2 = posB @@ -2021,7 +2021,7 @@ dx, dy = x2 - x1, y2 - y1 f = self.rad - + cx, cy = x12 + f*dy, y12 - f*dx vertices = [(x1, y1), @@ -2037,7 +2037,7 @@ class Angle3Connector(ConnectorBase): """ Creates a simple quadratic bezier curve between two points. The middle control points is placed at the intersecting - point of two lines which crosses the start (or end) point + point of two lines which crosses the start (or end) point and has a angle of angleA (or angleB). """ def __init__(self, angleA=90, angleB=0): @@ -2049,9 +2049,9 @@ x2, y2 = posB cosA, sinA = math.cos(self.angleA/180.*math.pi),\ - math.sin(self.angleA/180.*math.pi), + math.sin(self.angleA/180.*math.pi), cosB, sinB = math.cos(self.angleB/180.*math.pi),\ - math.sin(self.angleB/180.*math.pi), + math.sin(self.angleB/180.*math.pi), cx, cy = get_intersection(x1, y1, cosA, sinA, x2, y2, cosB, sinB) @@ -2075,15 +2075,15 @@ self.angleB = angleB self.rad = rad - + def connect(self, posA, posB): x1, y1 = posA x2, y2 = posB cosA, sinA = math.cos(self.angleA/180.*math.pi),\ - math.sin(self.angleA/180.*math.pi), + math.sin(self.angleA/180.*math.pi), cosB, sinB = math.cos(self.angleB/180.*math.pi),\ - -math.sin(self.angleB/180.*math.pi), + -math.sin(self.angleB/180.*math.pi), cx, cy = get_intersection(x1, y1, cosA, sinA, x2, y2, cosB, sinB) @@ -2099,10 +2099,10 @@ (cx, cy), (cx + self.rad * cosB, cy + self.rad * sinB)]) codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3]) - + vertices.append((x2, y2)) codes.append(Path.LINETO) - + return Path(vertices, codes) @@ -2122,7 +2122,7 @@ self.armB = armB self.rad = rad - + def connect(self, posA, posB): x1, y1 = posA x2, y2 = posB @@ -2130,14 +2130,14 @@ vertices = [(x1, y1)] rounded = [] codes = [Path.MOVETO] - + if self.armA: cosA = math.cos(self.angleA/180.*math.pi) sinA = math.sin(self.angleA/180.*math.pi) #x_armA, y_armB d = self.armA - self.rad rounded.append((x1 + d*cosA, y1 + d*sinA)) - d = self.armA + d = self.armA rounded.append((x1 + d*cosA, y1 + d*sinA)) if self.armB: @@ -2149,7 +2149,7 @@ xp, yp = rounded[-1] dx, dy = x_armB - xp, y_armB - yp dd = (dx*dx + dy*dy)**.5 - + rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) vertices.extend(rounded) codes.extend([Path.LINETO, @@ -2163,7 +2163,7 @@ d = dd - self.rad rounded = [(xp + d*dx/dd, yp + d*dy/dd), (x_armB, y_armB)] - + if rounded: xp, yp = rounded[-1] dx, dy = x2 - xp, y2 - yp @@ -2177,7 +2177,7 @@ vertices.append((x2, y2)) codes.append(Path.LINETO) - + return Path(vertices, codes) @@ -2216,7 +2216,7 @@ assert segments[0][1] == Path.MOVETO assert segments[1][1] == Path.CURVE3 - + return list(segments[0][0]) + list(segments[1][0]) @@ -2284,7 +2284,7 @@ def _get_pad_projected(self, x0, y0, x1, y1, linewidth): # when no arrow head is drawn - + dx, dy = x0 - x1, y0 - y1 cp_distance = math.sqrt(dx**2 + dy**2) @@ -2298,7 +2298,7 @@ ddy = pady_projected * dy / cp_distance return ddx, ddy - + def _get_arrow_wedge(self, x0, y0, x1, y1, head_dist, cos_t, sin_t, linewidth ): @@ -2338,7 +2338,7 @@ return vertices_arrow, codes_arrow, ddx, ddy - + def transmute(self, path, mutation_size, linewidth): head_length, head_width = self.head_length * mutation_size, \ @@ -2360,7 +2360,7 @@ verticesA, codesA = [], [] #ddxA, ddyA = self._get_pad_projected(x1, y1, x0, y0, linewidth) ddxA, ddyA = 0., 0., #self._get_pad_projected(x1, y1, x0, y0, linewidth) - + # end arrow x2, y2 = path.vertices[-2] x3, y3 = path.vertices[-1] @@ -2443,8 +2443,8 @@ def transmute(self, path, mutation_size, linewidth): x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - # divide the path into a head and a tail + + # divide the path into a head and a tail head_length = self.head_length * mutation_size in_f = inside_circle(x2, y2, head_length) arrow_path = [(x0, y0), (x1, y1), (x2, y2)] @@ -2497,8 +2497,8 @@ def transmute(self, path, mutation_size, linewidth): x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - # divide the path into a head and a tail + + # divide the path into a head and a tail head_length = self.head_length * mutation_size arrow_path = [(x0, y0), (x1, y1), (x2, y2)] @@ -2510,7 +2510,7 @@ tolerence=0.01) path_head = path_in - # path for head + # path for head in_f = inside_circle(x2, y2, head_length*.8) path_out, path_in = \ split_bezier_intersecting_with_closedpath(arrow_path, @@ -2530,7 +2530,7 @@ tail_width*.5, w1=1., wm=0.6, w2=0.3) - # path for head + # path for head in_f = inside_circle(x0, y0, tail_width*.3) path_in, path_out = \ split_bezier_intersecting_with_closedpath(arrow_path, @@ -2593,7 +2593,7 @@ def transmute(self, path, mutation_size, linewidth): x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] b_plus, b_minus = make_wedged_bezier2(arrow_path, self.tail_width * mutation_size / 2., @@ -2676,12 +2676,12 @@ "angle":AngleConnector, "angle3":Angle3Connector, } - + def __str__(self): return self.__class__.__name__ \ + "FancyArrowPatch(%g,%g,%g,%g,%g,%g)" % tuple(self._q_bezier) - def __init__(self, posA=None, posB=None, + def __init__(self, posA=None, posB=None, path=None, arrowstyle="simple", arrow_transmuter=None, @@ -2752,7 +2752,7 @@ elif posA is None and posB is None and path is not None: self._posA_posB = None - self._connetors = None + self._connetors = None else: raise ValueError("either posA and posB, or path need to provided") @@ -2760,7 +2760,7 @@ self.patchB = patchB self.shrinkA = shrinkA self.shrinkB = shrinkB - + Patch.__init__(self, **kwargs) self._path_original = path @@ -2857,7 +2857,7 @@ """ Set the connector. - ACCEPTS: + ACCEPTS: """ self._connector = connector @@ -2897,7 +2897,7 @@ except KeyError: raise ValueError("Unknown Arrowstyle : %s" % arrowstyle_name) try: - arrowstyle_args_pair = [as.split("=") for as in as_list[1:]] + arrowstyle_args_pair = [a.split("=") for a in as_list[1:]] arrowstyle_args = dict([(k, float(v)) for k, v in arrowstyle_args_pair]) except ValueError: raise ValueError("Incorrect Arrowstyle argument : %s" % arrowstyle) @@ -2953,7 +2953,7 @@ """ _path = self.get_path_in_displaycoord() return self.get_transform().inverted().transform_path(_path) - + def get_path_in_displaycoord(self): """ Return the mutated path of the arrow in the display coord @@ -2972,16 +2972,16 @@ _path = self.get_transform().transform_path(self._path_original) - + _path, closed = self.get_arrow_transmuter()(_path, self.get_mutation_scale(), self.get_linewidth(), self.get_mutation_aspect() ) - + if not closed: self.fill = False - + return _path This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-30 13:41:51
|
Revision: 6354 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6354&view=rev Author: mdboom Date: 2008-10-30 13:41:45 +0000 (Thu, 30 Oct 2008) Log Message: ----------- Don't render backlinks in FAQ -- the underlines are distracting and inconsistent with the rest of the docs... ;) Modified Paths: -------------- trunk/matplotlib/doc/faq/environment_variables_faq.rst trunk/matplotlib/doc/faq/howto_faq.rst trunk/matplotlib/doc/faq/index.rst trunk/matplotlib/doc/faq/installing_faq.rst trunk/matplotlib/doc/faq/troubleshooting_faq.rst Modified: trunk/matplotlib/doc/faq/environment_variables_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/environment_variables_faq.rst 2008-10-30 13:18:07 UTC (rev 6353) +++ trunk/matplotlib/doc/faq/environment_variables_faq.rst 2008-10-30 13:41:45 UTC (rev 6354) @@ -5,7 +5,9 @@ ********************* .. contents:: + :backlinks: none + .. envvar:: HOME The user's home directory. On linux, :envvar:`~ <HOME>` is shorthand for :envvar:`HOME`. Modified: trunk/matplotlib/doc/faq/howto_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/howto_faq.rst 2008-10-30 13:18:07 UTC (rev 6353) +++ trunk/matplotlib/doc/faq/howto_faq.rst 2008-10-30 13:41:45 UTC (rev 6354) @@ -5,6 +5,7 @@ ***** .. contents:: + :backlinks: none .. _howto-plotting: Modified: trunk/matplotlib/doc/faq/index.rst =================================================================== --- trunk/matplotlib/doc/faq/index.rst 2008-10-30 13:18:07 UTC (rev 6353) +++ trunk/matplotlib/doc/faq/index.rst 2008-10-30 13:41:45 UTC (rev 6354) @@ -1,8 +1,8 @@ .. _faq-index: -#################### - The Matplotlib FAQ -#################### +################## +The Matplotlib FAQ +################## .. htmlonly:: Modified: trunk/matplotlib/doc/faq/installing_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/installing_faq.rst 2008-10-30 13:18:07 UTC (rev 6353) +++ trunk/matplotlib/doc/faq/installing_faq.rst 2008-10-30 13:41:45 UTC (rev 6354) @@ -6,6 +6,7 @@ .. contents:: + :backlinks: none Report a compilation problem Modified: trunk/matplotlib/doc/faq/troubleshooting_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/troubleshooting_faq.rst 2008-10-30 13:18:07 UTC (rev 6353) +++ trunk/matplotlib/doc/faq/troubleshooting_faq.rst 2008-10-30 13:41:45 UTC (rev 6354) @@ -5,6 +5,7 @@ *************** .. contents:: + :backlinks: none .. _matplotlib-version: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-10-30 13:18:15
|
Revision: 6353 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6353&view=rev Author: jdh2358 Date: 2008-10-30 13:18:07 +0000 (Thu, 30 Oct 2008) Log Message: ----------- added jae-joons fancy arrow and box patch for annotations Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/patches.py trunk/matplotlib/lib/matplotlib/text.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/annotation_demo2.py trunk/matplotlib/lib/matplotlib/bezier.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-10-29 20:28:57 UTC (rev 6352) +++ trunk/matplotlib/CHANGELOG 2008-10-30 13:18:07 UTC (rev 6353) @@ -1,3 +1,7 @@ +2008-10-24 Added Jae Joon's fancy arrow, box and annotation + enhancements -- see + examples/pylab_examples/annotation_demo2.py + 2008-10-23 Autoscaling is now supported with shared axes - EF 2008-10-23 Fixed exception in dviread that happened with Minion - JKS Added: trunk/matplotlib/examples/pylab_examples/annotation_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/annotation_demo2.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/annotation_demo2.py 2008-10-30 13:18:07 UTC (rev 6353) @@ -0,0 +1,151 @@ + +from matplotlib.pyplot import figure, show +from matplotlib.patches import Ellipse +import numpy as np + +if 1: + fig = figure(1,figsize=(8,5)) + ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1,5), ylim=(-4,3)) + + t = np.arange(0.0, 5.0, 0.01) + s = np.cos(2*np.pi*t) + line, = ax.plot(t, s, lw=3, color='purple') + + ax.annotate('arrowstyle', xy=(0, 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + arrowprops=dict(arrowstyle="->") + ) + + ax.annotate('arc3', xy=(0.5, -1), xycoords='data', + xytext=(-30, -30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="arc3,rad=.2") + ) + + ax.annotate('arc', xy=(1., 1), xycoords='data', + xytext=(-40, 30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="arc,angleA=0,armA=30,rad=10"), + ) + + ax.annotate('arc', xy=(1.5, -1), xycoords='data', + xytext=(-40, -30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="arc,angleA=0,armA=20,angleB=-90,armB=15,rad=7"), + ) + + ax.annotate('angle', xy=(2., 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="angle,angleA=0,angleB=90,rad=10"), + ) + + ax.annotate('angle3', xy=(2.5, -1), xycoords='data', + xytext=(-50, -30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="angle3,angleA=0,angleB=-90"), + ) + + + ax.annotate('angle', xy=(3., 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="->", + connectionstyle="angle,angleA=0,angleB=90,rad=10"), + ) + + ax.annotate('angle', xy=(3.5, -1), xycoords='data', + xytext=(-70, -60), textcoords='offset points', + size=20, + bbox=dict(boxstyle="round4,pad=.5", fc="0.8"), + arrowprops=dict(arrowstyle="->", + connectionstyle="angle,angleA=0,angleB=-90,rad=10"), + ) + + ax.annotate('angle', xy=(4., 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="->", + shrinkA=0, shrinkB=10, + connectionstyle="angle,angleA=0,angleB=90,rad=10"), + ) + + + + fig.savefig('annotation_connection') + + +if 1: + fig = figure(2) + fig.clf() + ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1,5), ylim=(-5,3)) + + el = Ellipse((2, -1), 0.5, 0.5) + ax.add_patch(el) + + ax.annotate('$->$', xy=(2., -1), xycoords='data', + xytext=(-150, -140), textcoords='offset points', + bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="->", + patchB=el, + connectionstyle="angle,angleA=90,angleB=0,rad=10"), + ) + + ax.annotate('fancy', xy=(2., -1), xycoords='data', + xytext=(-100, 60), textcoords='offset points', + size=20, + #bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="fancy", + fc="0.6", ec="none", + patchB=el, + connectionstyle="angle3,angleA=0,angleB=-90"), + ) + + ax.annotate('simple', xy=(2., -1), xycoords='data', + xytext=(100, 60), textcoords='offset points', + size=20, + #bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="simple", + fc="0.6", ec="none", + patchB=el, + connectionstyle="arc3,rad=0.3"), + ) + + ax.annotate('wedge', xy=(2., -1), xycoords='data', + xytext=(-100, -100), textcoords='offset points', + size=20, + #bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="wedge,tail_width=0.7", + fc="0.6", ec="none", + patchB=el, + connectionstyle="arc3,rad=-0.3"), + ) + + + ann = ax.annotate('wedge', xy=(2., -1), xycoords='data', + xytext=(0, -45), textcoords='offset points', + size=20, + bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec=(1., .5, .5)), + arrowprops=dict(arrowstyle="wedge,tail_width=1.", + fc=(1.0, 0.7, 0.7), ec=(1., .5, .5), + patchA=None, + patchB=el, + relpos=(0.2, 0.8), + connectionstyle="arc3,rad=-0.1"), + ) + + ann = ax.annotate('wedge', xy=(2., -1), xycoords='data', + xytext=(35, 0), textcoords='offset points', + size=20, va="center", + bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec="none"), + arrowprops=dict(arrowstyle="wedge,tail_width=1.", + fc=(1.0, 0.7, 0.7), ec="none", + patchA=None, + patchB=el, + relpos=(0.2, 0.5), + ) + ) + + fig.savefig('annotation_arrowstyle') + +show() Added: trunk/matplotlib/lib/matplotlib/bezier.py =================================================================== --- trunk/matplotlib/lib/matplotlib/bezier.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/bezier.py 2008-10-30 13:18:07 UTC (rev 6353) @@ -0,0 +1,478 @@ +""" +A module providing some utility functions regarding bezier path manipulation. +""" + + +import numpy as np +from math import sqrt + +from matplotlib.path import Path + +from operator import xor + + +# some functions + +def get_intersection(cx1, cy1, cos_t1, sin_t1, + cx2, cy2, cos_t2, sin_t2): + """ return a intersecting point between a line through (cx1, cy1) + and having angle t1 and a line through (cx2, cy2) and angle t2. + """ + + # line1 => sin_t1 * (x - cx1) - cos_t1 * (y - cy1) = 0. + # line1 => sin_t1 * x + cos_t1 * y = sin_t1*cx1 - cos_t1*cy1 + + line1_rhs = sin_t1 * cx1 - cos_t1 * cy1 + line2_rhs = sin_t2 * cx2 - cos_t2 * cy2 + + # rhs matrix + a, b = sin_t1, -cos_t1 + c, d = sin_t2, -cos_t2 + + ad_bc = a*d-b*c + if ad_bc == 0.: + raise ValueError("Given lines do not intersect") + + #rhs_inverse + a_, b_ = d, -b + c_, d_ = -c, a + a_, b_, c_, d_ = [k / ad_bc for k in [a_, b_, c_, d_]] + + x = a_* line1_rhs + b_ * line2_rhs + y = c_* line1_rhs + d_ * line2_rhs + + return x, y + + + +def get_normal_points(cx, cy, cos_t, sin_t, length): + """ + For a line passing through (*cx*, *cy*) and having a angle *t*, + return locations of the two points located along its perpendicular line at the distance of *length*. + """ + + if length == 0.: + return cx, cy, cx, cy + + cos_t1, sin_t1 = sin_t, -cos_t + cos_t2, sin_t2 = -sin_t, cos_t + + x1, y1 = length*cos_t1 + cx, length*sin_t1 + cy + x2, y2 = length*cos_t2 + cx, length*sin_t2 + cy + + return x1, y1, x2, y2 + + + + +## BEZIER routines + + + + + +# subdividing bezier curve +# http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-sub.html + +def _de_casteljau1(beta, t): + next_beta = beta[:-1] * (1-t) + beta[1:] * t + return next_beta + +def split_de_casteljau(beta, t): + """split a bezier segment defined by its controlpoints *beta* + into two separate segment divided at *t* and return their control points. + + """ + beta = np.asarray(beta) + beta_list = [beta] + while True: + beta = _de_casteljau1(beta, t) + beta_list.append(beta) + if len(beta) == 1: + break + left_beta = [beta[0] for beta in beta_list] + right_beta = [beta[-1] for beta in reversed(beta_list)] + + return left_beta, right_beta + + + + + + + +def find_bezier_t_intersecting_with_closedpath(bezier_point_at_t, inside_closedpath, + t0=0., t1=1., tolerence=0.01): + """ Find a parameter t0 and t1 of the given bezier path which + bounds the intersecting points with a provided closed + path(*inside_closedpath*). Search starts from *t0* and *t1* and it + uses a simple bisecting algorithm therefore one of the end point + must be inside the path while the orther doesn't. The search stop + when |t0-t1| gets smaller than the given tolerence. + value for + + - bezier_point_at_t : a function which returns x, y coordinates at *t* + + - inside_closedpath : return True if the point is insed the path + + """ + # inside_closedpath : function + + start = bezier_point_at_t(t0) + end = bezier_point_at_t(t1) + + start_inside = inside_closedpath(start) + end_inside = inside_closedpath(end) + + if not xor(start_inside, end_inside): + raise ValueError("the segment does not seemed to intersect with the path") + + while 1: + + # return if the distance is smaller than the tolerence + if (start[0]-end[0])**2 + (start[1]-end[1])**2 < tolerence**2: + return t0, t1 + + # calculate the middle point + middle_t = 0.5*(t0+t1) + middle = bezier_point_at_t(middle_t) + middle_inside = inside_closedpath(middle) + + if xor(start_inside, middle_inside): + t1 = middle_t + end = middle + end_inside = middle_inside + else: + t0 = middle_t + start = middle + start_inside = middle_inside + + + + + +class BezierSegment: + """ + A simple class of a 2-dimensional bezier segment + """ + + # Highrt order bezier lines can be supported by simplying adding + # correcponding values. + _binom_coeff = {1:np.array([1., 1.]), + 2:np.array([1., 2., 1.]), + 3:np.array([1., 3., 3., 1.])} + + def __init__(self, control_points): + """ + *control_points* : location of contol points. It needs have a + shpae of n * 2, where n is the order of the bezier line. 1<= + n <= 3 is supported. + """ + _o = len(control_points) + self._orders = np.arange(_o) + _coeff = BezierSegment._binom_coeff[_o - 1] + + _control_points = np.asarray(control_points) + xx = _control_points[:,0] + yy = _control_points[:,1] + + self._px = xx * _coeff + self._py = yy * _coeff + + def point_at_t(self, t): + "evaluate a point at t" + one_minus_t_powers = np.power(1.-t, self._orders)[::-1] + t_powers = np.power(t, self._orders) + + tt = one_minus_t_powers * t_powers + _x = sum(tt * self._px) + _y = sum(tt * self._py) + + return _x, _y + + +def split_bezier_intersecting_with_closedpath(bezier, + inside_closedpath, + tolerence=0.01): + + """ + bezier : control points of the bezier segment + inside_closedpath : a function which returns true if the point is inside the path + """ + + bz = BezierSegment(bezier) + bezier_point_at_t = bz.point_at_t + + t0, t1 = find_bezier_t_intersecting_with_closedpath(bezier_point_at_t, + inside_closedpath, + tolerence=tolerence) + + _left, _right = split_de_casteljau(bezier, (t0+t1)/2.) + return _left, _right + + + +def find_r_to_boundary_of_closedpath(inside_closedpath, xy, + cos_t, sin_t, + rmin=0., rmax=1., tolerence=0.01): + """ + Find a radius r (centered at *xy*) between *rmin* and *rmax* at + which it intersect with the path. + + inside_closedpath : function + cx, cy : center + cos_t, sin_t : cosine and sine for the angle + rmin, rmax : + """ + + cx, cy = xy + def _f(r): + return cos_t*r + cx, sin_t*r + cy + + find_bezier_t_intersecting_with_closedpath(_f, inside_closedpath, + t0=rmin, t1=rmax, tolerence=tolerence) + + + +## matplotlib specific + +def split_path_inout(path, inside, tolerence=0.01, reorder_inout=False): + """ divide a path into two segment at the point where inside(x, y) + becomes False. + """ + + path_iter = path.iter_segments() + + ctl_points, command = path_iter.next() + begin_inside = inside(ctl_points[-2:]) # true if begin point is inside + + bezier_path = None + ctl_points_old = ctl_points + + concat = np.concatenate + + iold=0 + i = 1 + + for ctl_points, command in path_iter: + iold=i + i += len(ctl_points)/2 + if inside(ctl_points[-2:]) != begin_inside: + bezier_path = concat([ctl_points_old[-2:], ctl_points]) + break + + ctl_points_old = ctl_points + + if bezier_path is None: + raise ValueError("The path does not seem to intersect with the patch") + + bp = zip(bezier_path[::2], bezier_path[1::2]) + left, right = split_bezier_intersecting_with_closedpath(bp, + inside, + tolerence) + if len(left) == 2: + codes_left = [Path.LINETO] + codes_right = [Path.MOVETO, Path.LINETO] + elif len(left) == 3: + codes_left = [Path.CURVE3, Path.CURVE3] + codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3] + elif len(left) == 4: + codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4] + codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4] + else: + raise ValueError() + + verts_left = left[1:] + verts_right = right[:] + + #i += 1 + + if path.codes is None: + path_in = Path(concat([path.vertices[:i], verts_left])) + path_out = Path(concat([verts_right, path.vertices[i:]])) + + else: + path_in = Path(concat([path.vertices[:iold], verts_left]), + concat([path.codes[:iold], codes_left])) + + path_out = Path(concat([verts_right, path.vertices[i:]]), + concat([codes_right, path.codes[i:]])) + + if reorder_inout and begin_inside == False: + path_in, path_out = path_out, path_in + + return path_in, path_out + + + + + +def inside_circle(cx, cy, r): + r2 = r**2 + def _f(xy): + x, y = xy + return (x-cx)**2 + (y-cy)**2 < r2 + return _f + + + +# quadratic bezier lines + +def get_cos_sin(x0, y0, x1, y1): + dx, dy = x1-x0, y1-y0 + d = (dx*dx + dy*dy)**.5 + return dx/d, dy/d + + +def get_parallels(bezier2, width): + """ + Given the quadraitc bezier control points *bezier2*, returns + control points of quadrativ bezier lines roughly parralel to given + one separated by *width*. + """ + + # The parallel bezier lines constructed by following ways. + # c1 and c2 are contol points representing the begin and end of the bezier line. + # cm is the middle point + c1x, c1y = bezier2[0] + cmx, cmy = bezier2[1] + c2x, c2y = bezier2[2] + + # t1 and t2 is the anlge between c1 and cm, cm, c2. + # They are also a angle of the tangential line of the path at c1 and c2 + cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy) + cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c2x, c2y) + + # find c1_left, c1_right which are located along the lines + # throught c1 and perpendicular to the tangential lines of the + # bezier path at a distance of width. Same thing for c2_left and + # c2_right with respect to c2. + c1x_left, c1y_left, c1x_right, c1y_right = \ + get_normal_points(c1x, c1y, cos_t1, sin_t1, width) + c2x_left, c2y_left, c2x_right, c2y_right = \ + get_normal_points(c2x, c2y, cos_t2, sin_t2, width) + + # find cm_left which is the intersectng point of a line through + # c1_left with angle t1 and a line throught c2_left with angle + # t2. Same with cm_right. + cmx_left, cmy_left = get_intersection(c1x_left, c1y_left, cos_t1, sin_t1, + c2x_left, c2y_left, cos_t2, sin_t2) + + cmx_right, cmy_right = get_intersection(c1x_right, c1y_right, cos_t1, sin_t1, + c2x_right, c2y_right, cos_t2, sin_t2) + + # the parralel bezier lines are created with control points of + # [c1_left, cm_left, c2_left] and [c1_right, cm_right, c2_right] + path_left = [(c1x_left, c1y_left), (cmx_left, cmy_left), (c2x_left, c2y_left)] + path_right = [(c1x_right, c1y_right), (cmx_right, cmy_right), (c2x_right, c2y_right)] + + return path_left, path_right + + + +def make_wedged_bezier2(bezier2, length, shrink_factor=0.5): + """ + Being similar to get_parallels, returns + control points of two quadrativ bezier lines having a width roughly parralel to given + one separated by *width*. + """ + + xx1, yy1 = bezier2[2] + xx2, yy2 = bezier2[1] + xx3, yy3 = bezier2[0] + + cx, cy = xx3, yy3 + x0, y0 = xx2, yy2 + + dist = sqrt((x0-cx)**2 + (y0-cy)**2) + cos_t, sin_t = (x0-cx)/dist, (y0-cy)/dist, + + x1, y1, x2, y2 = get_normal_points(cx, cy, cos_t, sin_t, length) + + xx12, yy12 = (xx1+xx2)/2., (yy1+yy2)/2., + xx23, yy23 = (xx2+xx3)/2., (yy2+yy3)/2., + + dist = sqrt((xx12-xx23)**2 + (yy12-yy23)**2) + cos_t, sin_t = (xx12-xx23)/dist, (yy12-yy23)/dist, + + xm1, ym1, xm2, ym2 = get_normal_points(xx2, yy2, cos_t, sin_t, length*shrink_factor) + + l_plus = [(x1, y1), (xm1, ym1), (xx1, yy1)] + l_minus = [(x2, y2), (xm2, ym2), (xx1, yy1)] + + return l_plus, l_minus + + +def find_control_points(c1x, c1y, mmx, mmy, c2x, c2y): + """ Find control points of the bezier line throught c1, mm, c2. We + simply assume that c1, mm, c2 which have parameteric value 0, 0.5, and 1. + """ + + cmx = .5 * (4*mmx - (c1x + c2x)) + cmy = .5 * (4*mmy - (c1y + c2y)) + + return [(c1x, c1y), (cmx, cmy), (c2x, c2y)] + + +def make_wedged_bezier2(bezier2, width, w1=1., wm=0.5, w2=0.): + """ + Being similar to get_parallels, returns + control points of two quadrativ bezier lines having a width roughly parralel to given + one separated by *width*. + """ + + # c1, cm, c2 + c1x, c1y = bezier2[0] + cmx, cmy = bezier2[1] + c3x, c3y = bezier2[2] + + + # t1 and t2 is the anlge between c1 and cm, cm, c3. + # They are also a angle of the tangential line of the path at c1 and c3 + cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy) + cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c3x, c3y) + + # find c1_left, c1_right which are located along the lines + # throught c1 and perpendicular to the tangential lines of the + # bezier path at a distance of width. Same thing for c3_left and + # c3_right with respect to c3. + c1x_left, c1y_left, c1x_right, c1y_right = \ + get_normal_points(c1x, c1y, cos_t1, sin_t1, width*w1) + c3x_left, c3y_left, c3x_right, c3y_right = \ + get_normal_points(c3x, c3y, cos_t2, sin_t2, width*w2) + + + + + # find c12, c23 and c123 which are middle points of c1-cm, cm-c3 and c12-c23 + c12x, c12y = (c1x+cmx)*.5, (c1y+cmy)*.5 + c23x, c23y = (cmx+c3x)*.5, (cmy+c3y)*.5 + c123x, c123y = (c12x+c23x)*.5, (c12y+c23y)*.5 + + # tangential angle of c123 (angle between c12 and c23) + cos_t123, sin_t123 = get_cos_sin(c12x, c12y, c23x, c23y) + + c123x_left, c123y_left, c123x_right, c123y_right = \ + get_normal_points(c123x, c123y, cos_t123, sin_t123, width*wm) + + + path_left = find_control_points(c1x_left, c1y_left, + c123x_left, c123y_left, + c3x_left, c3y_left) + path_right = find_control_points(c1x_right, c1y_right, + c123x_right, c123y_right, + c3x_right, c3y_right) + + return path_left, path_right + + + + +if 0: + path = Path([(0, 0), (1, 0), (2, 2)], + [Path.MOVETO, Path.CURVE3, Path.CURVE3]) + left, right = divide_path_inout(path, inside) + clf() + ax = gca() + + Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-10-29 20:28:57 UTC (rev 6352) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-10-30 13:18:07 UTC (rev 6353) @@ -1475,7 +1475,153 @@ return path +class Round4BoxTransmuter(BboxTransmuterBase): + """ + A box with round edges. + """ + def __init__(self, pad=0.3, rounding_size=None): + self.pad = pad + self.rounding_size = rounding_size + BboxTransmuterBase.__init__(self) + + def transmute(self, x0, y0, width, height, mutation_size): + + # padding + pad = mutation_size * self.pad + + # roudning size. Use a half of the pad if not set. + if self.rounding_size: + dr = mutation_size * self.rounding_size + else: + dr = pad / 2. + + width, height = width + 2.*pad - 2*dr, \ + height + 2.*pad - 2*dr, + + + x0, y0 = x0-pad+dr, y0-pad+dr, + x1, y1 = x0+width, y0 + height + + + cp = [(x0, y0), + (x0+dr, y0-dr), (x1-dr, y0-dr), (x1, y0), + (x1+dr, y0+dr), (x1+dr, y1-dr), (x1, y1), + (x1-dr, y1+dr), (x0+dr, y1+dr), (x0, y1), + (x0-dr, y1-dr), (x0-dr, y0+dr), (x0, y0), + (x0, y0)] + + com = [Path.MOVETO, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CLOSEPOLY] + + path = Path(cp, com) + + return path + + + + +class SawtoothBoxTransmuter(BboxTransmuterBase): + """ + A sawtooth box. + """ + + def __init__(self, pad=0.3, tooth_size=None): + self.pad = pad + self.tooth_size = tooth_size + BboxTransmuterBase.__init__(self) + + def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): + + + # padding + pad = mutation_size * self.pad + + # size of sawtooth + if self.tooth_size is None: + tooth_size = self.pad * .5 * mutation_size + else: + tooth_size = self.tooth_size * mutation_size + + tooth_size2 = tooth_size / 2. + width, height = width + 2.*pad - tooth_size, \ + height + 2.*pad - tooth_size, + + # the sizes of the vertical and horizontal sawtooth are + # separately adjusted to fit the given box size. + dsx_n = int(round((width - tooth_size) / (tooth_size * 2))) * 2 + dsx = (width - tooth_size) / dsx_n + dsy_n = int(round((height - tooth_size) / (tooth_size * 2))) * 2 + dsy = (height - tooth_size) / dsy_n + + + x0, y0 = x0-pad+tooth_size2, y0-pad+tooth_size2 + x1, y1 = x0+width, y0 + height + + + bottom_saw_x = [x0] + \ + [x0 + tooth_size2 + dsx*.5* i for i in range(dsx_n*2)] + \ + [x1 - tooth_size2] + bottom_saw_y = [y0] + \ + [y0 - tooth_size2, y0, y0 + tooth_size2, y0] * dsx_n + \ + [y0 - tooth_size2] + + right_saw_x = [x1] + \ + [x1 + tooth_size2, x1, x1 - tooth_size2, x1] * dsx_n + \ + [x1 + tooth_size2] + right_saw_y = [y0] + \ + [y0 + tooth_size2 + dsy*.5* i for i in range(dsy_n*2)] + \ + [y1 - tooth_size2] + + top_saw_x = [x1] + \ + [x1 - tooth_size2 - dsx*.5* i for i in range(dsx_n*2)] + \ + [x0 + tooth_size2] + top_saw_y = [y1] + \ + [y1 + tooth_size2, y1, y1 - tooth_size2, y1] * dsx_n + \ + [y1 + tooth_size2] + + left_saw_x = [x0] + \ + [x0 - tooth_size2, x0, x0 + tooth_size2, x0] * dsy_n + \ + [x0 - tooth_size2] + left_saw_y = [y1] + \ + [y1 - tooth_size2 - dsy*.5* i for i in range(dsy_n*2)] + \ + [y0 + tooth_size2] + + saw_vertices = zip(bottom_saw_x, bottom_saw_y) + \ + zip(right_saw_x, right_saw_y) + \ + zip(top_saw_x, top_saw_y) + \ + zip(left_saw_x, left_saw_y) + \ + [(bottom_saw_x[0], bottom_saw_y[0])] + + return saw_vertices + + + def transmute(self, x0, y0, width, height, mutation_size): + + saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) + path = Path(saw_vertices) + return path + + +class RoundtoothBoxTransmuter(SawtoothBoxTransmuter): + """ + A roundtooth(?) box. + """ + + def transmute(self, x0, y0, width, height, mutation_size): + + saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) + + cp = [Path.MOVETO] + ([Path.CURVE3, Path.CURVE3] * ((len(saw_vertices)-1)//2)) + path = Path(saw_vertices, cp) + + return path + + def _list_available_boxstyles(transmuters): """ a helper function of the :class:`FancyBboxPatch` to list the available box styles. It inspects the arguments of the __init__ methods of @@ -1520,6 +1666,9 @@ _fancy_bbox_transmuters = {"square":SquareBoxTransmuter, "round":RoundBoxTransmuter, + "round4":Round4BoxTransmuter, + "sawtooth":SawtoothBoxTransmuter, + "roundtooth":RoundtoothBoxTransmuter, } def __str__(self): @@ -1585,6 +1734,7 @@ __init__.__doc__ = cbook.dedent(__init__.__doc__) % kwdoc del kwdoc + @classmethod def list_available_boxstyles(cls): return _list_available_boxstyles(cls._fancy_bbox_transmuters) @@ -1758,3 +1908,1123 @@ def get_bbox(self): return transforms.Bbox.from_bounds(self._x, self._y, self._width, self._height) + + + + +from matplotlib.bezier import split_bezier_intersecting_with_closedpath +from matplotlib.bezier import get_intersection, inside_circle, get_parallels +from matplotlib.bezier import make_wedged_bezier2 +from matplotlib.bezier import split_path_inout, inside_circle + +class ConnectorBase(object): + """ The ConnectorClass is used to define a path between a two + points. This class is used in the FancyArrowPatch class. It + creates a path between point A and point B. When optional patch + objects (pathcA & patchB) are provided and if they enclose the + point A or B, the path is clipped to the boundary of the each + patch. Additionally the path can be shirnked by a fixed size + (given in points) with shrinkA and shrinkB. + """ + + class SimpleEvent: + def __init__(self, xy): + self.x, self.y = xy + + def _clip(self, path, patchA, patchB): + """ Clip the path to the boundary of the patchA and patchB. + The starting point of the path needed to be inside of the + patchA and the end point inside the patch B. The contains + methods of each patch object is utilized to test if the point + is inside the path. + """ + + if patchA: + def insideA(xy_display): + #xy_display = patchA.get_data_transform().transform_point(xy_data) + xy_event = ConnectorBase.SimpleEvent(xy_display) + return patchA.contains(xy_event)[0] + + try: + left, right = split_path_inout(path, insideA) + except ValueError: + right = path + + path = right + + if patchB: + def insideB(xy_display): + #xy_display = patchB.get_data_transform().transform_point(xy_data) + xy_event = ConnectorBase.SimpleEvent(xy_display) + return patchB.contains(xy_event)[0] + + try: + left, right = split_path_inout(path, insideB) + except ValueError: + left = path + + path = left + + #ppp = patchB.get_patch_transform().transform_path(patchB.get_path()) + #def insideB(xy_data): + # return ppp.contains_point(xy_data) + ##return patchB.contains(ConnectorBase.SimpleEvent(xy))[0] + + return path + + + def _shrink(self, path, shrinkA, shrinkB): + """ + Shrink the path by fixed size (in points) with shrinkA and shrinkB + """ + if shrinkA: + x, y = path.vertices[0] + insideA = inside_circle(x, y, shrinkA) + + left, right = split_path_inout(path, insideA) + path = right + + if shrinkB: + x, y = path.vertices[-1] + insideB = inside_circle(x, y, shrinkB) + + left, right = split_path_inout(path, insideB) + path = left + + return path + + def __call__(self, posA, posB, + shrinkA=2., shrinkB=2., patchA=None, patchB=None): + + path = self.connect(posA, posB) + + clipped_path = self._clip(path, patchA, patchB) + shrinked_path = self._shrink(clipped_path, shrinkA, shrinkB) + + return shrinked_path + + +class Arc3Connector(ConnectorBase): + """ Creates a simple quadratic bezier curve between two + points. The curve is created so that the middle contol points (C1) + is located at the same distance from the start (C0) and end + points(C2) and the distance of the C1 to the line connecting C0-C2 + is *rad* times the distance of C0-C2. + """ + def __init__(self, rad=0.): + self.rad = rad + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + x12, y12 = (x1 + x2)/2., (y1 + y2)/2. + dx, dy = x2 - x1, y2 - y1 + + f = self.rad + + cx, cy = x12 + f*dy, y12 - f*dx + + vertices = [(x1, y1), + (cx, cy), + (x2, y2)] + codes = [Path.MOVETO, + Path.CURVE3, + Path.CURVE3] + + return Path(vertices, codes) + + +class Angle3Connector(ConnectorBase): + """ Creates a simple quadratic bezier curve between two + points. The middle control points is placed at the intersecting + point of two lines which crosses the start (or end) point + and has a angle of angleA (or angleB). + """ + def __init__(self, angleA=90, angleB=0): + self.angleA = angleA + self.angleB = angleB + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + + cosA, sinA = math.cos(self.angleA/180.*math.pi),\ + math.sin(self.angleA/180.*math.pi), + cosB, sinB = math.cos(self.angleB/180.*math.pi),\ + math.sin(self.angleB/180.*math.pi), + + cx, cy = get_intersection(x1, y1, cosA, sinA, + x2, y2, cosB, sinB) + + vertices = [(x1, y1), (cx, cy), (x2, y2)] + codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] + + return Path(vertices, codes) + + +class AngleConnector(ConnectorBase): + """ Creates a picewise continuous quadratic bezier path between + two points. The path has a one passing-through point placed at the + intersecting point of two lines which crosses the start (or end) + point and has a angle of angleA (or angleB). The connecting edges are + rounded with *rad*. + """ + + def __init__(self, angleA=90, angleB=0, rad=0.): + self.angleA = angleA + self.angleB = angleB + + self.rad = rad + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + + cosA, sinA = math.cos(self.angleA/180.*math.pi),\ + math.sin(self.angleA/180.*math.pi), + cosB, sinB = math.cos(self.angleB/180.*math.pi),\ + -math.sin(self.angleB/180.*math.pi), + + cx, cy = get_intersection(x1, y1, cosA, sinA, + x2, y2, cosB, sinB) + + vertices = [(x1, y1)] + codes = [Path.MOVETO] + + if self.rad == 0.: + vertices.append((cx, cy)) + codes.append(Path.LINETO) + else: + vertices.extend([(cx - self.rad * cosA, cy - self.rad * sinA), + (cx, cy), + (cx + self.rad * cosB, cy + self.rad * sinB)]) + codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3]) + + vertices.append((x2, y2)) + codes.append(Path.LINETO) + + return Path(vertices, codes) + + + +class ArcConnector(ConnectorBase): + """ Creates a picewise continuous quadratic bezier path between + two points. The path can have two passing-through points, a point + placed at the distance of armA and angle of angleA from point A, + another point with respect to point B. The edges are rounded with + *rad*. + """ + + def __init__(self, angleA=0, angleB=0, armA=None, armB=None, rad=0.): + self.angleA = angleA + self.angleB = angleB + self.armA = armA + self.armB = armB + + self.rad = rad + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + + vertices = [(x1, y1)] + rounded = [] + codes = [Path.MOVETO] + + if self.armA: + cosA = math.cos(self.angleA/180.*math.pi) + sinA = math.sin(self.angleA/180.*math.pi) + #x_armA, y_armB + d = self.armA - self.rad + rounded.append((x1 + d*cosA, y1 + d*sinA)) + d = self.armA + rounded.append((x1 + d*cosA, y1 + d*sinA)) + + if self.armB: + cosB = math.cos(self.angleB/180.*math.pi) + sinB = math.sin(self.angleB/180.*math.pi) + x_armB, y_armB = x2 + self.armB*cosB, y2 + self.armB*sinB + + if rounded: + xp, yp = rounded[-1] + dx, dy = x_armB - xp, y_armB - yp + dd = (dx*dx + dy*dy)**.5 + + rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) + vertices.extend(rounded) + codes.extend([Path.LINETO, + Path.CURVE3, + Path.CURVE3]) + else: + xp, yp = vertices[-1] + dx, dy = x_armB - xp, y_armB - yp + dd = (dx*dx + dy*dy)**.5 + + d = dd - self.rad + rounded = [(xp + d*dx/dd, yp + d*dy/dd), + (x_armB, y_armB)] + + if rounded: + xp, yp = rounded[-1] + dx, dy = x2 - xp, y2 - yp + dd = (dx*dx + dy*dy)**.5 + + rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) + vertices.extend(rounded) + codes.extend([Path.LINETO, + Path.CURVE3, + Path.CURVE3]) + + vertices.append((x2, y2)) + codes.append(Path.LINETO) + + return Path(vertices, codes) + + + + +class ArrowTransmuterBase(object): + """ + Arrow Transmuter Base class + + ArrowTransmuterBase and its derivatives are used to make a fancy + arrow around a given path. The __call__ method returns a path + (which will be used to create a PathPatch instance) and a boolean + value indicating the path is open therefore is not fillable. This + class is not an artist and actual drawing of the fancy arrow is + done by the FancyArrowPatch class. + + """ + + # The derived classes are required to be able to be initialized + # w/o arguments, i.e., all its argument (except self) must have + # the default values. + + def __init__(self): + super(ArrowTransmuterBase, self).__init__() + + @staticmethod + def ensure_quadratic_bezier(path): + """ Some ArrowTransmuter class only wokrs with a simple + quaratic bezier curve (created with Arc3Connetion or + Angle3Connector). This static method is to check if the + provided path is a simple quadratic bezier curve and returns + its control points if true. + """ + segments = list(path.iter_segments()) + assert len(segments) == 2 + + assert segments[0][1] == Path.MOVETO + assert segments[1][1] == Path.CURVE3 + + return list(segments[0][0]) + list(segments[1][0]) + + + def transmute(self, path, mutation_size, linewidth): + """ + The transmute method is a very core of the ArrowTransmuter + class and must be overriden in the subclasses. It receives the + path object along which the arrow will be drawn, and the + mutation_size, with which the amount arrow head and etc. will + be scaled. It returns a Path instance. The linewidth may be + used to adjust the the path so that it does not pass beyond + the given points. + """ + + raise NotImplementedError('Derived must override') + + + + def __call__(self, path, mutation_size, linewidth, + aspect_ratio=1.): + """ + The __call__ method is a thin wrapper around the transmute method + and take care of the aspect ratio. + """ + + if aspect_ratio is not None: + # Squeeze the given height by the aspect_ratio + + vertices, codes = path.vertices[:], path.codes[:] + # Squeeze the height + vertices[:,1] = vertices[:,1] / aspect_ratio + path_shrinked = Path(vertices, codes) + # call transmute method with squeezed height. + path_mutated, closed = self.transmute(path_shrinked, linewidth, + mutation_size) + vertices, codes = path_mutate.vertices, path_mutate.codes + # Restore the height + vertices[:,1] = vertices[:,1] * aspect_ratio + return Path(vertices, codes), closed + else: + return self.transmute(path, mutation_size, linewidth) + + + +class CurveArrowTransmuter(ArrowTransmuterBase): + """ + A simple arrow which will work with any path instance. The + returned path is simply concatenation of the original path + at + most two paths representing the arrow at the begin point and the + at the end point. The returned path is not closed and only meant + to be stroked. + """ + + def __init__(self, beginarrow=None, endarrow=None, + head_length=.2, head_width=.1): + """ The arrows are drawn if *beginarrow* and/or *endarrow* are + true. *head_length* and *head_width* determines the size of + the arrow relative to the *mutation scale*. + """ + self.beginarrow, self.endarrow = beginarrow, endarrow + self.head_length, self.head_width = \ + head_length, head_width + super(CurveArrowTransmuter, self).__init__() + + + def _get_pad_projected(self, x0, y0, x1, y1, linewidth): + # when no arrow head is drawn + + dx, dy = x0 - x1, y0 - y1 + cp_distance = math.sqrt(dx**2 + dy**2) + + # padx_projected, pady_projected : amount of pad to account + # projection of the wedge + padx_projected = (.5*linewidth) + pady_projected = (.5*linewidth) + + # apply pad for projected edge + ddx = padx_projected * dx / cp_distance + ddy = pady_projected * dy / cp_distance + + return ddx, ddy + + def _get_arrow_wedge(self, x0, y0, x1, y1, + head_dist, cos_t, sin_t, linewidth + ): + """ Return the paths for arrow heads. Since arrow lines are + drawn with capstyle=projected, The arrow is goes beyond the + desired point. This method also returns the amount of the path + to be shrinked so that it does not overshoot. + """ + + # arrow from x0, y0 to x1, y1 + + + dx, dy = x0 - x1, y0 - y1 + cp_distance = math.sqrt(dx**2 + dy**2) + + # padx_projected, pady_projected : amount of pad for account + # the overshooting of the projection of the wedge + padx_projected = (.5*linewidth / cos_t) + pady_projected = (.5*linewidth / sin_t) + + # apply pad for projected edge + ddx = padx_projected * dx / cp_distance + ddy = pady_projected * dy / cp_distance + + # offset for arrow wedge + dx, dy = dx / cp_distance * head_dist, dy / cp_distance * head_dist + + dx1, dy1 = cos_t * dx + sin_t * dy, -sin_t * dx + cos_t * dy + dx2, dy2 = cos_t * dx - sin_t * dy, sin_t * dx + cos_t * dy + + vertices_arrow = [(x1+ddx+dx1, y1+ddy+dy1), + (x1+ddx, y1++ddy), + (x1+ddx+dx2, y1+ddy+dy2)] + codes_arrow = [Path.MOVETO, + Path.LINETO, + Path.LINETO] + + return vertices_arrow, codes_arrow, ddx, ddy + + + def transmute(self, path, mutation_size, linewidth): + + head_length, head_width = self.head_length * mutation_size, \ + self.head_width * mutation_size + head_dist = math.sqrt(head_length**2 + head_width**2) + cos_t, sin_t = head_length / head_dist, head_width / head_dist + + + # begin arrow + x0, y0 = path.vertices[0] + x1, y1 = path.vertices[1] + + if self.beginarrow: + verticesA, codesA, ddxA, ddyA = \ + self._get_arrow_wedge(x1, y1, x0, y0, + head_dist, cos_t, sin_t, + linewidth) + else: + verticesA, codesA = [], [] + #ddxA, ddyA = self._get_pad_projected(x1, y1, x0, y0, linewidth) + ddxA, ddyA = 0., 0., #self._get_pad_projected(x1, y1, x0, y0, linewidth) + + # end arrow + x2, y2 = path.vertices[-2] + x3, y3 = path.vertices[-1] + + if self.endarrow: + verticesB, codesB, ddxB, ddyB = \ + self._get_arrow_wedge(x2, y2, x3, y3, + head_dist, cos_t, sin_t, + linewidth) + else: + verticesB, codesB = [], [] + ddxB, ddyB = 0., 0. #self._get_pad_projected(x2, y2, x3, y3, linewidth) + + + # this simple code will not work if ddx, ddy is greater than + # separation bettern vertices. + vertices = np.concatenate([verticesA + [(x0+ddxA, y0+ddyA)], + path.vertices[1:-1], + [(x3+ddxB, y3+ddyB)] + verticesB]) + codes = np.concatenate([codesA, + path.codes, + codesB]) + + p = Path(vertices, codes) + + return p, False + + +class CurveArrowATransmuter(CurveArrowTransmuter): + """ + A CurveArrowTransmuter with arrow at begin point. This class is + only meant to be used to define the arrowstyle and users may + simply use the original CurveArrowTransmuter class when necesary. + """ + + def __init__(self, head_length=.4, head_width=.2): + super(CurveArrowATransmuter, self).__init__( \ + beginarrow=True, endarrow=False, + head_length=head_length, head_width=head_width ) + + +class CurveArrowBTransmuter(CurveArrowTransmuter): + """ + A CurveArrowTransmuter with arrow at end point. This class is + only meant to be used to define the arrowstyle and users may + simply use the original CurveArrowTransmuter class when necesary. + """ + + def __init__(self, head_length=.4, head_width=.2): + super(CurveArrowBTransmuter, self).__init__( \ + beginarrow=False, endarrow=True, + head_length=head_length, head_width=head_width ) + + +class CurveArrowABTransmuter(CurveArrowTransmuter): + """ + A CurveArrowTransmuter with arrows at both begin and end + points. This class is only meant to be used to define the + arrowstyle and users may simply use the original + CurveArrowTransmuter class when necesary. + """ + + def __init__(self, head_length=.4, head_width=.2): + super(CurveArrowABTransmuter, self).__init__( \ + beginarrow=True, endarrow=True, + head_length=head_length, head_width=head_width ) + + + +class SimpleArrowTransmuter(ArrowTransmuterBase): + """ + A simple arrow. Only works with a quadratic bezier curve. + """ + + def __init__(self, head_length=.5, head_width=.5, tail_width=.2): + self.head_length, self.head_width, self.tail_width = \ + head_length, head_width, tail_width + super(SimpleArrowTransmuter, self).__init__() + + def transmute(self, path, mutation_size, linewidth): + + x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) + + # divide the path into a head and a tail + head_length = self.head_length * mutation_size + in_f = inside_circle(x2, y2, head_length) + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] + arrow_out, arrow_in = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + + # head + head_width = self.head_width * mutation_size + head_l, head_r = make_wedged_bezier2(arrow_in, head_width/2., + wm=.8) + + + + # tail + tail_width = self.tail_width * mutation_size + tail_left, tail_right = get_parallels(arrow_out, tail_width/2.) + + head_right, head_left = head_r, head_l + patch_path = [(Path.MOVETO, tail_right[0]), + (Path.CURVE3, tail_right[1]), + (Path.CURVE3, tail_right[2]), + (Path.LINETO, head_right[0]), + (Path.CURVE3, head_right[1]), + (Path.CURVE3, head_right[2]), + (Path.CURVE3, head_left[1]), + (Path.CURVE3, head_left[0]), + (Path.LINETO, tail_left[2]), + (Path.CURVE3, tail_left[1]), + (Path.CURVE3, tail_left[0]), + (Path.LINETO, tail_right[0]), + (Path.CLOSEPOLY, tail_right[0]), + ] + path = Path([p for c, p in patch_path], [c for c, p in patch_path]) + + return path, True + + +class FancyArrowTransmuter(ArrowTransmuterBase): + """ + A fancy arrow. Only works with a quadratic bezier curve. + """ + + def __init__(self, head_length=.4, head_width=.4, tail_width=.4): + self.head_length, self.head_width, self.tail_width = \ + head_length, head_width, tail_width + super(FancyArrowTransmuter, self).__init__() + + def transmute(self, path, mutation_size, linewidth): + + x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) + + # divide the path into a head and a tail + head_length = self.head_length * mutation_size + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] + + # path for head + in_f = inside_circle(x2, y2, head_length) + path_out, path_in = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + path_head = path_in + + # path for head + in_f = inside_circle(x2, y2, head_length*.8) + path_out, path_in = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + path_tail = path_out + + + # head + head_width = self.head_width * mutation_size + head_l, head_r = make_wedged_bezier2(path_head, head_width/2., + wm=.6) + + # tail + tail_width = self.tail_width * mutation_size + tail_left, tail_right = make_wedged_bezier2(path_tail, + tail_width*.5, + w1=1., wm=0.6, w2=0.3) + + # path for head + in_f = inside_circle(x0, y0, tail_width*.3) + path_in, path_out = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + tail_start = path_in[-1] + + head_right, head_left = head_r, head_l + patch_path = [(Path.MOVETO, tail_start), + (Path.LINETO, tail_right[0]), + (Path.CURVE3, tail_right[1]), + (Path.CURVE3, tail_right[2]), + (Path.LINETO, head_right[0]), + (Path.CURVE3, head_right[1]), + (Path.CURVE3, head_right[2]), + (Path.CURVE3, head_left[1]), + (Path.CURVE3, head_left[0]), + (Path.LINETO, tail_left[2]), + (Path.CURVE3, tail_left[1]), + (Path.CURVE3, tail_left[0]), + (Path.LINETO, tail_start), + (Path.CLOSEPOLY, tail_start), + ] + patch_path2 = [(Path.MOVETO, tail_right[0]), + (Path.CURVE3, tail_right[1]), + (Path.CURVE3, tail_right[2]), + (Path.LINETO, head_right[0]), + (Path.CURVE3, head_right[1]), + (Path.CURVE3, head_right[2]), + (Path.CURVE3, head_left[1]), + (Path.CURVE3, head_left[0]), + (Path.LINETO, tail_left[2]), + (Path.CURVE3, tail_left[1]), + (Path.CURVE3, tail_left[0]), + (Path.CURVE3, tail_start), + (Path.CURVE3, tail_right[0]), + (Path.CLOSEPOLY, tail_right[0]), + ] + path = Path([p for c, p in patch_path], [c for c, p in patch_path]) + + return path, True + + + + + +class WedgeArrowTransmuter(ArrowTransmuterBase): + """ + Wedge(?) shape. Only wokrs with a quadratic bezier curve. The + begin point has a width of the tail_width and the end point has a + width of 0. At the middle, the width is shrink_factor*tail_width. + """ + + def __init__(self, tail_width=.3, shrink_factor=0.5): + self.tail_width = tail_width + self.shrink_factor = shrink_factor + super(WedgeArrowTransmuter, self).__init__() + + + def transmute(self, path, mutation_size, linewidth): + + x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) + + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] + b_plus, b_minus = make_wedged_bezier2(arrow_path, + self.tail_width * mutation_size / 2., + wm=self.shrink_factor) + + + patch_path = [(Path.MOVETO, b_plus[0]), + (Path.CURVE3, b_plus[1]), + (Path.CURVE3, b_plus[2]), + (Path.LINETO, b_minus[2]), + (Path.CURVE3, b_minus[1]), + (Path.CURVE3, b_minus[0]), + (Path.CLOSEPOLY, b_minus[0]), + ] + path = Path([p for c, p in patch_path], [c for c, p in patch_path]) + + return path, True + + + + +def _list_available_connectionstyles(connectors): + """ a helper function of the FancyArrowPatch to list the available + connection styles. It inspects the arguments of the __init__ methods of + each classes and report them + """ + import inspect + s = [] + for name, cls in connectors.items(): + args, varargs, varkw, defaults = inspect.getargspec(cls.__init__) + args_string = ["%s=%s" % (argname, str(argdefault)) \ + for argname, argdefault in zip(args[1:], defaults)] + s.append(",".join([name]+args_string)) + s.sort() + return s + +def _list_available_arrowstyles(transmuters): + """ a helper function of the FancyArrowPatch to list the available + arrow styles. It inspects the arguments of the __init__ methods of + each classes and report them + """ + import inspect + s = [] + for name, cls in transmuters.items(): + args, varargs, varkw, defaults = inspect.getargspec(cls.__init__) + args_string = ["%s=%s" % (argname, str(argdefault)) \ + for argname, argdefault in zip(args[1:], defaults)] + s.append(",".join([name]+args_string)) + s.sort() + return s + + + +class FancyArrowPatch(Patch): + """ + Draw a fancy arrow along a path. + + The "arrowstyle" argument determins what kind of + arrow will be drawn. In other words, it selects the + ArrowTransmuter class to use, and sets optional attributes. A + custom ArrowTransmuter can be used with arrow_transmuter argument + (should be an instance, not a class). mutation_scale determines + the overall size of the mutation (by which I mean the + transformation of the path to the fancy arrow) and the + mutation_aspect determines the aspect-ratio of the mutation. + + """ + + _fancy_arrow_transmuters = {"simple":SimpleArrowTransmuter, + "fancy":FancyArrowTransmuter, + "wedge":WedgeArrowTransmuter, + "-":CurveArrowTransmuter, + "->":CurveArrowBTransmuter, + "<-":CurveArrowATransmuter, + "<->":CurveArrowABTransmuter, + } + + _connectors = {"arc3":Arc3Connector, + "arc":ArcConnector, + "angle":AngleConnector, + "angle3":Angle3Connector, + } + + def __str__(self): + return self.__class__.__name__ \ + + "FancyArrowPatch(%g,%g,%g,%g,%g,%g)" % tuple(self._q_bezier) + + def __init__(self, posA=None, posB=None, + path=None, + arrowstyle="simple", + arrow_transmuter=None, + connectionstyle="arc3", + connector=None, + patchA=None, + patchB=None, + shrinkA=2., + shrinkB=2., + mutation_scale=1., + mutation_aspect=None, + **kwargs): + """ + If *posA* and *posB* is given, a path connecting two point are + created according to the connectionstyle. The path will be + clipped with *patchA* and *patchB* and further shirnked by + *shrinkA* and *shrinkB*. An arrow is drawn along this + resulting path using the *arrowstyle* parameter. If *path* + provided, an arrow is drawn along this path and *patchA*, + *patchB*, *shrinkA*, and *shrinkB* are ignored. + + The *connectionstyle* describes how *posA* and *posB* are + connected. It should be one of the available connectionstyle + names, with optional comma-separated attributes. Following + connection styles are available. + + %(AvailableConnectorstyles)s + + The connectionstyle name can be "custom", in which case the + *connector* needs to be set, which should be an instance + of ArrowTransmuterBase (or its derived). + + + The *arrowstyle* describes how the fancy arrow will be drawn. It + should be one of the available arrowstyle names, with optional + comma-separated attributes. These attributes are meant to be + scaled with the *mutation_scale*. Following arrow styles are + available. + + %(AvailableArrowstyles)s + + The arrowstyle name can be "custom", in which case the + arrow_transmuter needs to be set, which should be an instance + of ArrowTransmuterBase (or its derived). + + *mutation_scale* : a value with which attributes of arrowstyle + (e.g., head_length) will be scaled. default=1. + + *mutation_aspect* : The height of the rectangle will be + squeezed by this value before the mutation and the mutated + box will be stretched by the inverse of it. default=None. + + Valid kwargs are: + %(Patch)s + """ + + if posA is not None and posB is not None and path is None: + self._posA_posB = [posA, posB] + + if connectionstyle == "custom": + if connector is None: + raise ValueError("connector argument is needed with custom connectionstyle") + self.set_connector(connector) + else: + if connectionstyle is None: + connectionstyle = "arc3" + self.set_connectionstyle(connectionstyle) + + elif posA is None and posB is None and path is not None: + self._posA_posB = None + self._connetors = None + else: + raise ValueError("either posA and po... [truncated message content] |
From: <ry...@us...> - 2008-10-29 20:28:59
|
Revision: 6352 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6352&view=rev Author: ryanmay Date: 2008-10-29 20:28:57 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Fix 'extra credit' DraggableRectangle to no longer use Rectangle.xy. (Thanks to Neil Crighton for finding the original problem.) Modified Paths: -------------- trunk/matplotlib/doc/users/event_handling.rst Modified: trunk/matplotlib/doc/users/event_handling.rst =================================================================== --- trunk/matplotlib/doc/users/event_handling.rst 2008-10-29 19:45:18 UTC (rev 6351) +++ trunk/matplotlib/doc/users/event_handling.rst 2008-10-29 20:28:57 UTC (rev 6352) @@ -257,8 +257,8 @@ if DraggableRectangle.lock is not None: return contains, attrd = self.rect.contains(event) if not contains: return - print 'event contains', self.rect.xy - x0, y0 = self.rect.xy + x0, y0 = self.rect.get_x(), self.rect.get_y() + print 'event contains', x0, y0 self.press = x0, y0, event.xdata, event.ydata DraggableRectangle.lock = self This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 19:45:22
|
Revision: 6351 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6351&view=rev Author: mdboom Date: 2008-10-29 19:45:18 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Fix compile for those that need the wxAgg C-extension. Modified Paths: -------------- trunk/matplotlib/src/_wxagg.cpp Modified: trunk/matplotlib/src/_wxagg.cpp =================================================================== --- trunk/matplotlib/src/_wxagg.cpp 2008-10-29 18:27:05 UTC (rev 6350) +++ trunk/matplotlib/src/_wxagg.cpp 2008-10-29 19:45:18 UTC (rev 6351) @@ -178,7 +178,7 @@ rbuf.attach(srcBuffer, srcWidth, srcHeight, srcStride); pixfmt pf(rbuf); renderer_base rndr(pf); - rndr.copy_from(*aggRenderer->renderingBuffer, ®ion, + rndr.copy_from(aggRenderer->renderingBuffer, ®ion, (int)-l, (int)(t-h)); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 18:27:07
|
Revision: 6350 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6350&view=rev Author: mdboom Date: 2008-10-29 18:27:05 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Speed up quadmesh drawing. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/lib/matplotlib/path.py Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-10-29 18:26:34 UTC (rev 6349) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-10-29 18:27:05 UTC (rev 6350) @@ -551,12 +551,6 @@ else: c = coordinates - # We could let the Path constructor generate the codes for us, - # but this is faster, since we know they'll always be the same - codes = np.array( - [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY], - Path.code_type) - points = np.concatenate(( c[0:-1, 0:-1], c[0:-1, 1: ], @@ -565,7 +559,7 @@ c[0:-1, 0:-1] ), axis=2) points = points.reshape((meshWidth * meshHeight, 5, 2)) - return [Path(x, codes) for x in points] + return [Path(x) for x in points] convert_mesh_to_paths = staticmethod(convert_mesh_to_paths) def get_datalim(self, transData): Modified: trunk/matplotlib/lib/matplotlib/path.py =================================================================== --- trunk/matplotlib/lib/matplotlib/path.py 2008-10-29 18:26:34 UTC (rev 6349) +++ trunk/matplotlib/lib/matplotlib/path.py 2008-10-29 18:27:05 UTC (rev 6350) @@ -111,6 +111,7 @@ self.should_simplify = (len(vertices) >= 128 and (codes is None or np.all(codes <= Path.LINETO))) + self.has_nonfinite = not np.isfinite(vertices).all() self.codes = codes self.vertices = vertices @@ -183,13 +184,18 @@ for v in vertices[1:]: yield v, LINETO elif codes is None: - next_code = MOVETO - for v in vertices: - if (~isfinite(v)).any(): - next_code = MOVETO - else: - yield v, next_code - next_code = LINETO + if self.has_nonfinite: + next_code = MOVETO + for v in vertices: + if np.isfinite(v).all(): + yield v, next_code + next_code = LINETO + else: + next_code = MOVETO + else: + yield vertices[0], MOVETO + for v in vertices[1:]: + yield v, LINETO else: i = 0 was_nan = False @@ -203,7 +209,7 @@ else: num_vertices = NUM_VERTICES[int(code)] curr_vertices = vertices[i:i+num_vertices].flatten() - if (~isfinite(curr_vertices)).any(): + if not isfinite(curr_vertices).all(): was_nan = True elif was_nan: yield curr_vertices[-2:], MOVETO This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 18:26:43
|
Revision: 6349 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6349&view=rev Author: mdboom Date: 2008-10-29 18:26:34 +0000 (Wed, 29 Oct 2008) Log Message: ----------- PDF speed optimization. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-10-29 17:16:02 UTC (rev 6348) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-10-29 18:26:34 UTC (rev 6349) @@ -112,7 +112,7 @@ result.append(' '.join(strings[lasti:])) return '\n'.join(result) - +_string_escape_regex = re.compile(r'([\\()])') def pdfRepr(obj): """Map Python objects to PDF syntax.""" @@ -138,7 +138,7 @@ # simpler to escape them all. TODO: cut long strings into lines; # I believe there is some maximum line length in PDF. elif is_string_like(obj): - return '(' + re.sub(r'([\\()])', r'\\\1', obj) + ')' + return '(' + _string_escape_regex.sub(r'\\\1', obj) + ')' # Dictionaries. The keys must be PDF names, so if we find strings # there, we make Name objects from them. The values may be @@ -207,12 +207,13 @@ class Name: """PDF name object.""" + _regex = re.compile(r'[^!-~]') def __init__(self, name): if isinstance(name, Name): self.name = name.name else: - self.name = re.sub(r'[^!-~]', Name.hexify, name) + self.name = self._regex.sub(Name.hexify, name) def __repr__(self): return "<Name %s>" % self.name This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 17:16:09
|
Revision: 6348 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6348&view=rev Author: mdboom Date: 2008-10-29 17:16:02 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Speed up do_nothing_show test for interactive use, which was dominating startup time. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/__init__.py Modified: trunk/matplotlib/lib/matplotlib/backends/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/__init__.py 2008-10-29 17:15:32 UTC (rev 6347) +++ trunk/matplotlib/lib/matplotlib/backends/__init__.py 2008-10-29 17:16:02 UTC (rev 6348) @@ -32,7 +32,7 @@ # by the backend, just do nothing def do_nothing_show(*args, **kwargs): frame = inspect.currentframe() - fname = inspect.getframeinfo(frame.f_back)[0] + fname = frame.f_back.f_code.co_filename if fname in ('<stdin>', '<ipython console>'): warnings.warn(""" Your currently selected backend, '%s' does not support show(). This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 17:15:42
|
Revision: 6347 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6347&view=rev Author: mdboom Date: 2008-10-29 17:15:32 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Fix caching of findfont results. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/font_manager.py Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-29 16:43:05 UTC (rev 6346) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-29 17:15:32 UTC (rev 6347) @@ -705,7 +705,9 @@ return parse_fontconfig_pattern(pattern) def __hash__(self): - return hash(repr(self.__dict__)) + l = self.__dict__.items() + l.sort() + return hash(repr(l)) def __str__(self): return self.get_fontconfig_pattern() @@ -1192,7 +1194,7 @@ font_cache = self.ttf_lookup_cache fontlist = self.ttflist - cached = font_cache.get(prop) + cached = font_cache.get(hash(prop)) if cached: return cached @@ -1223,7 +1225,7 @@ (prop, best_font.name, best_font.fname, best_score)) result = best_font.fname - font_cache[prop] = result + font_cache[hash(prop)] = result return result This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 16:43:08
|
Revision: 6346 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6346&view=rev Author: mdboom Date: 2008-10-29 16:43:05 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Minor fixes to text docstrings, and to make getters and setters completely symmetric. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/patches.py trunk/matplotlib/lib/matplotlib/text.py Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-10-29 13:51:47 UTC (rev 6345) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-10-29 16:43:05 UTC (rev 6346) @@ -1383,7 +1383,7 @@ """ Simple square box. - 'pad' :an amount of padding. + *pad*: an amount of padding. """ def __init__(self, pad=0.3): Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2008-10-29 13:51:47 UTC (rev 6345) +++ trunk/matplotlib/lib/matplotlib/text.py 2008-10-29 16:43:05 UTC (rev 6346) @@ -176,7 +176,6 @@ self._linespacing = linespacing self.update(kwargs) #self.set_bbox(dict(pad=0)) - __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def contains(self,mouseevent): """Test whether the mouse event occurred in the patch. @@ -441,10 +440,14 @@ "Return the color of the text" return self._color - def get_font_properties(self): + def get_fontproperties(self): "Return the :class:`~font_manager.FontProperties` object" return self._fontproperties + def get_font_properties(self): + 'alias for get_fontproperties' + return self.get_fontproperties + def get_family(self): "Return the list of font families used for font lookup" return self._fontproperties.get_family() @@ -687,6 +690,10 @@ """ self._fontproperties.set_variant(variant) + def set_fontvariant(self, variant): + 'alias for set_variant' + return self.set_variant(variant) + def set_name(self, fontname): """alias for set_family""" return self.set_family(fontname) @@ -832,7 +839,12 @@ fp = FontProperties(fp) self._fontproperties = fp.copy() + def set_font_properties(self, fp): + 'alias for set_fontproperties' + self.set_fontproperties(fp) + artist.kwdocd['Text'] = artist.kwdoc(Text) +Text.__init__.im_func.__doc__ = cbook.dedent(Text.__init__.__doc__) % artist.kwdocd class TextWithDash(Text): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-29 13:51:56
|
Revision: 6345 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6345&view=rev Author: mdboom Date: 2008-10-29 13:51:47 +0000 (Wed, 29 Oct 2008) Log Message: ----------- [204515] References to __doc__ break with -OO and in py2exe Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/figure.py trunk/matplotlib/lib/matplotlib/patches.py trunk/matplotlib/lib/matplotlib/scale.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-10-29 03:36:27 UTC (rev 6344) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-10-29 13:51:47 UTC (rev 6345) @@ -5392,7 +5392,7 @@ def barbs(self, *args, **kw): """ - %s + %(barbs_doc)s **Example:** .. plot:: mpl_examples/pylab_examples/barb_demo.py @@ -5403,7 +5403,8 @@ self.update_datalim(b.get_offsets()) self.autoscale_view() return b - barbs.__doc__ = cbook.dedent(barbs.__doc__) % mquiver.Barbs.barbs_doc + barbs.__doc__ = cbook.dedent(barbs.__doc__) % { + 'barbs_doc': mquiver.Barbs.barbs_doc} def fill(self, *args, **kwargs): """ Modified: trunk/matplotlib/lib/matplotlib/figure.py =================================================================== --- trunk/matplotlib/lib/matplotlib/figure.py 2008-10-29 03:36:27 UTC (rev 6344) +++ trunk/matplotlib/lib/matplotlib/figure.py 2008-10-29 13:51:47 UTC (rev 6345) @@ -563,7 +563,7 @@ sets the projection type of the axes. (For backward compatibility, ``polar=True`` may also be provided, which is equivalent to ``projection='polar'``). Valid values for - *projection* are: %s. Some of these projections support + *projection* are: %(list)s. Some of these projections support additional kwargs, which may be provided to :meth:`add_axes`:: rect = l,b,w,h @@ -589,7 +589,7 @@ The following kwargs are supported: - %s + %(Axes)s """ key = self._make_key(*args, **kwargs) @@ -623,8 +623,9 @@ self._seen[key] = a return a - add_axes.__doc__ = add_axes.__doc__ % (", ".join(get_projection_names()), '%(Axes)s') - add_axes.__doc__ = dedent(add_axes.__doc__) % artist.kwdocd + add_axes.__doc__ = dedent(add_axes.__doc__) % \ + {'list': (", ".join(get_projection_names())), + 'Axes': artist.kwdocd['Axes']} def add_subplot(self, *args, **kwargs): """ @@ -640,7 +641,7 @@ *projection*, which chooses a projection type for the axes. (For backward compatibility, *polar=True* may also be provided, which is equivalent to *projection='polar'*). Valid - values for *projection* are: %s. Some of these projections + values for *projection* are: %(list)s. Some of these projections support additional *kwargs*, which may be provided to :meth:`add_axes`. @@ -651,7 +652,8 @@ return it. The following kwargs are supported: - %s + + %(Axes)s """ key = self._make_key(*args, **kwargs) @@ -685,8 +687,9 @@ self.sca(a) self._seen[key] = a return a - add_subplot.__doc__ = dedent(add_subplot.__doc__) % ( - ", ".join(get_projection_names()), "%(Axes)s") % artist.kwdocd + add_subplot.__doc__ = dedent(add_subplot.__doc__) % { + 'list': ", ".join(get_projection_names()), + 'Axes': artist.kwdocd['Axes']} def clf(self): """ Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-10-29 03:36:27 UTC (rev 6344) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-10-29 13:51:47 UTC (rev 6345) @@ -1634,7 +1634,7 @@ kwdoc["AvailableBoxstyles"]=" | ".join([l \ for l in _list_available_boxstyles(_fancy_bbox_transmuters)]) kwdoc.update(artist.kwdocd) - set_boxstyle.__doc__ = set_boxstyle.__doc__ % kwdoc + set_boxstyle.__doc__ = cbook.dedent(set_boxstyle.__doc__) % kwdoc del kwdoc def set_mutation_scale(self, scale): Modified: trunk/matplotlib/lib/matplotlib/scale.py =================================================================== --- trunk/matplotlib/lib/matplotlib/scale.py 2008-10-29 03:36:27 UTC (rev 6344) +++ trunk/matplotlib/lib/matplotlib/scale.py 2008-10-29 13:51:47 UTC (rev 6345) @@ -401,7 +401,7 @@ """ Return a scale class by name. - ACCEPTS: [ %s ] + ACCEPTS: [ %(names)s ] """ scale = scale.lower() if scale is None: @@ -411,7 +411,8 @@ raise ValueError("Unknown scale type '%s'" % scale) return _scale_mapping[scale](axis, **kwargs) -scale_factory.__doc__ = scale_factory.__doc__ % " | ".join(get_scale_names()) +scale_factory.__doc__ = dedent(scale_factory.__doc__) % \ + {'names': " | ".join(get_scale_names())} def register_scale(scale_class): """ @@ -431,7 +432,8 @@ docs.append(" '%s'" % name) docs.append("") class_docs = dedent(scale_class.__init__.__doc__) - class_docs = "".join([" %s\n" % x for x in class_docs.split("\n")]) + class_docs = "".join([" %s\n" % + x for x in class_docs.split("\n")]) docs.append(class_docs) docs.append("") return "\n".join(docs) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-10-29 03:36:33
|
Revision: 6344 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6344&view=rev Author: ryanmay Date: 2008-10-29 03:36:27 +0000 (Wed, 29 Oct 2008) Log Message: ----------- Fix DraggableRectangle event handling example to no longer use the (now removed) Rectangle.xy property. Modified Paths: -------------- trunk/matplotlib/doc/users/event_handling.rst Modified: trunk/matplotlib/doc/users/event_handling.rst =================================================================== --- trunk/matplotlib/doc/users/event_handling.rst 2008-10-28 19:36:07 UTC (rev 6343) +++ trunk/matplotlib/doc/users/event_handling.rst 2008-10-29 03:36:27 UTC (rev 6344) @@ -182,8 +182,8 @@ contains, attrd = self.rect.contains(event) if not contains: return - print 'event contains', self.rect.xy - x0, y0 = self.rect.xy + x0, y0 = self.rect.get_x(), self.rect.get_y() + print 'event contains', x0, y0 self.press = x0, y0, event.xdata, event.ydata def on_motion(self, event): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-28 19:36:20
|
Revision: 6343 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6343&view=rev Author: mdboom Date: 2008-10-28 19:36:07 +0000 (Tue, 28 Oct 2008) Log Message: ----------- Add short circuit for font lookup Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/font_manager.py Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 18:10:51 UTC (rev 6342) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 19:36:07 UTC (rev 6343) @@ -1211,6 +1211,8 @@ if score < best_score: best_score = score best_font = font + if score == 0: + break if best_font is None or best_score > 10.0: verbose.report('findfont: Could not match %s. Returning %s' % This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-28 18:11:02
|
Revision: 6342 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6342&view=rev Author: mdboom Date: 2008-10-28 18:10:51 +0000 (Tue, 28 Oct 2008) Log Message: ----------- Add nearest neighbor search for fonts. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/fonts_demo.py trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py trunk/matplotlib/lib/matplotlib/font_manager.py trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py trunk/matplotlib/lib/matplotlib/text.py Modified: trunk/matplotlib/examples/pylab_examples/fonts_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fonts_demo.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/examples/pylab_examples/fonts_demo.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -11,7 +11,7 @@ subplot(111, axisbg='w') font0 = FontProperties() -alignment = {'horizontalalignment':'center', 'verticalalignment':'center'} +alignment = {'horizontalalignment':'center', 'verticalalignment':'baseline'} ### Show family options family = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] @@ -53,7 +53,7 @@ t = text(0.0, 0.9, 'variant', fontproperties=font1, **alignment) -for k in range(1): +for k in range(2): font = font0.copy() font.set_family('serif') font.set_variant(variant[k]) Modified: trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -8,7 +8,7 @@ from pylab import * subplot(111, axisbg='w') -alignment = {'horizontalalignment':'center', 'verticalalignment':'center'} +alignment = {'horizontalalignment':'center', 'verticalalignment':'baseline'} ### Show family options family = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] @@ -40,7 +40,7 @@ t = text(0.0, 0.9, 'variant', **alignment) -for k in range(1): +for k in range(2): t = text( 0.0, yp[k], variant[k], family='serif', variant=variant[k], **alignment) Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -4,9 +4,9 @@ This module provides a single :class:`FontManager` instance that can be shared across backends and platforms. The :func:`findfont` function returns the best TrueType (TTF) font file in the local or -system font path that matches the specified FontProperties. The -FontManager also handles Adobe Font Metrics (AFM) font files for use -by the PostScript backend. +system font path that matches the specified :class:`FontProperties` +instance. The :class:`FontManager` also handles Adobe Font Metrics +(AFM) font files for use by the PostScript backend. The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) font specification <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_. @@ -64,15 +64,53 @@ verbose = matplotlib.verbose -font_scalings = {'xx-small': 0.579, 'x-small': 0.694, 'small': 0.833, - 'medium': 1.0, 'large': 1.200, 'x-large': 1.440, - 'xx-large': 1.728, 'larger': 1.2, 'smaller': 0.833} +font_scalings = { + 'xx-small' : 0.579, + 'x-small' : 0.694, + 'small' : 0.833, + 'medium' : 1.0, + 'large' : 1.200, + 'x-large' : 1.440, + 'xx-large' : 1.728, + 'larger' : 1.2, + 'smaller' : 0.833, + None : 1.0} -weight_dict = {'light': 200, 'normal': 400, 'regular': 400, 'book': 400, - 'medium': 500, 'roman': 500, 'semibold': 600, 'demibold': 600, - 'demi': 600, 'bold': 700, 'heavy': 800, 'extra bold': 800, - 'black': 900} +stretch_dict = { + 'ultra-condensed' : 100, + 'extra-condensed' : 200, + 'condensed' : 300, + 'semi-condensed' : 400, + 'normal' : 500, + 'semi-expanded' : 600, + 'expanded' : 700, + 'extra-expanded' : 800, + 'ultra-expanded' : 900} +weight_dict = { + 'ultralight' : 100, + 'light' : 200, + 'normal' : 400, + 'regular' : 400, + 'book' : 400, + 'medium' : 500, + 'roman' : 500, + 'semibold' : 600, + 'demibold' : 600, + 'demi' : 600, + 'bold' : 700, + 'heavy' : 800, + 'extra bold' : 800, + 'black' : 900} + +font_family_aliases = set([ + 'serif', + 'sans-serif', + 'cursive', + 'fantasy', + 'monospace', + 'sans']) + # OS Font paths MSFolders = \ r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' @@ -117,12 +155,14 @@ 'afm': ('afm',)}[fontext] def win32FontDirectory(): - """Return the user-specified font directory for Win32. This is + """ + Return the user-specified font directory for Win32. This is looked up from the registry key:: \\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Fonts - If the key is not found, $WINDIR/Fonts will be returned.""" + If the key is not found, $WINDIR/Fonts will be returned. + """ try: import _winreg except ImportError: @@ -331,19 +371,21 @@ return weight -class FontKey(object): +class FontEntry(object): """ A class for storing Font properties. It is used when populating the font lookup dictionary. """ def __init__(self, + fname ='', name ='', style ='normal', variant='normal', weight ='normal', stretch='normal', - size ='medium' + size ='medium', ): + self.fname = fname self.name = name self.style = style self.variant = variant @@ -355,12 +397,12 @@ self.size = size -def ttfFontProperty(font): +def ttfFontProperty(fontpath, font): """ A function for populating the :class:`FontKey` by extracting information from the TrueType font file. - *font* is an :class:`FT2Font` instance. + *font* is a :class:`FT2Font` instance. """ name = font.family_name @@ -447,10 +489,10 @@ # !!!! Incomplete size_adjust = None - return FontKey(name, style, variant, weight, stretch, size) + return FontEntry(fontpath, name, style, variant, weight, stretch, size) -def afmFontProperty(font): +def afmFontProperty(fontpath, font): """ A function for populating a :class:`FontKey` instance by extracting information from the AFM font file. @@ -507,37 +549,17 @@ # !!!! Incomplete size_adjust = None - return FontKey(name, style, variant, weight, stretch, size) + return FontEntry(fontpath, name, style, variant, weight, stretch, size) -def add_filename(fontdict, prop, fname): +def createFontList(fontfiles, fontext='ttf'): """ - A function to add a font file name to the font dictionary using - the :class:`FontKey` properties. If a font property has no - dictionary, then create it. + A function to create a font lookup list. The default is to create + a list of TrueType fonts. An AFM font list can optionally be + created. """ - try: - size = str(float(prop.size)) - except ValueError: - size = prop.size - d = fontdict. \ - setdefault(prop.name, {}).\ - setdefault(prop.style, {}).\ - setdefault(prop.variant, {}).\ - setdefault(prop.weight, {}).\ - setdefault(prop.stretch, {}) - d[size] = fname - - -def createFontDict(fontfiles, fontext='ttf'): - """ - A function to create a font lookup dictionary. The default is to - create a dictionary for TrueType fonts. An AFM font dictionary - can optionally be created. - """ - - fontdict = {} + fontlist = [] # Add fonts from list of known font files. seen = {} for fpath in fontfiles: @@ -559,7 +581,7 @@ except RuntimeError: verbose.report("Could not parse font file %s"%fpath) continue - prop = afmFontProperty(font) + prop = afmFontProperty(fpath, font) else: try: font = ft2font.FT2Font(str(fpath)) @@ -570,59 +592,12 @@ verbose.report("Cannot handle unicode filenames") #print >> sys.stderr, 'Bad file is', fpath continue - try: prop = ttfFontProperty(font) + try: prop = ttfFontProperty(fpath, font) except: continue - add_filename(fontdict, prop, fpath) - return fontdict + fontlist.append(prop) + return fontlist -def setWeights(font): - """ - A function to populate missing values in a font weight - dictionary. This proceedure is necessary since the font finding - algorithm always matches on the weight property. - """ - - # !!!! Not completely correct - temp = font.copy() - if len(temp) == 1: - wgt = temp.keys()[0] - for j in range(100, 1000, 100): - font[j] = temp[wgt] - - if 400 in temp: - for j in range(100, 1000, 100): - font[j] = temp[400] - if 500 in temp: - if 400 in temp: - for j in range(500, 1000, 100): - font[j] = temp[500] - else: - for j in range(100, 1000, 100): - font[j] = temp[500] - - if 300 in temp: - for j in [100, 200, 300]: - font[j] = temp[300] - if 200 in temp: - if 300 in temp: - for j in [100, 200]: - font[j] = temp[200] - else: - for j in [100, 200, 300]: - font[j] = temp[200] - - if 800 in temp: - for j in [600, 700, 800, 900]: - font[j] = temp[800] - if 700 in temp: - if 800 in temp: - for j in [600, 700]: - font[j] = temp[700] - else: - for j in [600, 700, 800, 900]: - font[j] = temp[700] - class FontProperties(object): """ A class for storing and manipulating font properties. @@ -633,31 +608,33 @@ specification. The six properties are: - family: A list of font names in decreasing order of priority. - The last item is the default font name and is given the name - of the font family, either 'serif', 'sans-serif', 'cursive', - 'fantasy', or 'monospace'. + The items may include a generic font family name, either + 'serif', 'sans-serif', 'cursive', 'fantasy', or 'monospace'. + In that case, the actual font to be used will be looked up + from the associated rcParam in :file:`matplotlibrc`. - style: Either 'normal', 'italic' or 'oblique'. - variant: Either 'normal' or 'small-caps'. - - stretch: Either an absolute value of 'ultra-condensed', - 'extra- condensed', 'condensed', 'semi-condensed', 'normal', - 'semi-expanded', 'expanded', 'extra-expanded' or - 'ultra-expanded'; or a relative value of narrower or wider. - This property is currently not implemented and is set to - normal. + - stretch: A numeric value in the range 0-1000 or one of + 'ultra-condensed', 'extra-condensed', 'condensed', + 'semi-condensed', 'normal', 'semi-expanded', 'expanded', + 'extra-expanded' or 'ultra-expanded' - - weight: A numeric value in the range 100, 200, 300, ..., 900. + - weight: A numeric value in the range 0-1000 or one of + 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', + 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', + 'extra bold', 'black' - - size: Either an absolute value of 'xx-small', 'x-small', 'small', - 'medium', 'large', 'x-large', 'xx-large'; or a relative value - of smaller or larger; or an absolute font size, e.g. 12; - or 'scalable'. + - size: Either an relative value of 'xx-small', 'x-small', + 'small', 'medium', 'large', 'x-large', 'xx-large' or an + absolute font size, e.g. 12 - The default font property for TrueType fonts is:: + The default font property for TrueType fonts (as specified in the + default :file:`matplotlibrc` file) is:: - sans-serif, normal, normal, normal, 400, scalable. + sans-serif, normal, normal, normal, normal, scalable. Alternatively, a font may be specified using an absolute path to a .ttf file, by using the *fname* kwarg. @@ -672,9 +649,8 @@ <http://www.fontconfig.org/>`_ pattern, if it is the only argument provided. See the documentation on `fontconfig patterns <http://www.fontconfig.org/fontconfig-user.html>`_. This support - does not require fontconfig to be installed or support for it to - be enabled. We are merely borrowing its pattern syntax for use - here. + does not require fontconfig to be installed. We are merely + borrowing its pattern syntax for use here. Note that matplotlib's internal font manager and fontconfig use a different algorithm to lookup fonts, so the results of the same pattern @@ -760,6 +736,7 @@ if self._slant is None: return rcParams['font.style'] return self._slant + get_slant = get_style def get_variant(self): """ @@ -772,7 +749,10 @@ def get_weight(self): """ - Return the font weight. + Set the font weight. Options are: A numeric value in the + range 0-1000 or one of 'light', 'normal', 'regular', 'book', + 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', + 'heavy', 'extra bold', 'black' """ if self._weight is None: return rcParams['font.weight'] @@ -780,8 +760,9 @@ def get_stretch(self): """ - Return the font stretch or width. Options are: 'normal', - 'narrow', 'condensed', or 'wide'. + Return the font stretch or width. Options are: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'. """ if self._stretch is None: return rcParams['font.stretch'] @@ -793,8 +774,17 @@ """ if self._size is None: return rcParams['font.size'] - return float(self._size) + return self._size + def get_size_in_points(self): + if self._size is not None: + try: + return float(self._size) + except ValueError: + pass + default_size = fontManager.get_default_size() + return default_size * font_scalings.get(self._size) + def get_file(self): """ Return the filename of the associated font. @@ -849,46 +839,51 @@ def set_weight(self, weight): """ - Set the font weight. + Set the font weight. May be either a numeric value in the + range 0-1000 or one of 'ultralight', 'light', 'normal', + 'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', + 'demi', 'bold', 'heavy', 'extra bold', 'black' """ - if (weight is not None and - weight not in weight_dict and - weight not in weight_dict.keys()): - raise ValueError("weight is invalid") + if weight is not None: + try: + weight = int(weight) + if weight < 0 or weight > 1000: + raise ValueError() + except ValueError: + if weight not in weight_dict: + raise ValueError("weight is invalid") self._weight = weight def set_stretch(self, stretch): """ - Set the font stretch or width. Options are: 'normal', 'narrow', - 'condensed', or 'wide'. + Set the font stretch or width. Options are: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded' or + 'ultra-expanded', or a numeric value in the range 0-1000. """ - if stretch not in ('normal', 'narrow', 'condensed', 'wide', None): - raise ValueError("stretch is invalid") + if stretch is not None: + try: + stretch = int(stretch) + if stretch < 0 or stretch > 1000: + raise ValueError() + except ValueError: + if stretch not in stretch_dict: + raise ValueError("stretch is invalid") self._stretch = stretch def set_size(self, size): """ - Set the font size. Either an absolute value of 'xx-small', - 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'; - or a relative value of smaller or larger; or an absolute font - size, e.g. 12; or 'scalable'. + Set the font size. Either an relative value of 'xx-small', + 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large' + or an absolute font size, e.g. 12. """ - if size is None: - self._size = None - else: - if is_string_like(size): - parent_size = fontManager.get_default_size() - scaling = font_scalings.get(size) - if scaling is not None: - size = parent_size * scaling - else: - try: - size = float(size) - except ValueError: - size = parent_size - assert(type(size) in (int, float)) - self._size = size - get_size_in_points = get_size + if size is not None: + try: + size = float(size) + except ValueError: + if size is not None and size not in font_scalings: + raise ValueError("size is invalid") + self._size = size def set_file(self, file): """ @@ -958,24 +953,18 @@ class FontManager: """ On import, the :class:`FontManager` singleton instance creates a - dictionary of TrueType fonts based on the font properties: name, - style, variant, weight, stretch, and size. The :meth:`findfont` - method searches this dictionary for a font file name that exactly - matches the font properties of the specified text. If none is - found, a default font is returned. By updating the dictionary - with the properties of the found font, the font dictionary can act - like a font cache. - - .. note:: The font lookup mechanism is very exact and brittle. If - the *exact* font specified is not found, a default font is - always returned. This should be improved in the future. + list of TrueType fonts based on the font properties: name, style, + variant, weight, stretch, and size. The :meth:`findfont` method + does a nearest neighbor search to find the font that most closely + matches the specification. If no good enough match is found, a + default font is returned. """ def __init__(self, size=None, weight='normal'): self.__default_weight = weight self.default_size = size - paths = [os.path.join(rcParams['datapath'],'fonts','ttf'), - os.path.join(rcParams['datapath'],'fonts','afm')] + paths = [os.path.join(rcParams['datapath'], 'fonts', 'ttf'), + os.path.join(rcParams['datapath'], 'fonts', 'afm')] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: @@ -1002,7 +991,7 @@ # use anything self.defaultFont = self.ttffiles[0] - self.ttfdict = createFontDict(self.ttffiles) + self.ttflist = createFontList(self.ttffiles) if rcParams['pdf.use14corefonts']: # Load only the 14 PDF core fonts. These fonts do not need to be @@ -1013,12 +1002,15 @@ # ZapfDingbats. afmpath = os.path.join(rcParams['datapath'],'fonts','pdfcorefonts') afmfiles = findSystemFonts(afmpath, fontext='afm') - self.afmdict = createFontDict(afmfiles, fontext='afm') + self.afmlist = createFontList(afmfiles, fontext='afm') else: self.afmfiles = findSystemFonts(paths, fontext='afm') + \ findSystemFonts(fontext='afm') - self.afmdict = createFontDict(self.afmfiles, fontext='afm') + self.afmlist = createFontList(self.afmfiles, fontext='afm') + self.ttf_lookup_cache = {} + self.afm_lookup_cache = {} + def get_default_weight(self): """ Return the default font weight. @@ -1054,17 +1046,133 @@ # !!!! Needs implementing raise NotImplementedError + # Each of the scoring functions below should return a value between + # 0.0 (perfect match) and 1.0 (terrible match) + def score_family(self, families, family2): + """ + Returns a match score between the list of font families in + *families* and the font family name *family2*. + + An exact match anywhere in the list returns 0.0. + + A match by generic font name will return 0.1. + + No match will return 1.0. + """ + for i, family1 in enumerate(families): + if family1.lower() in font_family_aliases: + if family1 == 'sans': + family1 == 'sans-serif' + options = rcParams['font.' + family1] + if family2 in options: + idx = options.index(family2) + return 0.1 + elif family1.lower() == family2.lower(): + return 0.0 + return 1.0 + + def score_style(self, style1, style2): + """ + Returns a match score between *style1* and *style2*. + + An exact match returns 0.0. + + A match between 'italic' and 'oblique' returns 0.1. + + No match returns 1.0. + """ + if style1 == style2: + return 0.0 + elif style1 in ('italic', 'oblique') and \ + style2 in ('italic', 'oblique'): + return 0.1 + return 1.0 + + def score_variant(self, variant1, variant2): + """ + Returns a match score between *variant1* and *variant2*. + + An exact match returns 0.0, otherwise 1.0. + """ + if variant1 == variant2: + return 0.0 + else: + return 1.0 + + def score_stretch(self, stretch1, stretch2): + """ + Returns a match score between *stretch1* and *stretch2*. + + The result is the absolute value of the difference between the + CSS numeric values of *stretch1* and *stretch2*, normalized + between 0.0 and 1.0. + """ + try: + stretchval1 = int(stretch1) + except ValueError: + stretchval1 = stretch_dict.get(stretch1, 500) + try: + stretchval2 = int(stretch2) + except ValueError: + stretchval2 = stretch_dict.get(stretch2, 500) + return abs(stretchval1 - stretchval2) / 1000.0 + + def score_weight(self, weight1, weight2): + """ + Returns a match score between *weight1* and *weight2*. + + The result is the absolute value of the difference between the + CSS numeric values of *weight1* and *weight2*, normalized + between 0.0 and 1.0. + """ + try: + weightval1 = int(weight1) + except ValueError: + weightval1 = weight_dict.get(weight1, 500) + try: + weightval2 = int(weight2) + except ValueError: + weightval2 = weight_dict.get(weight2, 500) + return abs(weightval1 - weightval2) / 1000.0 + + def score_size(self, size1, size2): + """ + Returns a match score between *size1* and *size2*. + + If *size2* (the size specified in the font file) is 'scalable', this + function always returns 0.0, since any font size can be generated. + + Otherwise, the result is the absolute distance between *size1* and + *size2*, normalized so that the usual range of font sizes (6pt - + 72pt) will lie between 0.0 and 1.0. + """ + if size2 == 'scalable': + return 0.0 + # Size value should have already been + try: + sizeval1 = float(size1) + except ValueError: + sizeval1 = self.default_size * font_scalings(size1) + try: + sizeval2 = float(size2) + except ValueError: + return 1.0 + return abs(sizeval1 - sizeval2) / 72.0 + def findfont(self, prop, fontext='ttf'): """ - Search the font dictionary for a font that exactly or closely - matches the specified font properties. See the - :class:`FontProperties` class for a description. + Search the font list for the font that most closely matches + the :class:`FontProperties` *prop*. - The properties are searched in the following order: name, - style, variant, weight, stretch, and size. The font weight - always matches returning the closest weight, and the font size - always matches for scalable fonts. An oblique style font will - be used inplace of a missing italic style font if present. + :meth:`findfont` performs a nearest neighbor search. Each + font is given a similarity score to the target font + properties. The first font with the highest score is + returned. If no matches below a certain threshold are found, + the default font (usually Vera Sans) is returned. + + The result is cached, so subsequent lookups don't have to + perform the O(n) nearest neighbor search. + See the `W3C Cascading Style Sheet, Level 1 <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ documentation for a description of the font finding algorithm. @@ -1078,108 +1186,45 @@ return fname if fontext == 'afm': - fontdict = self.afmdict + font_cache = self.afm_lookup_cache + fontlist = self.afmlist else: - fontdict = self.ttfdict + font_cache = self.ttf_lookup_cache + fontlist = self.ttflist - original_name = prop.get_family()[0] - style = prop.get_style() - variant = prop.get_variant() - weight = weight_as_number(prop.get_weight()) - stretch = prop.get_stretch() - size = str(prop.get_size_in_points()) + cached = font_cache.get(prop) + if cached: + return cached - def lookup_name(name): - try: - fname = fontdict[name][style][variant][weight][stretch][size] - verbose.report('\tfindfont cached %(name)s, %(style)s, %(variant)s, %(weight)s, %(stretch)s, %(size)s'%locals(), 'debug') - verbose.report('findfont returning %s'%fname, 'debug') - return fname - except KeyError: - pass + best_score = 1e64 + best_font = None + for font in fontlist: + # Matching family should have highest priority, so it is multiplied + # by 10.0 + score = \ + self.score_family(prop.get_family(), font.name) * 10.0 + \ + self.score_style(prop.get_style(), font.style) + \ + self.score_variant(prop.get_variant(), font.variant) + \ + self.score_weight(prop.get_weight(), font.weight) + \ + self.score_stretch(prop.get_stretch(), font.stretch) + \ + self.score_size(prop.get_size(), font.size) + if score < best_score: + best_score = score + best_font = font - fname = None - font = fontdict - if name in font: - font = font[name] - else: - verbose.report('\tfindfont failed %(name)s'%locals(), 'debug') - return None + if best_font is None or best_score > 10.0: + verbose.report('findfont: Could not match %s. Returning %s' % + (prop, self.defaultFont)) + result = self.defaultFont + else: + verbose.report('findfont: Matching %s to %s (%s) with score of %f' % + (prop, best_font.name, best_font.fname, best_score)) + result = best_font.fname - if style in font: - font = font[style] - elif style == 'italic' and 'oblique' in font: - font = font['oblique'] - elif style == 'oblique' and 'italic' in font: - font = font['italic'] - else: - verbose.report('\tfindfont failed %(name)s, %(style)s'%locals(), 'debug') - return None + font_cache[prop] = result + return result - if variant in font: - font = font[variant] - else: - verbose.report('\tfindfont failed %(name)s, %(style)s, %(variant)s'%locals(), 'debug') - return None - if weight not in font: - setWeights(font) - if weight not in font: - return None - font = font[weight] - - if stretch in font: - stretch_font = font[stretch] - if 'scalable' in stretch_font: - fname = stretch_font['scalable'] - elif size in stretch_font: - fname = stretch_font[size] - - if fname is None: - for val in font.values(): - if 'scalable' in val: - fname = val['scalable'] - break - - if fname is None: - for val in font.values(): - if size in val: - fname = val[size] - break - - if fname is None: - verbose.report('\tfindfont failed %(name)s, %(style)s, %(variant)s %(weight)s, %(stretch)s'%locals(), 'debug') - else: - fontkey = FontKey(",".join(prop.get_family()), style, variant, weight, stretch, size) - add_filename(fontdict, fontkey, fname) - verbose.report('\tfindfont found %(name)s, %(style)s, %(variant)s %(weight)s, %(stretch)s, %(size)s'%locals(), 'debug') - verbose.report('findfont returning %s'%fname, 'debug') - return fname - - font_family_aliases = set(['serif', 'sans-serif', 'cursive', - 'fantasy', 'monospace', 'sans']) - - for name in prop.get_family(): - if name in font_family_aliases: - if name == 'sans': - name = 'sans-serif' - for name2 in rcParams['font.' + name]: - fname = lookup_name(name2) - if fname: - break - else: - fname = lookup_name(name) - if fname: - break - - if not fname: - fontkey = FontKey(",".join(prop.get_family()), style, variant, weight, stretch, size) - add_filename(fontdict, fontkey, self.defaultFont) - verbose.report('Could not match %s, %s, %s. Returning %s' % (name, style, weight, self.defaultFont)) - return self.defaultFont - return fname - - _is_opentype_cff_font_cache = {} def is_opentype_cff_font(filename): """ @@ -1232,7 +1277,7 @@ return result else: - _fmcache = os.path.join(get_configdir(), 'fontManager.cache') + _fmcache = os.path.join(get_configdir(), 'fontList.cache') fontManager = None Modified: trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py =================================================================== --- trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -178,5 +178,4 @@ if val != []: val = ','.join(val) props.append(":%s=%s" % (key, val)) - print parse_fontconfig_pattern(''.join(props)) return ''.join(props) Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/text.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -445,9 +445,17 @@ "Return the :class:`~font_manager.FontProperties` object" return self._fontproperties + def get_family(self): + "Return the list of font families used for font lookup" + return self._fontproperties.get_family() + + def get_fontfamily(self): + 'alias for get_family' + return self.get_family() + def get_name(self): "Return the font name as string" - return self._fontproperties.get_family()[-1] # temporary hack. + return self._fontproperties.get_name() def get_style(self): "Return the font style as string" @@ -457,26 +465,42 @@ "Return the font size as integer" return self._fontproperties.get_size_in_points() + def get_variant(self): + "Return the font variant as a string" + return self._fontproperties.get_variant() + + def get_fontvariant(self): + 'alias for get_variant' + return self.get_variant() + def get_weight(self): - "Get the font weight as string" + "Get the font weight as string or number" return self._fontproperties.get_weight() def get_fontname(self): 'alias for get_name' - return self._fontproperties.get_family()[-1] # temporary hack. + return self.get_name() def get_fontstyle(self): 'alias for get_style' - return self._fontproperties.get_style() + return self.get_style() def get_fontsize(self): 'alias for get_size' - return self._fontproperties.get_size_in_points() + return self.get_size() def get_fontweight(self): 'alias for get_weight' - return self._fontproperties.get_weight() + return self.get_weight() + def get_stretch(self): + 'Get the font stretch as a string or number' + return self._fontproperties.get_stretch() + + def get_fontstretch(self): + 'alias for get_stretch' + return self.get_stretch() + def get_ha(self): 'alias for get_horizontalalignment' return self.get_horizontalalignment() @@ -645,35 +669,35 @@ def set_family(self, fontname): """ - Set the font family + Set the font family. May be either a single string, or a list + of strings in decreasing priority. Each string may be either + a real font name or a generic font class name. If the latter, + the specific font names will be looked up in the + :file:`matplotlibrc` file. - ACCEPTS: [ 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ] + ACCEPTS: [ FONTNAME | 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ] """ self._fontproperties.set_family(fontname) def set_variant(self, variant): """ - Set the font variant, eg, + Set the font variant, either 'normal' or 'small-caps'. ACCEPTS: [ 'normal' | 'small-caps' ] """ self._fontproperties.set_variant(variant) def set_name(self, fontname): - """ - Set the font name, + """alias for set_family""" + return self.set_family(fontname) - ACCEPTS: string eg, ['Sans' | 'Courier' | 'Helvetica' ...] - """ - self._fontproperties.set_family(fontname) - def set_fontname(self, fontname): - 'alias for set_name' + """alias for set_family""" self.set_family(fontname) def set_style(self, fontstyle): """ - Set the font style + Set the font style. ACCEPTS: [ 'normal' | 'italic' | 'oblique'] """ @@ -681,33 +705,45 @@ def set_fontstyle(self, fontstyle): 'alias for set_style' - self._fontproperties.set_style(fontstyle) + return self.set_style(fontstyle) def set_size(self, fontsize): """ - Set the font size, eg., 8, 10, 12, 14... + Set the font size. May be either a size string, relative to + the default font size, or an absolute font size in points. - ACCEPTS: [ size in points | relative size eg 'smaller', 'x-large' ] + ACCEPTS: [ size in points | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large' ] """ self._fontproperties.set_size(fontsize) def set_fontsize(self, fontsize): 'alias for set_size' - self._fontproperties.set_size(fontsize) + return self.set_size(fontsize) + def set_weight(self, weight): + """ + Set the font weight. + + ACCEPTS: [ a numeric value in range 0-1000 | 'ultralight' | 'light' | 'normal' | 'regular' | 'book' | 'medium' | 'roman' | 'semibold' | 'demibold' | 'demi' | 'bold' | 'heavy' | 'extra bold' | 'black' ] + """ + self._fontproperties.set_weight(weight) + def set_fontweight(self, weight): 'alias for set_weight' - self._fontproperties.set_weight(weight) + return self.set_weight(weight) - def set_weight(self, weight): + def set_stretch(self, stretch): """ - Set the font weight + Set the font stretch (horizontal condensation or expansion). - ACCEPTS: [ 'normal' | 'bold' | 'heavy' | 'light' | 'ultrabold' | - 'ultralight'] + ACCEPTS: [ a numeric value in range 0-1000 | 'ultra-condensed' | 'extra-condensed' | 'condensed' | 'semi-condensed' | 'normal' | 'semi-expanded' | 'expanded' | 'extra-expanded' | 'ultra-expanded' ] """ - self._fontproperties.set_weight(weight) + self._fontproperties.set_stretch(stretch) + def set_fontstretch(self, stretch): + 'alias for set_stretch' + return self.set_stretch(stretch) + def set_position(self, xy): """ Set the (*x*, *y*) position of the text This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-28 13:24:51
|
Revision: 6341 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6341&view=rev Author: mdboom Date: 2008-10-28 12:51:39 +0000 (Tue, 28 Oct 2008) Log Message: ----------- Update TkAgg and GtkAgg extensions to match yesterday's refactoring in backend_agg Modified Paths: -------------- trunk/matplotlib/src/_gtkagg.cpp trunk/matplotlib/src/_tkagg.cpp Modified: trunk/matplotlib/src/_gtkagg.cpp =================================================================== --- trunk/matplotlib/src/_gtkagg.cpp 2008-10-27 21:13:24 UTC (rev 6340) +++ trunk/matplotlib/src/_gtkagg.cpp 2008-10-28 12:51:39 UTC (rev 6341) @@ -98,7 +98,7 @@ //destrb.clear(agg::rgba(1, 1, 1, 0)); agg::rect_base<int> region(destx, desty, (int)r, srcheight-(int)b); - destrb.copy_from(*aggRenderer->renderingBuffer, ®ion, + destrb.copy_from(aggRenderer->renderingBuffer, ®ion, -destx, -desty); } Modified: trunk/matplotlib/src/_tkagg.cpp =================================================================== --- trunk/matplotlib/src/_tkagg.cpp 2008-10-27 21:13:24 UTC (rev 6340) +++ trunk/matplotlib/src/_tkagg.cpp 2008-10-28 12:51:39 UTC (rev 6341) @@ -131,7 +131,7 @@ renderer_base destrb(destpf); agg::rect_base<int> region(destx, desty, (int)r, srcheight-(int)b); - destrb.copy_from(*aggRenderer->renderingBuffer, ®ion, + destrb.copy_from(aggRenderer->renderingBuffer, ®ion, -destx, -desty); } else { has_bbox = false; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-27 21:13:34
|
Revision: 6340 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6340&view=rev Author: mdboom Date: 2008-10-27 21:13:24 +0000 (Mon, 27 Oct 2008) Log Message: ----------- Reduce heap allocation of objects. Be safer when forced to do heap allocation. Remove some dead code. Modified Paths: -------------- trunk/matplotlib/src/_backend_agg.cpp trunk/matplotlib/src/_backend_agg.h Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-10-27 19:40:25 UTC (rev 6339) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-10-27 21:13:24 UTC (rev 6340) @@ -263,63 +263,57 @@ height(height), dpi(dpi), NUMBYTES(width*height*4), + pixBuffer(NULL), + renderingBuffer(), alphaBuffer(NULL), - alphaMaskRenderingBuffer(NULL), - alphaMask(NULL), - pixfmtAlphaMask(NULL), - rendererBaseAlphaMask(NULL), - rendererAlphaMask(NULL), - scanlineAlphaMask(NULL), + alphaMaskRenderingBuffer(), + alphaMask(alphaMaskRenderingBuffer), + pixfmtAlphaMask(alphaMaskRenderingBuffer), + rendererBaseAlphaMask(), + rendererAlphaMask(), + scanlineAlphaMask(), + slineP8(), + slineBin(), + pixFmt(), + rendererBase(), + rendererAA(), + rendererBin(), + theRasterizer(), debug(debug) { _VERBOSE("RendererAgg::RendererAgg"); unsigned stride(width*4); pixBuffer = new agg::int8u[NUMBYTES]; - renderingBuffer = new agg::rendering_buffer; - renderingBuffer->attach(pixBuffer, width, height, stride); - - slineP8 = new scanline_p8; - slineBin = new scanline_bin; - - pixFmt = new pixfmt(*renderingBuffer); - rendererBase = new renderer_base(*pixFmt); - rendererBase->clear(agg::rgba(1, 1, 1, 0)); - - rendererAA = new renderer_aa(*rendererBase); - rendererBin = new renderer_bin(*rendererBase); - theRasterizer = new rasterizer(); - //theRasterizer->filling_rule(agg::fill_even_odd); - //theRasterizer->filling_rule(agg::fill_non_zero); - + renderingBuffer.attach(pixBuffer, width, height, stride); + pixFmt.attach(renderingBuffer); + rendererBase.attach(pixFmt); + rendererBase.clear(agg::rgba(1, 1, 1, 0)); + rendererAA.attach(rendererBase); + rendererBin.attach(rendererBase); } void RendererAgg::create_alpha_buffers() { if (!alphaBuffer) { unsigned stride(width*4); alphaBuffer = new agg::int8u[NUMBYTES]; - alphaMaskRenderingBuffer = new agg::rendering_buffer; - alphaMaskRenderingBuffer->attach(alphaBuffer, width, height, stride); - alphaMask = new alpha_mask_type(*alphaMaskRenderingBuffer); - - pixfmtAlphaMask = new agg::pixfmt_gray8(*alphaMaskRenderingBuffer); - rendererBaseAlphaMask = new renderer_base_alpha_mask_type(*pixfmtAlphaMask); - rendererAlphaMask = new renderer_alpha_mask_type(*rendererBaseAlphaMask); - scanlineAlphaMask = new agg::scanline_p8(); + alphaMaskRenderingBuffer.attach(alphaBuffer, width, height, stride); + rendererBaseAlphaMask.attach(pixfmtAlphaMask); + rendererAlphaMask.attach(rendererBaseAlphaMask); } } template<class R> void -RendererAgg::set_clipbox(const Py::Object& cliprect, R rasterizer) { +RendererAgg::set_clipbox(const Py::Object& cliprect, R& rasterizer) { //set the clip rectangle from the gc _VERBOSE("RendererAgg::set_clipbox"); double l, b, r, t; if (py_convert_bbox(cliprect.ptr(), l, b, r, t)) { - rasterizer->clip_box(int(mpl_round(l)) + 1, height - int(mpl_round(b)), - int(mpl_round(r)), height - int(mpl_round(t))); + rasterizer.clip_box(int(mpl_round(l)) + 1, height - int(mpl_round(b)), + int(mpl_round(r)), height - int(mpl_round(t))); } _VERBOSE("RendererAgg::set_clipbox done"); @@ -341,45 +335,6 @@ return face; } -SnapData -SafeSnap::snap (const float& x, const float& y) { - xsnap = (int)(x + 0.5f); - ysnap = (int)(y + 0.5f); - - if ( first || ( (xsnap!=lastxsnap) || (ysnap!=lastysnap) ) ) { - lastxsnap = xsnap; - lastysnap = ysnap; - lastx = x; - lasty = y; - first = false; - return SnapData(true, xsnap, ysnap); - } - - // ok both are equal and we need to do an offset - if ( (x==lastx) && (y==lasty) ) { - // no choice but to return equal coords; set newpoint = false - lastxsnap = xsnap; - lastysnap = ysnap; - lastx = x; - lasty = y; - return SnapData(false, xsnap, ysnap); - } - - // ok the real points are not identical but the rounded ones, so do - // a one pixel offset - if (x>lastx) xsnap += 1.; - else if (x<lastx) xsnap -= 1.; - - if (y>lasty) ysnap += 1.; - else if (y<lasty) ysnap -= 1.; - - lastxsnap = xsnap; - lastysnap = ysnap; - lastx = x; - lasty = y; - return SnapData(true, xsnap, ysnap); -} - template<class Path> bool should_snap(Path& path, const agg::trans_affine& trans) { // If this contains only straight horizontal or vertical lines, it should be @@ -443,12 +398,17 @@ throw Py::MemoryError("RendererAgg::copy_from_bbox could not allocate memory for buffer"); } - agg::rendering_buffer rbuf; - rbuf.attach(reg->data, reg->width, reg->height, reg->stride); + try { + agg::rendering_buffer rbuf; + rbuf.attach(reg->data, reg->width, reg->height, reg->stride); - pixfmt pf(rbuf); - renderer_base rb(pf); - rb.copy_from(*renderingBuffer, &rect, -rect.x1, -rect.y1); + pixfmt pf(rbuf); + renderer_base rb(pf); + rb.copy_from(renderingBuffer, &rect, -rect.x1, -rect.y1); + } catch (...) { + delete reg; + throw Py::RuntimeError("An unknown error occurred in copy_from_bbox"); + } return Py::asObject(reg); } @@ -468,7 +428,7 @@ region->height, region->stride); - rendererBase->copy_from(rbuf, 0, region->rect.x1, region->rect.y1); + rendererBase.copy_from(rbuf, 0, region->rect.x1, region->rect.y1); return Py::Object(); } @@ -488,12 +448,12 @@ trans *= agg::trans_affine_translation(0.0, (double)height); PathIterator clippath_iter(clippath); - rendererBaseAlphaMask->clear(agg::gray8(0, 0)); + rendererBaseAlphaMask.clear(agg::gray8(0, 0)); transformed_path_t transformed_clippath(clippath_iter, trans); agg::conv_curve<transformed_path_t> curved_clippath(transformed_clippath); - theRasterizer->add_path(curved_clippath); - rendererAlphaMask->color(agg::gray8(255, 255)); - agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); + theRasterizer.add_path(curved_clippath); + rendererAlphaMask.color(agg::gray8(255, 255)); + agg::render_scanlines(theRasterizer, scanlineAlphaMask, rendererAlphaMask); lastclippath = clippath; lastclippath_transform = clippath_trans; } @@ -544,24 +504,22 @@ //maxim's suggestions for cached scanlines agg::scanline_storage_aa8 scanlines; - theRasterizer->reset(); - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); + theRasterizer.reset(); + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); agg::int8u staticFillCache[MARKER_CACHE_SIZE]; agg::int8u staticStrokeCache[MARKER_CACHE_SIZE]; - agg::int8u* fillCache = NULL; - agg::int8u* strokeCache = NULL; + agg::int8u* fillCache = staticFillCache; + agg::int8u* strokeCache = staticStrokeCache; try { unsigned fillSize = 0; if (face.first) { - theRasterizer->add_path(marker_path_curve); - agg::render_scanlines(*theRasterizer, *slineP8, scanlines); + theRasterizer.add_path(marker_path_curve); + agg::render_scanlines(theRasterizer, slineP8, scanlines); fillSize = scanlines.byte_size(); - if (fillSize < MARKER_CACHE_SIZE) - fillCache = staticFillCache; - else + if (fillSize >= MARKER_CACHE_SIZE) fillCache = new agg::int8u[fillSize]; scanlines.serialize(fillCache); } @@ -570,18 +528,16 @@ stroke.width(gc.linewidth); stroke.line_cap(gc.cap); stroke.line_join(gc.join); - theRasterizer->reset(); - theRasterizer->add_path(stroke); - agg::render_scanlines(*theRasterizer, *slineP8, scanlines); + theRasterizer.reset(); + theRasterizer.add_path(stroke); + agg::render_scanlines(theRasterizer, slineP8, scanlines); unsigned strokeSize = scanlines.byte_size(); - if (strokeSize < MARKER_CACHE_SIZE) - strokeCache = staticStrokeCache; - else + if (strokeSize >= MARKER_CACHE_SIZE) strokeCache = new agg::int8u[strokeSize]; scanlines.serialize(strokeCache); - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); set_clipbox(gc.cliprect, rendererBase); bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans); @@ -592,7 +548,7 @@ if (has_clippath) { while (path_transformed.vertex(&x, &y) != agg::path_cmd_stop) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); + pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_aa_renderer_type ren(r); @@ -608,14 +564,14 @@ } else { while (path_transformed.vertex(&x, &y) != agg::path_cmd_stop) { if (face.first) { - rendererAA->color(face.second); + rendererAA.color(face.second); sa.init(fillCache, fillSize, x, y); - agg::render_scanlines(sa, sl, *rendererAA); + agg::render_scanlines(sa, sl, rendererAA); } - rendererAA->color(gc.color); + rendererAA.color(gc.color); sa.init(strokeCache, strokeSize, x, y); - agg::render_scanlines(sa, sl, *rendererAA); + agg::render_scanlines(sa, sl, rendererAA); } } } catch(...) { @@ -734,8 +690,8 @@ GCAgg gc = GCAgg(args[4], dpi); - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); set_clipbox(gc.cliprect, theRasterizer); agg::rendering_buffer srcbuf((agg::int8u*)buffer, width, height, width); @@ -764,10 +720,10 @@ image_accessor_type ia(pixf_img, 0); image_span_gen_type image_span_generator(ia, interpolator, filter); span_gen_type output_span_generator(&image_span_generator, gc.color); - renderer_type ri(*rendererBase, sa, output_span_generator); + renderer_type ri(rendererBase, sa, output_span_generator); - theRasterizer->add_path(rect2); - agg::render_scanlines(*theRasterizer, *slineP8, ri); + theRasterizer.add_path(rect2); + agg::render_scanlines(theRasterizer, slineP8, ri); Py_XDECREF(image_array); @@ -789,8 +745,8 @@ agg::trans_affine clippath_trans; bool has_clippath = false; - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); if (args.size() == 6) { clippath = args[4]; clippath_trans = py_to_agg_transformation_matrix(args[5], false); @@ -828,15 +784,15 @@ image_accessor_type ia(pixf, agg::rgba8(0, 0, 0, 0)); interpolator_type interpolator(inv_mtx); image_span_gen_type image_span_generator(ia, interpolator); - pixfmt_amask_type pfa(*pixFmt, *alphaMask); + pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); renderer_type ri(r, sa, image_span_generator); - theRasterizer->add_path(rect2); - agg::render_scanlines(*theRasterizer, *slineP8, ri); + theRasterizer.add_path(rect2); + agg::render_scanlines(theRasterizer, slineP8, ri); } else { set_clipbox(box_obj, rendererBase); - rendererBase->blend_from(pixf, 0, (int)x, (int)(height-(y+image->rowsOut))); + rendererBase.blend_from(pixf, 0, (int)x, (int)(height-(y+image->rowsOut))); } image->flipud_out(empty); @@ -857,29 +813,29 @@ // Render face if (face.first) { - theRasterizer->add_path(path); + theRasterizer.add_path(path); if (gc.isaa) { if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); + pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_aa_renderer_type ren(r); ren.color(face.second); - agg::render_scanlines(*theRasterizer, *slineP8, ren); + agg::render_scanlines(theRasterizer, slineP8, ren); } else { - rendererAA->color(face.second); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + rendererAA.color(face.second); + agg::render_scanlines(theRasterizer, slineP8, rendererAA); } } else { if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); + pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_bin_renderer_type ren(r); ren.color(face.second); - agg::render_scanlines(*theRasterizer, *slineP8, ren); + agg::render_scanlines(theRasterizer, slineP8, ren); } else { - rendererBin->color(face.second); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererBin); + rendererBin.color(face.second); + agg::render_scanlines(theRasterizer, slineP8, rendererBin); } } } @@ -895,7 +851,7 @@ stroke.width(linewidth); stroke.line_cap(gc.cap); stroke.line_join(gc.join); - theRasterizer->add_path(stroke); + theRasterizer.add_path(stroke); } else { dash_t dash(path); for (GCAgg::dash_t::const_iterator i = gc.dashes.begin(); @@ -912,30 +868,30 @@ stroke.line_cap(gc.cap); stroke.line_join(gc.join); stroke.width(linewidth); - theRasterizer->add_path(stroke); + theRasterizer.add_path(stroke); } if (gc.isaa) { if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); + pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_aa_renderer_type ren(r); ren.color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, ren); + agg::render_scanlines(theRasterizer, slineP8, ren); } else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + rendererAA.color(gc.color); + agg::render_scanlines(theRasterizer, slineP8, rendererAA); } } else { if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); + pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_bin_renderer_type ren(r); ren.color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, ren); + agg::render_scanlines(theRasterizer, slineP8, ren); } else { - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); + rendererBin.color(gc.color); + agg::render_scanlines(theRasterizer, slineBin, rendererBin); } } } @@ -961,8 +917,8 @@ GCAgg gc = GCAgg(gc_obj, dpi); facepair_t face = _get_rgba_face(face_obj, gc.alpha); - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans); @@ -1074,8 +1030,8 @@ } // Handle any clipping globally - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); set_clipbox(cliprect, theRasterizer); bool has_clippath = render_clippath(clippath, clippath_trans); @@ -1430,21 +1386,28 @@ args.verify_length(0); int row_len = width*3; unsigned char* buf_tmp = new unsigned char[row_len * height]; - if (buf_tmp ==NULL) { + if (buf_tmp == NULL) { //todo: also handle allocation throw throw Py::MemoryError("RendererAgg::tostring_rgb could not allocate memory"); } - agg::rendering_buffer renderingBufferTmp; - renderingBufferTmp.attach(buf_tmp, - width, - height, - row_len); - agg::color_conv(&renderingBufferTmp, renderingBuffer, agg::color_conv_rgba32_to_rgb24()); + try { + agg::rendering_buffer renderingBufferTmp; + renderingBufferTmp.attach(buf_tmp, + width, + height, + row_len); + agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_rgb24()); + } catch (...) { + delete [] buf_tmp; + throw Py::RuntimeError("Unknown exception occurred in tostring_rgb"); + } + //todo: how to do this with native CXX PyObject* o = Py_BuildValue("s#", buf_tmp, row_len * height); + delete [] buf_tmp; return Py::asObject(o); } @@ -1463,12 +1426,16 @@ //todo: also handle allocation throw throw Py::MemoryError("RendererAgg::tostring_argb could not allocate memory"); } - agg::rendering_buffer renderingBufferTmp; - renderingBufferTmp.attach(buf_tmp, width, height, row_len); - agg::color_conv(&renderingBufferTmp, renderingBuffer, agg::color_conv_rgba32_to_argb32()); + try { + agg::rendering_buffer renderingBufferTmp; + renderingBufferTmp.attach(buf_tmp, width, height, row_len); + agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_argb32()); + } catch (...) { + delete [] buf_tmp; + throw Py::RuntimeError("Unknown exception occurred in tostring_argb"); + } - //todo: how to do this with native CXX PyObject* o = Py_BuildValue("s#", buf_tmp, row_len * height); delete [] buf_tmp; @@ -1488,14 +1455,19 @@ //todo: also handle allocation throw throw Py::MemoryError("RendererAgg::tostring_bgra could not allocate memory"); } - agg::rendering_buffer renderingBufferTmp; - renderingBufferTmp.attach(buf_tmp, - width, - height, - row_len); - agg::color_conv(&renderingBufferTmp, renderingBuffer, agg::color_conv_rgba32_to_bgra32()); + try { + agg::rendering_buffer renderingBufferTmp; + renderingBufferTmp.attach(buf_tmp, + width, + height, + row_len); + agg::color_conv(&renderingBufferTmp, &renderingBuffer, agg::color_conv_rgba32_to_bgra32()); + } catch (...) { + delete [] buf_tmp; + throw Py::RuntimeError("Unknown exception occurred in tostring_bgra"); + } //todo: how to do this with native CXX PyObject* o = Py_BuildValue("s#", @@ -1557,12 +1529,17 @@ int newsize = newwidth * newheight * 4; unsigned char* buf = new unsigned char[newsize]; + if (buf == NULL) { + throw Py::MemoryError("RendererAgg::tostring_minimized could not allocate memory"); + } + unsigned int* dst = (unsigned int*)buf; unsigned int* src = (unsigned int*)pixBuffer; for (int y = ymin; y < ymax; ++y) for (int x = xmin; x < xmax; ++x, ++dst) *dst = src[y * width + x]; + // The Py::String will take over the buffer data = Py::String((const char *)buf, (int)newsize); } @@ -1586,7 +1563,7 @@ _VERBOSE("RendererAgg::clear"); args.verify_length(0); - rendererBase->clear(agg::rgba(1, 1, 1, 0)); + rendererBase.clear(agg::rgba(1, 1, 1, 0)); return Py::Object(); } @@ -1627,25 +1604,8 @@ _VERBOSE("RendererAgg::~RendererAgg"); - - delete slineP8; - delete slineBin; - delete theRasterizer; - delete rendererAA; - delete rendererBin; - delete rendererBase; - delete pixFmt; - delete renderingBuffer; - - delete alphaMask; - delete alphaMaskRenderingBuffer; delete [] alphaBuffer; delete [] pixBuffer; - delete pixfmtAlphaMask; - delete rendererBaseAlphaMask; - delete rendererAlphaMask; - delete scanlineAlphaMask; - } /* ------------ module methods ------------- */ @@ -1752,5 +1712,4 @@ static _backend_agg_module* _backend_agg = NULL; _backend_agg = new _backend_agg_module; - } Modified: trunk/matplotlib/src/_backend_agg.h =================================================================== --- trunk/matplotlib/src/_backend_agg.h 2008-10-27 19:40:25 UTC (rev 6339) +++ trunk/matplotlib/src/_backend_agg.h 2008-10-27 21:13:24 UTC (rev 6340) @@ -64,25 +64,6 @@ typedef agg::renderer_base<agg::pixfmt_gray8> renderer_base_alpha_mask_type; typedef agg::renderer_scanline_aa_solid<renderer_base_alpha_mask_type> renderer_alpha_mask_type; -struct SnapData { - SnapData(const bool& newpoint, const float& xsnap, const float& ysnap) : - newpoint(newpoint), xsnap(xsnap), ysnap(ysnap) {} - bool newpoint; - float xsnap, ysnap; -}; - -class SafeSnap { - // snap to pixel center, avoiding 0 path length rounding errors. -public: - SafeSnap() : first(true), xsnap(0.0), lastx(0.0), lastxsnap(0.0), - ysnap(0.0), lasty(0.0), lastysnap(0.0) {} - SnapData snap (const float& x, const float& y); - -private: - bool first; - float xsnap, lastx, lastxsnap, ysnap, lasty, lastysnap; -}; - // a helper class to pass agg::buffer objects around. agg::buffer is // a class in the swig wrapper class BufferRegion : public Py::PythonExtension<BufferRegion> { @@ -198,27 +179,27 @@ size_t NUMBYTES; //the number of bytes in buffer agg::int8u *pixBuffer; - agg::rendering_buffer *renderingBuffer; + agg::rendering_buffer renderingBuffer; agg::int8u *alphaBuffer; - agg::rendering_buffer *alphaMaskRenderingBuffer; - alpha_mask_type *alphaMask; - agg::pixfmt_gray8 *pixfmtAlphaMask; - renderer_base_alpha_mask_type *rendererBaseAlphaMask; - renderer_alpha_mask_type *rendererAlphaMask; - agg::scanline_p8 *scanlineAlphaMask; + agg::rendering_buffer alphaMaskRenderingBuffer; + alpha_mask_type alphaMask; + agg::pixfmt_gray8 pixfmtAlphaMask; + renderer_base_alpha_mask_type rendererBaseAlphaMask; + renderer_alpha_mask_type rendererAlphaMask; + agg::scanline_p8 scanlineAlphaMask; + scanline_p8 slineP8; + scanline_bin slineBin; + pixfmt pixFmt; + renderer_base rendererBase; + renderer_aa rendererAA; + renderer_bin rendererBin; + rasterizer theRasterizer; + Py::Object lastclippath; + agg::trans_affine lastclippath_transform; - scanline_p8* slineP8; - scanline_bin* slineBin; - pixfmt *pixFmt; - renderer_base *rendererBase; - renderer_aa *rendererAA; - renderer_bin *rendererBin; - rasterizer *theRasterizer; - - const int debug; protected: @@ -227,7 +208,7 @@ agg::rgba rgb_to_color(const Py::SeqBase<Py::Object>& rgb, double alpha); facepair_t _get_rgba_face(const Py::Object& rgbFace, double alpha); template<class R> - void set_clipbox(const Py::Object& cliprect, R rasterizer); + void set_clipbox(const Py::Object& cliprect, R& rasterizer); bool render_clippath(const Py::Object& clippath, const agg::trans_affine& clippath_trans); template<class PathIteratorType> void _draw_path(PathIteratorType& path, bool has_clippath, @@ -251,8 +232,6 @@ private: void create_alpha_buffers(); - Py::Object lastclippath; - agg::trans_affine lastclippath_transform; }; // the extension module This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-27 19:40:27
|
Revision: 6339 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6339&view=rev Author: mdboom Date: 2008-10-27 19:40:25 +0000 (Mon, 27 Oct 2008) Log Message: ----------- Removed TODO (this has been implemented). Modified Paths: -------------- trunk/matplotlib/src/_backend_agg.cpp Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-10-27 18:52:41 UTC (rev 6338) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-10-27 19:40:25 UTC (rev 6339) @@ -775,7 +775,6 @@ } -// MGDTODO: Support clip paths Py::Object RendererAgg::draw_image(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_image"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-27 18:52:45
|
Revision: 6338 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6338&view=rev Author: mdboom Date: 2008-10-27 18:52:41 +0000 (Mon, 27 Oct 2008) Log Message: ----------- More docs. Modified Paths: -------------- trunk/matplotlib/doc/api/index.rst trunk/matplotlib/doc/devel/outline.rst trunk/matplotlib/lib/matplotlib/font_manager.py trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py trunk/matplotlib/lib/matplotlib/lines.py trunk/matplotlib/lib/matplotlib/mlab.py Added Paths: ----------- trunk/matplotlib/doc/api/font_manager_api.rst Added: trunk/matplotlib/doc/api/font_manager_api.rst =================================================================== --- trunk/matplotlib/doc/api/font_manager_api.rst (rev 0) +++ trunk/matplotlib/doc/api/font_manager_api.rst 2008-10-27 18:52:41 UTC (rev 6338) @@ -0,0 +1,21 @@ +*********************** +matplotlib font_manager +*********************** + +:mod:`matplotlib.font_manager` +============================== + +.. automodule:: matplotlib.font_manager + :members: + :undoc-members: + :show-inheritance: + +:mod:`matplotlib.fontconfig_pattern` +============================== + +.. automodule:: matplotlib.fontconfig_pattern + :members: + :undoc-members: + :show-inheritance: + + Modified: trunk/matplotlib/doc/api/index.rst =================================================================== --- trunk/matplotlib/doc/api/index.rst 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/doc/api/index.rst 2008-10-27 18:52:41 UTC (rev 6338) @@ -14,7 +14,6 @@ matplotlib_configuration_api.rst afm_api.rst artist_api.rst - figure_api.rst axes_api.rst axis_api.rst cbook_api.rst @@ -22,6 +21,8 @@ collections_api.rst colorbar_api.rst colors_api.rst + figure_api.rst + font_manager_api.rst nxutils_api.rst mathtext_api.rst mlab_api.rst Modified: trunk/matplotlib/doc/devel/outline.rst =================================================================== --- trunk/matplotlib/doc/devel/outline.rst 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/doc/devel/outline.rst 2008-10-27 18:52:41 UTC (rev 6338) @@ -126,17 +126,17 @@ dviread Darren needs conversion figure Darren needs conversion finance Darren needs conversion -font_manager Mike needs conversion -fontconfig_pattern Mike needs conversion +font_manager Mike converted +fontconfig_pattern Mike converted image needs conversion legend needs conversion -lines needs conversion +lines Mike & ??? converted mathtext Mike converted -mlab needs conversion -mpl needs conversion +mlab John/Mike converted +mpl N/A patches Mike converted path Mike converted -pylab no docstrings +pylab N/A pyplot converted quiver needs conversion rcsetup needs conversion Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -1,17 +1,26 @@ """ -A module for finding, managing, and using fonts across-platforms. +A module for finding, managing, and using fonts across platforms. -This module provides a single FontManager that can be shared across -backends and platforms. The findfont() function returns the best -TrueType (TTF) font file in the local or system font path that matches -the specified FontProperties. The FontManager also handles Adobe Font -Metrics (AFM) font files for use by the PostScript backend. +This module provides a single :class:`FontManager` instance that can +be shared across backends and platforms. The :func:`findfont` +function returns the best TrueType (TTF) font file in the local or +system font path that matches the specified FontProperties. The +FontManager also handles Adobe Font Metrics (AFM) font files for use +by the PostScript backend. -The design is based on the W3C Cascading Style Sheet, Level 1 (CSS1) -font specification (http://www.w3.org/TR/1998/REC-CSS2-19980512/ ). +The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) +font specification <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_. Future versions may implement the Level 2 or 2.1 specifications. +Experimental support is included for using `fontconfig +<http://www.fontconfig.org>`_ on Unix variant plaforms (Linux, OS X, +Solaris). To enable it, set the constant ``USE_FONTCONFIG`` in this +file to ``True``. Fontconfig has the advantage that it is the +standard way to look up fonts on X11 platforms, so if a font is +installed, it is much more likely to be found. +""" +""" KNOWN ISSUES - documentation @@ -24,9 +33,9 @@ - 'light' is an invalid weight value, remove it. - update_fonts not implemented - Authors : John Hunter <jdh...@ac...> Paul Barrett <Ba...@ST...> + Michael Droettboom <md...@ST...> Copyright : John Hunter (2004,2005), Paul Barrett (2004,2005) License : matplotlib license (PSF compatible) The font directory code is from ttfquery, @@ -99,12 +108,21 @@ X11FontDirectories.append(path) def get_fontext_synonyms(fontext): + """ + Return a list of file extensions extensions that are synonyms for + the given file extension *fileext*. + """ return {'ttf': ('ttf', 'otf'), + 'otf': ('ttf', 'otf'), 'afm': ('afm',)}[fontext] def win32FontDirectory(): - """Return the user-specified font directory for Win32.""" + """Return the user-specified font directory for Win32. This is + looked up from the registry key:: + \\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Fonts + + If the key is not found, $WINDIR/Fonts will be returned.""" try: import _winreg except ImportError: @@ -126,8 +144,9 @@ def win32InstalledFonts(directory=None, fontext='ttf'): """ Search for fonts in the specified font directory, or use the - system directories if none given. A list of TrueType fonts are - returned by default with AFM fonts as an option. + system directories if none given. A list of TrueType font + filenames are returned by default, or AFM fonts if *fontext* == + 'afm'. """ import _winreg @@ -168,11 +187,16 @@ return None def OSXFontDirectory(): - """Return the system font directories for OS X.""" - + """ + Return the system font directories for OS X. This is done by + starting at the list of hardcoded paths in + :attr:`OSXFontDirectories` and returning all nested directories + within them. + """ fontpaths = [] def add(arg,directory,files): fontpaths.append(directory) + for fontdir in OSXFontDirectories: try: if os.path.isdir(fontdir): @@ -182,7 +206,9 @@ return fontpaths def OSXInstalledFonts(directory=None, fontext='ttf'): - """Get list of font files on OS X - ignores font suffix by default""" + """ + Get list of font files on OS X - ignores font suffix by default. + """ if directory is None: directory = OSXFontDirectory() @@ -200,7 +226,12 @@ def x11FontDirectory(): - """Return the system font directories for X11.""" + """ + Return the system font directories for X11. This is done by + starting at the list of hardcoded paths in + :attr:`X11FontDirectories` and returning all nested directories + within them. + """ fontpaths = [] def add(arg,directory,files): fontpaths.append(directory) @@ -213,10 +244,14 @@ pass return fontpaths + def get_fontconfig_fonts(fontext='ttf'): - """Grab a list of all the fonts that are being tracked by fontconfig. - This is an easy way to grab all of the fonts the user wants to be made - available to applications, without knowing where all of them reside.""" + """ + Grab a list of all the fonts that are being tracked by fontconfig + by making a system call to ``fc-list``. This is an easy way to + grab all of the fonts the user wants to be made available to + applications, without needing knowing where all of them reside. + """ try: import commands except ImportError: @@ -299,9 +334,8 @@ class FontKey(object): """ A class for storing Font properties. It is used when populating - the font dictionary. + the font lookup dictionary. """ - def __init__(self, name ='', style ='normal', @@ -323,8 +357,10 @@ def ttfFontProperty(font): """ - A function for populating the FontKey by extracting information - from the TrueType font file. + A function for populating the :class:`FontKey` by extracting + information from the TrueType font file. + + *font* is an :class:`FT2Font` instance. """ name = font.family_name @@ -416,8 +452,10 @@ def afmFontProperty(font): """ - A function for populating the FontKey by extracting information - from the AFM font file. + A function for populating a :class:`FontKey` instance by + extracting information from the AFM font file. + + *font* is a class:`AFM` instance. """ name = font.get_familyname() @@ -475,8 +513,8 @@ def add_filename(fontdict, prop, fname): """ A function to add a font file name to the font dictionary using - the FontKey properties. If a font property has no dictionary, then - create it. + the :class:`FontKey` properties. If a font property has no + dictionary, then create it. """ try: size = str(float(prop.size)) @@ -494,9 +532,9 @@ def createFontDict(fontfiles, fontext='ttf'): """ - A function to create a dictionary of font file paths. The - default is to create a dictionary for TrueType fonts. An AFM font - dictionary can optionally be created. + A function to create a font lookup dictionary. The default is to + create a dictionary for TrueType fonts. An AFM font dictionary + can optionally be created. """ fontdict = {} @@ -589,42 +627,55 @@ """ A class for storing and manipulating font properties. - The font properties are those described in the W3C Cascading Style - Sheet, Level 1 (CSS1; http://www.w3.org/TR/1998/REC-CSS2-19980512/) - font specification. The six properties are: + The font properties are those described in the `W3C Cascading + Style Sheet, Level 1 + <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ font + specification. The six properties are: - family - A list of font names in decreasing order of priority. - The last item is the default font name and is given the - name of the font family, either serif, sans-serif, - cursive, fantasy, and monospace. - style - Either normal, italic or oblique. - variant - Either normal or small-caps. - stretch - Either an absolute value of ultra-condensed, extra- - condensed, condensed, semi-condensed, normal, semi- - expanded, expanded, extra-expanded or ultra-expanded; - or a relative value of narrower or wider. - This property is currently not implemented and is set to - normal. - weight - A numeric value in the range 100, 200, 300, ..., 900. - size - Either an absolute value of xx-small, x-small, small, - medium, large, x-large, xx-large; or a relative value - of smaller or larger; or an absolute font size, e.g. 12; - or scalable. + - family: A list of font names in decreasing order of priority. + The last item is the default font name and is given the name + of the font family, either 'serif', 'sans-serif', 'cursive', + 'fantasy', or 'monospace'. - The default font property for TrueType fonts is: sans-serif, normal, - normal, normal, 400, scalable. + - style: Either 'normal', 'italic' or 'oblique'. - The preferred usage of font sizes is to use the relative values, e.g. - large, instead of absolute font sizes, e.g. 12. This approach allows - all text sizes to be made larger or smaller based on the font manager's - default font size, i.e. by using the set_default_size() method of the - font manager. + - variant: Either 'normal' or 'small-caps'. - This class will also accept a fontconfig pattern, if it is the only - argument provided. fontconfig patterns are described here: + - stretch: Either an absolute value of 'ultra-condensed', + 'extra- condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded' or + 'ultra-expanded'; or a relative value of narrower or wider. + This property is currently not implemented and is set to + normal. - http://www.fontconfig.org/fontconfig-user.html + - weight: A numeric value in the range 100, 200, 300, ..., 900. + - size: Either an absolute value of 'xx-small', 'x-small', 'small', + 'medium', 'large', 'x-large', 'xx-large'; or a relative value + of smaller or larger; or an absolute font size, e.g. 12; + or 'scalable'. + + The default font property for TrueType fonts is:: + + sans-serif, normal, normal, normal, 400, scalable. + + Alternatively, a font may be specified using an absolute path to a + .ttf file, by using the *fname* kwarg. + + The preferred usage of font sizes is to use the relative values, + e.g. 'large', instead of absolute font sizes, e.g. 12. This + approach allows all text sizes to be made larger or smaller based + on the font manager's default font size, i.e. by using the + :meth:`FontManager.set_default_size` method. + + This class will also accept a `fontconfig + <http://www.fontconfig.org/>`_ pattern, if it is the only argument + provided. See the documentation on `fontconfig patterns + <http://www.fontconfig.org/fontconfig-user.html>`_. This support + does not require fontconfig to be installed or support for it to + be enabled. We are merely borrowing its pattern syntax for use + here. + Note that matplotlib's internal font manager and fontconfig use a different algorithm to lookup fonts, so the results of the same pattern may be different in matplotlib than in other applications that use @@ -684,8 +735,9 @@ return self.get_fontconfig_pattern() def get_family(self): - """Return a list of font names that comprise the font family. """ + Return a list of font names that comprise the font family. + """ if self._family is None: family = rcParams['font.family'] if is_string_like(family): @@ -694,25 +746,33 @@ return self._family def get_name(self): - """Return the name of the font that best matches the font properties.""" + """ + Return the name of the font that best matches the font + properties. + """ return ft2font.FT2Font(str(findfont(self))).family_name def get_style(self): - """Return the font style. Values are: normal, italic or oblique.""" + """ + Return the font style. Values are: 'normal', 'italic' or + 'oblique'. + """ if self._slant is None: return rcParams['font.style'] return self._slant def get_variant(self): - """Return the font variant. Values are: normal or small-caps.""" + """ + Return the font variant. Values are: 'normal' or + 'small-caps'. + """ if self._variant is None: return rcParams['font.variant'] return self._variant def get_weight(self): """ - Return the font weight. See the FontProperties class for a - a list of possible values. + Return the font weight. """ if self._weight is None: return rcParams['font.weight'] @@ -720,30 +780,46 @@ def get_stretch(self): """ - Return the font stretch or width. Options are: normal, - narrow, condensed, or wide. + Return the font stretch or width. Options are: 'normal', + 'narrow', 'condensed', or 'wide'. """ if self._stretch is None: return rcParams['font.stretch'] return self._stretch def get_size(self): - """Return the font size.""" + """ + Return the font size. + """ if self._size is None: return rcParams['font.size'] return float(self._size) def get_file(self): + """ + Return the filename of the associated font. + """ return self._file def get_fontconfig_pattern(self): + """ + Get a fontconfig pattern suitable for looking up the font as + specified with fontconfig's ``fc-match`` utility. + + See the documentation on `fontconfig patterns + <http://www.fontconfig.org/fontconfig-user.html>`_. + + This support does not require fontconfig to be installed or + support for it to be enabled. We are merely borrowing its + pattern syntax for use here. + """ return generate_fontconfig_pattern(self) def set_family(self, family): """ Change the font family. May be either an alias (generic name - is CSS parlance), such as: serif, sans-serif, cursive, - fantasy, or monospace, or a real font name. + is CSS parlance), such as: 'serif', 'sans-serif', 'cursive', + 'fantasy', or 'monospace', or a real font name. """ if family is None: self._family = None @@ -754,22 +830,26 @@ set_name = set_family def set_style(self, style): - """Set the font style. Values are: normal, italic or oblique.""" + """ + Set the font style. Values are: 'normal', 'italic' or + 'oblique'. + """ if style not in ('normal', 'italic', 'oblique', None): raise ValueError("style must be normal, italic or oblique") self._slant = style set_slant = set_style def set_variant(self, variant): - """Set the font variant. Values are: normal or small-caps.""" + """ + Set the font variant. Values are: 'normal' or 'small-caps'. + """ if variant not in ('normal', 'small-caps', None): raise ValueError("variant must be normal or small-caps") self._variant = variant def set_weight(self, weight): """ - Set the font weight. See the FontProperties class for a - a list of possible values. + Set the font weight. """ if (weight is not None and weight not in weight_dict and @@ -779,15 +859,20 @@ def set_stretch(self, stretch): """ - Set the font stretch or width. Options are: normal, narrow, - condensed, or wide. + Set the font stretch or width. Options are: 'normal', 'narrow', + 'condensed', or 'wide'. """ if stretch not in ('normal', 'narrow', 'condensed', 'wide', None): raise ValueError("stretch is invalid") self._stretch = stretch def set_size(self, size): - """Set the font size.""" + """ + Set the font size. Either an absolute value of 'xx-small', + 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'; + or a relative value of smaller or larger; or an absolute font + size, e.g. 12; or 'scalable'. + """ if size is None: self._size = None else: @@ -803,13 +888,26 @@ size = parent_size assert(type(size) in (int, float)) self._size = size + get_size_in_points = get_size def set_file(self, file): + """ + Set the filename of the fontfile to use. In this case, all + other properties will be ignored. + """ self._file = file - get_size_in_points = get_size + def set_fontconfig_pattern(self, pattern): + """ + Set the properties by parsing a fontconfig *pattern*. - def set_fontconfig_pattern(self, pattern): + See the documentation on `fontconfig patterns + <http://www.fontconfig.org/fontconfig-user.html>`_. + + This support does not require fontconfig to be installed or + support for it to be enabled. We are merely borrowing its + pattern syntax for use here. + """ for key, val in self._parse_fontconfig_pattern(pattern).items(): if type(val) == list: getattr(self, "set_" + key)(val[0]) @@ -821,7 +919,9 @@ return FontProperties(_init = self) def ttfdict_to_fnames(d): - 'flatten a ttfdict to all the filenames it contains' + """ + flatten a ttfdict to all the filenames it contains + """ fnames = [] for named in d.values(): for styled in named.values(): @@ -833,8 +933,10 @@ return fnames def pickle_dump(data, filename): - """Equivalent to pickle.dump(data, open(filename, 'w')) - but closes the file to prevent filehandle leakage.""" + """ + Equivalent to pickle.dump(data, open(filename, 'w')) + but closes the file to prevent filehandle leakage. + """ fh = open(filename, 'w') try: pickle.dump(data, fh) @@ -842,8 +944,10 @@ fh.close() def pickle_load(filename): - """Equivalent to pickle.load(open(filename, 'r')) - but closes the file to prevent filehandle leakage.""" + """ + Equivalent to pickle.load(open(filename, 'r')) + but closes the file to prevent filehandle leakage. + """ fh = open(filename, 'r') try: data = pickle.load(fh) @@ -853,15 +957,19 @@ class FontManager: """ - On import, the FontManager creates a dictionary of TrueType - fonts based on the font properties: name, style, variant, weight, - stretch, and size. The findfont() method searches this dictionary - for a font file name that exactly matches the font properties of the - specified text. If none is found, a default font is returned. By - updating the dictionary with the properties of the found font, the - font dictionary can act like a font cache. + On import, the :class:`FontManager` singleton instance creates a + dictionary of TrueType fonts based on the font properties: name, + style, variant, weight, stretch, and size. The :meth:`findfont` + method searches this dictionary for a font file name that exactly + matches the font properties of the specified text. If none is + found, a default font is returned. By updating the dictionary + with the properties of the found font, the font dictionary can act + like a font cache. + + .. note:: The font lookup mechanism is very exact and brittle. If + the *exact* font specified is not found, a default font is + always returned. This should be improved in the future. """ - def __init__(self, size=None, weight='normal'): self.__default_weight = weight self.default_size = size @@ -870,7 +978,6 @@ os.path.join(rcParams['datapath'],'fonts','afm')] # Create list of font paths - for pathname in ['TTFPATH', 'AFMPATH']: if pathname in os.environ: ttfpath = os.environ[pathname] @@ -913,21 +1020,30 @@ self.afmdict = createFontDict(self.afmfiles, fontext='afm') def get_default_weight(self): - "Return the default font weight." + """ + Return the default font weight. + """ return self.__default_weight def get_default_size(self): - "Return the default font size." + """ + Return the default font size. + """ if self.default_size is None: return rcParams['font.size'] return self.default_size def set_default_weight(self, weight): - "Set the default font weight. The initial value is 'normal'." + """ + Set the default font weight. The initial value is 'normal'. + """ self.__default_weight = weight def set_default_size(self, size): - "Set the default font size in points. The initial value is set by font.size in rc." + """ + Set the default font size in points. The initial value is set + by ``font.size`` in rc. + """ self.default_size = size def update_fonts(self, filenames): @@ -941,16 +1057,17 @@ def findfont(self, prop, fontext='ttf'): """ Search the font dictionary for a font that exactly or closely - matches the specified font properties. See the FontProperties class - for a description. + matches the specified font properties. See the + :class:`FontProperties` class for a description. - The properties are searched in the following order: name, style, - variant, weight, stretch, and size. The font weight always matches - returning the closest weight, and the font size always matches for - scalable fonts. An oblique style font will be used inplace of a - missing italic style font if present. See the W3C Cascading Style - Sheet, Level 1 (CSS1; http://www.w3.org/TR/1998/REC-CSS2-19980512/) - documentation for a description of the font finding algorithm. + The properties are searched in the following order: name, + style, variant, weight, stretch, and size. The font weight + always matches returning the closest weight, and the font size + always matches for scalable fonts. An oblique style font will + be used inplace of a missing italic style font if present. + See the `W3C Cascading Style Sheet, Level 1 + <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ documentation + for a description of the font finding algorithm. """ debug = False if is_string_like(prop): @@ -1067,7 +1184,8 @@ def is_opentype_cff_font(filename): """ Returns True if the given font is a Postscript Compact Font Format - Font embedded in an OpenType wrapper. + Font embedded in an OpenType wrapper. Used by the PostScript and + PDF backends that can not subset these fonts. """ if os.path.splitext(filename)[1].lower() == '.otf': result = _is_opentype_cff_font_cache.get(filename) @@ -1080,7 +1198,7 @@ return result return False - +# The experimental fontconfig-based backend. if USE_FONTCONFIG and sys.platform != 'win32': import re Modified: trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py =================================================================== --- trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -1,22 +1,24 @@ """ -A module for parsing a fontconfig pattern. +A module for parsing and generating fontconfig patterns. -This class is defined here because it must be available in: - - The old-style config framework (rcsetup.py) - - The traits-based config framework (mpltraits.py) - - The font manager (font_manager.py) +See the `fontconfig pattern specification +<http://www.fontconfig.org/fontconfig-user.html>`_ for more +information. +""" -It probably logically belongs in font_manager.py, but -placing it in any of these places would have created cyclical -dependency problems, or an undesired dependency on traits even -when the traits-based config framework is not used. +# Author : Michael Droettboom <md...@st...> +# License : matplotlib license (PSF compatible) -See here for a rough specification of these patterns: -http://www.fontconfig.org/fontconfig-user.html +# This class is defined here because it must be available in: +# - The old-style config framework (:file:`rcsetup.py`) +# - The traits-based config framework (:file:`mpltraits.py`) +# - The font manager (:file:`font_manager.py`) -Author : Michael Droettboom <md...@st...> -License : matplotlib license (PSF compatible) -""" +# It probably logically belongs in :file:`font_manager.py`, but +# placing it in any of these places would have created cyclical +# dependency problems, or an undesired dependency on traits even +# when the traits-based config framework is not used. + import re from matplotlib.pyparsing import Literal, ZeroOrMore, \ Optional, Regex, StringEnd, ParseException, Suppress @@ -32,11 +34,11 @@ class FontconfigPatternParser: """A simple pyparsing-based parser for fontconfig-style patterns. - See here for a rough specification of these patterns: - http://www.fontconfig.org/fontconfig-user.html + See the `fontconfig pattern specification + <http://www.fontconfig.org/fontconfig-user.html>`_ for more + information. """ - _constants = { 'thin' : ('weight', 'light'), 'extralight' : ('weight', 'light'), @@ -113,6 +115,11 @@ self.ParseException = ParseException def parse(self, pattern): + """ + Parse the given fontconfig *pattern* and return a dictionary + of key/value pairs useful for initializing a + :class:`font_manager.FontProperties` object. + """ props = self._properties = {} try: self._parser.parseString(pattern) @@ -156,8 +163,10 @@ parse_fontconfig_pattern = FontconfigPatternParser().parse def generate_fontconfig_pattern(d): - """Given a dictionary of key/value pairs, generates a fontconfig pattern - string.""" + """ + Given a dictionary of key/value pairs, generates a fontconfig + pattern string. + """ props = [] families = '' size = '' Modified: trunk/matplotlib/lib/matplotlib/lines.py =================================================================== --- trunk/matplotlib/lib/matplotlib/lines.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/lines.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -1,6 +1,6 @@ """ This module contains all the 2D line class which can draw with a -variety of line styles, markers and colors +variety of line styles, markers and colors. """ # TODO: expose cap and join style attrs @@ -32,10 +32,12 @@ return _unmasked_index_ranges(mask, compressed=compressed) -def segment_hits(cx,cy,x,y,radius): - """Determine if any line segments are within radius of a point. Returns - the list of line segments that are within that radius. +def segment_hits(cx, cy, x, y, radius): """ + Determine if any line segments are within radius of a + point. Returns the list of line segments that are within that + radius. + """ # Process single points specially if len(x) < 2: res, = np.nonzero( (cx - x)**2 + (cy - y)**2 <= radius**2 ) @@ -173,7 +175,8 @@ Create a :class:`~matplotlib.lines.Line2D` instance with *x* and *y* data in sequences *xdata*, *ydata*. - The kwargs are Line2D properties: + The kwargs are :class:`~matplotlib.lines.Line2D` properties: + %(Line2D)s """ Artist.__init__(self) @@ -239,8 +242,9 @@ Test whether the mouse event occurred on the line. The pick radius determines the precision of the location test (usually within five points of the value). Use - :meth:`~matplotlib.lines.Line2D.get_pickradius`/:meth:`~matplotlib.lines.Line2D.set_pickradius` - to view or modify it. + :meth:`~matplotlib.lines.Line2D.get_pickradius` or + :meth:`~matplotlib.lines.Line2D.set_pickradius` to view or + modify it. Returns *True* if any values are within the radius along with ``{'ind': pointlist}``, where *pointlist* is the set of points Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -265,9 +265,9 @@ vectors see numpy.blackman, numpy.hamming, numpy.bartlett, scipy.signal, scipy.signal.get_window etc. - If length x < NFFT, it will be zero padded to NFFT + If len(*x*) < *NFFT*, it will be zero padded to *NFFT*. - Returns the tuple (*Pxx*, *freqs*) + Returns the tuple (*Pxx*, *freqs*). Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) @@ -318,26 +318,26 @@ window=window_hanning, noverlap=0): """ The cross spectral density Pxy by Welches average periodogram - method. The vectors x and y are divided into NFFT length - segments. Each segment is detrended by function detrend and - windowed by function window. noverlap gives the length of the - overlap between segments. The product of the direct FFTs of x and - y are averaged over each segment to compute Pxy, with a scaling to - correct for power loss due to windowing. Fs is the sampling - frequency. + method. The vectors *x* and *y* are divided into *NFFT* length + segments. Each segment is detrended by function *detrend* and + windowed by function *window*. *noverlap* gives the length of the + overlap between segments. The product of the direct FFTs of *x* + and *y* are averaged over each segment to compute *Pxy*, with a + scaling to correct for power loss due to windowing. *Fs* is the + sampling frequency. - NFFT must be even; a power of 2 is most efficient + *NFFT* must be even; a power of 2 is most efficient - window can be a function or a vector of length NFFT. To create - window vectors see numpy.blackman, numpy.hamming, numpy.bartlett, - scipy.signal, scipy.signal.get_window etc. + *window* can be a function or a vector of length *NFFT*. To create + window vectors see :func:`numpy.blackman`, :func:`numpy.hamming`, + :func:`numpy.bartlett`, :func:`scipy.signal`, + :func:`scipy.signal.get_window` etc. - Returns the tuple Pxy, freqs + Returns the tuple (*Pxy*, *freqs*) Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) - """ if NFFT % 2: @@ -393,14 +393,15 @@ def specgram(x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=128): """ - Compute a spectrogram of data in x. Data are split into NFFT + Compute a spectrogram of data in *x*. Data are split into *NFFT* length segements and the PSD of each section is computed. The - windowing function window is applied to each segment, and the - amount of overlap of each segment is specified with noverlap. + windowing function *window* is applied to each segment, and the + amount of overlap of each segment is specified with *noverlap*. - window can be a function or a vector of length NFFT. To create - window vectors see numpy.blackman, numpy.hamming, numpy.bartlett, - scipy.signal, scipy.signal.get_window etc. + *window* can be a function or a vector of length *NFFT*. To create + window vectors see :func:`numpy.blackman`, :func:`numpy.hamming`, + :func:`numpy.bartlett`, :func:`scipy.signal`, + :func:`scipy.signal.get_window` etc. If *x* is real (i.e. non-complex) only the positive spectrum is given. If *x* is complex then the complete spectrum is given. @@ -474,13 +475,13 @@ -_coh_error = """Coherence is calculated by averaging over NFFT -length segments. Your signal is too short for your choice of NFFT. +_coh_error = """Coherence is calculated by averaging over *NFFT* +length segments. Your signal is too short for your choice of *NFFT*. """ def cohere(x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0): """ - The coherence between x and y. Coherence is the normalized + The coherence between *x* and *y*. Coherence is the normalized cross spectral density: .. math:: @@ -510,17 +511,20 @@ def corrcoef(*args): """ - corrcoef(X) where X is a matrix returns a matrix of correlation - coefficients for the columns of X. + corrcoef(*X*) where *X* is a matrix returns a matrix of correlation + coefficients for the columns of *X* - corrcoef(x,y) where x and y are vectors returns the matrix of - correlation coefficients for x and y. + corrcoef(*x*, *y*) where *x* and *y* are vectors returns the matrix of + correlation coefficients for *x* and *y*. - Numpy arrays can be real or complex + Numpy arrays can be real or complex. - The correlation matrix is defined from the covariance matrix C as + The correlation matrix is defined from the covariance matrix *C* + as - r(i,j) = C[i,j] / sqrt(C[i,i]*C[j,j]) + .. math:: + + r_{ij} = \\frac{C_{ij}}{\\sqrt{C_{ii}C_{jj}}} """ warnings.warn("Use numpy.corrcoef", DeprecationWarning) kw = dict(rowvar=False) @@ -528,11 +532,12 @@ def polyfit(*args, **kwargs): - """ - def polyfit(x,y,N) + u""" + polyfit(*x*, *y*, *N*) - Do a best fit polynomial of order N of y to x. Return value is a - vector of polynomial coefficients [pk ... p1 p0]. Eg, for N=2 + Do a best fit polynomial of order *N* of *y* to *x*. Return value + is a vector of polynomial coefficients [pk ... p1 p0]. Eg, for + *N*=2:: p2*x0^2 + p1*x0 + p0 = y1 p2*x1^2 + p1*x1 + p0 = y1 @@ -541,28 +546,32 @@ p2*xk^2 + p1*xk + p0 = yk - Method: if X is a the Vandermonde Matrix computed from x (see - `vandermonds <http://mathworld.wolfram.com/VandermondeMatrix.html>`_), then the - polynomial least squares solution is given by the 'p' in + Method: if *X* is a the Vandermonde Matrix computed from *x* (see + `vandermonds + <http://mathworld.wolfram.com/VandermondeMatrix.html>`_), then the + polynomial least squares solution is given by the '*p*' in X*p = y - where X is a len(x) x N+1 matrix, p is a N+1 length vector, and y - is a len(x) x 1 vector + where *X* is a (len(*x*) \N{MULTIPLICATION SIGN} *N* + 1) matrix, + *p* is a *N*+1 length vector, and *y* is a (len(*x*) + \N{MULTIPLICATION SIGN} 1) vector. This equation can be solved as - p = (XT*X)^-1 * XT * y + .. math:: - where XT is the transpose of X and -1 denotes the inverse. - Numerically, however, this is not a good method, so we use - numpy.linalg.lstsq. + p = (X_t X)^-1 X_t y - For more info, see - `least squares fitting <http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html>`_, - but note that the k's and n's in the superscripts and subscripts - on that page. The linear algebra is correct, however. + where :math:`X_t` is the transpose of *X* and -1 denotes the + inverse. Numerically, however, this is not a good method, so we + use :func:`numpy.linalg.lstsq`. + For more info, see `least squares fitting + <http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html>`_, + but note that the *k*'s and *n*'s in the superscripts and + subscripts on that page. The linear algebra is correct, however. + .. seealso:: :func:`polyval` """ @@ -574,12 +583,12 @@ def polyval(*args, **kwargs): """ - y = polyval(p,x) + *y* = polyval(*p*, *x*) - p is a vector of polynomial coeffients and y is the polynomial - evaluated at x. + *p* is a vector of polynomial coeffients and *y* is the polynomial + evaluated at *x*. - Example code to remove a polynomial (quadratic) trend from y: + Example code to remove a polynomial (quadratic) trend from y:: p = polyfit(x, y, 2) trend = polyval(p, x) @@ -593,12 +602,11 @@ def vander(*args, **kwargs): """ - X = vander(x,N=None) + *X* = vander(*x*, *N* = *None*) - The Vandermonde matrix of vector x. The i-th column of X is the - the i-th power of x. N is the maximum power to compute; if N is - None it defaults to len(x). - + The Vandermonde matrix of vector *x*. The *i*-th column of *X* is the + the *i*-th power of *x*. *N* is the maximum power to compute; if *N* is + *None* it defaults to len(*x*). """ warnings.warn("Use numpy.vander()", DeprecationWarning) return np.vander(*args, **kwargs) @@ -613,61 +621,63 @@ progressCallback=donothing_callback, returnPxx=False): - """ - Cxy, Phase, freqs = cohere_pairs( X, ij, ...) + u""" + Cxy, Phase, freqs = cohere_pairs(X, ij, ...) - Compute the coherence for all pairs in ij. X is a - numSamples,numCols numpy array. ij is a list of tuples (i,j). - Each tuple is a pair of indexes into the columns of X for which - you want to compute coherence. For example, if X has 64 columns, - and you want to compute all nonredundant pairs, define ij as + Compute the coherence for all pairs in *ij*. *X* is a + (*numSamples*, *numCols*) numpy array. *ij* is a list of tuples + (*i*, *j*). Each tuple is a pair of indexes into the columns of *X* + for which you want to compute coherence. For example, if *X* has 64 + columns, and you want to compute all nonredundant pairs, define *ij* + as:: ij = [] for i in range(64): for j in range(i+1,64): - ij.append( (i,j) ) + ij.append( (i, j) ) - The other function arguments, except for 'preferSpeedOverMemory' - (see below), are explained in the help string of 'psd'. + The other function arguments, except for *preferSpeedOverMemory* + (see below), are explained in the help string of :func:`psd`. - Return value is a tuple (Cxy, Phase, freqs). + Return value is a tuple (*Cxy*, *Phase*, *freqs*). - Cxy -- a dictionary of (i,j) tuples -> coherence vector for that - pair. Ie, Cxy[(i,j) = cohere(X[:,i], X[:,j]). Number of - dictionary keys is len(ij) + - *Cxy*: a dictionary of (*i*, *j*) tuples -> coherence vector for that + pair. I.e., ``Cxy[(i,j)] = cohere(X[:,i], X[:,j])``. Number of + dictionary keys is ``len(ij)``. - Phase -- a dictionary of phases of the cross spectral density at - each frequency for each pair. keys are (i,j). + - *Phase*: a dictionary of phases of the cross spectral density at + each frequency for each pair. The keys are ``(i,j)``. - freqs -- a vector of frequencies, equal in length to either the - coherence or phase vectors for any i,j key. Eg, to make a coherence - Bode plot: + - *freqs*: a vector of frequencies, equal in length to either + the coherence or phase vectors for any (*i*, *j*) key.. Eg, + to make a coherence Bode plot:: subplot(211) plot( freqs, Cxy[(12,19)]) subplot(212) plot( freqs, Phase[(12,19)]) - For a large number of pairs, cohere_pairs can be much more - efficient than just calling cohere for each pair, because it - caches most of the intensive computations. If N is the number of - pairs, this function is O(N) for most of the heavy lifting, - whereas calling cohere for each pair is O(N^2). However, because - of the caching, it is also more memory intensive, making 2 - additional complex arrays with approximately the same number of - elements as X. + For a large number of pairs, :func:`cohere_pairs` can be much more + efficient than just calling :func:`cohere` for each pair, because + it caches most of the intensive computations. If *N* is the + number of pairs, this function is O(N) for most of the heavy + lifting, whereas calling cohere for each pair is + O(N\N{SUPERSCRIPT TWO}). However, because of the caching, it is + also more memory intensive, making 2 additional complex arrays + with approximately the same number of elements as *X*. - The parameter 'preferSpeedOverMemory', if false, limits the + The parameter *preferSpeedOverMemory*, if *False*, limits the caching by only making one, rather than two, complex cache arrays. This is useful if memory becomes critical. Even when - preferSpeedOverMemory is false, cohere_pairs will still give - significant performace gains over calling cohere for each pair, - and will use subtantially less memory than if - preferSpeedOverMemory is true. In my tests with a 43000,64 array - over all nonredundant pairs, preferSpeedOverMemory=1 delivered a - 33% performace boost on a 1.7GHZ Athlon with 512MB RAM compared - with preferSpeedOverMemory=0. But both solutions were more than - 10x faster than naievly crunching all possible pairs through + *preferSpeedOverMemory* is *False*, :func:`cohere_pairs` will + still give significant performace gains over calling + :func:`cohere` for each pair, and will use subtantially less + memory than if *preferSpeedOverMemory* is *True*. In my tests + with a (43000, 64) array over all non-redundant pairs, + *preferSpeedOverMemory* = *True* delivered a 33% performace boost + on a 1.7GHZ Athlon with 512MB RAM compared with + *preferSpeedOverMemory* = *False*. But both solutions were more + than 10x faster than naievly crunching all possible pairs through cohere. .. seealso:: @@ -757,17 +767,21 @@ def entropy(y, bins): - """ - Return the entropy of the data in y + r""" + Return the entropy of the data in *y*. - \sum p_i log2(p_i) where p_i is the probability of observing y in - the ith bin of bins. bins can be a number of bins or a range of - bins; see numpy.histogram + .. math:: - Compare S with analytic calculation for a Gaussian - x = mu + sigma*randn(200000) - Sanalytic = 0.5 * ( 1.0 + log(2*pi*sigma**2.0) ) + \sum p_i \log_2(p_i) + where :math:`p_i` is the probability of observing *y* in the + :math:`i^{th}` bin of *bins*. *bins* can be a number of bins or a + range of bins; see :func:`numpy.histogram`. + + Compare *S* with analytic calculation for a Gaussian:: + + x = mu + sigma * randn(200000) + Sanalytic = 0.5 * ( 1.0 + log(2*pi*sigma**2.0) ) """ n,bins = np.histogram(y, bins) n = n.astype(np.float_) @@ -783,29 +797,30 @@ def hist(y, bins=10, normed=0): """ - Return the histogram of y with bins equally sized bins. If bins - is an array, use the bins. Return value is - (n,x) where n is the count for each bin in x + Return the histogram of *y* with *bins* equally sized bins. If + bins is an array, use those bins. Return value is (*n*, *x*) + where *n* is the count for each bin in *x*. - If normed is False, return the counts in the first element of the - return tuple. If normed is True, return the probability density - n/(len(y)*dbin) + If *normed* is *False*, return the counts in the first element of + the returned tuple. If *normed* is *True*, return the probability + density :math:`\\frac{n}{(len(y)\mathrm{dbin}}`. - If y has rank>1, it will be raveled. If y is masked, only - the unmasked values will be used. + If *y* has rank > 1, it will be raveled. If *y* is masked, only the + unmasked values will be used. + Credits: the Numeric 22 documentation """ warnings.warn("Use numpy.histogram()", DeprecationWarning) return np.histogram(y, bins=bins, range=None, normed=normed) def normpdf(x, *args): - "Return the normal pdf evaluated at x; args provides mu, sigma" + "Return the normal pdf evaluated at *x*; args provides *mu*, *sigma*" mu, sigma = args return 1./(np.sqrt(2*np.pi)*sigma)*np.exp(-0.5 * (1./sigma*(x - mu))**2) def levypdf(x, gamma, alpha): - "Returm the levy pdf evaluated at x for params gamma, alpha" + "Returm the levy pdf evaluated at *x* for params *gamma*, *alpha*" N = len(x) @@ -835,7 +850,7 @@ def trapz(x, y): """ - Trapezoidal integral of y(x). + Trapezoidal integral of *y*(*x*). """ warnings.warn("Use numpy.trapz(y,x) instead of trapz(x,y)", DeprecationWarning) return np.trapz(y, x) @@ -849,10 +864,9 @@ def longest_contiguous_ones(x): """ - return the indices of the longest stretch of contiguous ones in x, - assuming x is a vector of zeros and ones. - If there are two equally long stretches, pick the first - + Return the indices of the longest stretch of contiguous ones in *x*, + assuming *x* is a vector of zeros and ones. If there are two + equally long stretches, pick the first. """ x = np.ravel(x) if len(x)==0: @@ -880,16 +894,21 @@ def prepca(P, frac=0): """ - Compute the principal components of P. P is a numVars x - numObs array. frac is the minimum fraction of - variance that a component must contain to be included. + Compute the principal components of *P*. *P* is a (*numVars*, + *numObs*) array. *frac* is the minimum fraction of variance that a + component must contain to be included. - Return value are - Pcomponents : a numVars x numObs array - Trans : the weights matrix, ie, Pcomponents = Trans*P - fracVar : the fraction of the variance accounted for by each - component returned + Return value is a tuple of the form (*Pcomponents*, *Trans*, + *fracVar*) where: + - *Pcomponents* : a (numVars, numObs) array + + - *Trans* : the weights matrix, ie, *Pcomponents* = *Trans* * + *P* + + - *fracVar* : the fraction of the variance accounted for by each + component returned + A similar function of the same name was in the Matlab (TM) R13 Neural Network Toolbox but is not found in later versions; its successor seems to be called "processpcs". @@ -907,11 +926,11 @@ def prctile(x, p = (0.0, 25.0, 50.0, 75.0, 100.0)): """ - Return the percentiles of x. p can either be a sequence of - percentile values or a scalar. If p is a sequence the i-th element - of the return sequence is the p(i)-th percentile of x. - If p is a scalar, the largest value of x less than or equal - to the p percentage point in the sequence is returned. + Return the percentiles of *x*. *p* can either be a sequence of + percentile values or a scalar. If *p* is a sequence, the ith + element of the return sequence is the *p*(i)-th percentile of *x*. + If *p* is a scalar, the largest value of *x* less than or equal to + the *p* percentage point in the sequence is returned. """ @@ -929,15 +948,15 @@ def prctile_rank(x, p): """ - return the for each element in x, return the rank 0..len(p) . Eg - if p=(25, 50, 75), the return value will be a len(x) array with - values in [0,1,2,3] where 0 indicates the value is less than the - 25th percentile, 1 indicates the value is >= the 25th and < 50th - percentile, ... and 3 indicates the value is above the 75th - percentile cutoff + Return the rank for each element in *x*, return the rank + 0..len(*p*). Eg if *p* = (25, 50, 75), the return value will be a + len(*x*) array with values in [0,1,2,3] where 0 indicates the + value is less than the 25th percentile, 1 indicates the value is + >= the 25th and < 50th percentile, ... and 3 indicates the value + is above the 75th percentile cutoff. - p is either an array of percentiles in [0..100] or a scalar which - indicates how many quantiles of data you want ranked + *p* is either an array of percentiles in [0..100] or a scalar which + indicates how many quantiles of data you want ranked. """ if not cbook.iterable(p): @@ -953,10 +972,10 @@ def center_matrix(M, dim=0): """ - Return the matrix M with each row having zero mean and unit std + Return the matrix *M* with each row having zero mean and unit std. - if dim=1 operate on columns instead of rows. (dim is opposite - to the numpy axis kwarg.) + If *dim* = 1 operate on columns instead of rows. (*dim* is + opposite to the numpy axis kwarg.) """ M = np.asarray(M, np.float_) if dim: @@ -970,7 +989,10 @@ def rk4(derivs, y0, t): """ - Integrate 1D or ND system of ODEs using 4-th order Runge-Kutta. This is a toy implementation which may be useful if you find yourself stranded on a system w/o scipy. Otherwise use ``scipy.integrate`` + Integrate 1D or ND system of ODEs using 4-th order Runge-Kutta. + This is a toy implementation which may be useful if you find + yourself stranded on a system w/o scipy. Otherwise use + :func:`scipy.integrate`. *y0* initial state vector @@ -1009,7 +1031,6 @@ If you have access to scipy, you should probably be using the scipy.integrate tools rather than this function. - """ try: Ny = len(y0) @@ -1040,9 +1061,11 @@ def bivariate_normal(X, Y, sigmax=1.0, sigmay=1.0, mux=0.0, muy=0.0, sigmaxy=0.0): """ - Bivariate gaussan distribution for equal shape X, Y + Bivariate Gaussian distribution for equal shape *X*, *Y*. - See `bivariate normal <http://mathworld.wolfram.com/BivariateNormalDistribution.html>`_ at mathworld. + See `bivariate normal + <http://mathworld.wolfram.com/BivariateNormalDistribution.html>`_ + at mathworld. """ Xmu = X-mux Ymu = Y-muy @@ -1052,21 +1075,22 @@ denom = 2*np.pi*sigmax*sigmay*np.sqrt(1-rho**2) return np.exp( -z/(2*(1-rho**2))) / denom - - - def get_xyz_where(Z, Cond): """ - Z and Cond are MxN matrices. Z are data and Cond is a boolean - matrix where some condition is satisfied. Return value is x,y,z - where x and y are the indices into Z and z are the values of Z at - those indices. x,y,z are 1D arrays + *Z* and *Cond* are *M* x *N* matrices. *Z* are data and *Cond* is + a boolean matrix where some condition is satisfied. Return value + is (*x*, *y*, *z*) where *x* and *y* are the indices into *Z* and + *z* are the values of *Z* at those indices. *x*, *y*, and *z* are + 1D arrays. """ X,Y = np.indices(Z.shape) return X[Cond], Y[Cond], Z[Cond] def get_sparse_matrix(M,N,frac=0.1): - 'return a MxN sparse matrix with frac elements randomly filled' + """ + Return a *M* x *N* sparse matrix with *frac* elements randomly + filled. + """ data = np.zeros((M,N))*0. for i in range(int(M*N*frac)): x = np.random.randint(0,M-1) @@ -1075,15 +1099,17 @@ return data def dist(x,y): - 'return the distance between two points' + """ + Return the distance between two points. + """ d = x-y return np.sqrt(np.dot(d,d)) def dist_point_to_segment(p, s0, s1): """ - get the distance of a point to a segment. + Get the distance of a point to a segment. - p, s0, s1 are xy sequences + *p*, *s0*, *s1* are *xy* sequences This algorithm from http://softsurfer.com/Archive/algorithm_0102/algorithm_0102.htm#Distance%20to%20Ray%20or%20Segment @@ -1108,12 +1134,11 @@ def segments_intersect(s1, s2): """ - Return True if s1 and s2 intersect. - s1 and s2 are defined as + Return *True* if *s1* and *s2* intersect. + *s1* and *s2* are defined as:: - s1: (x1, y1), (x2, y2) - s2: (x3, y3), (x4, y4) - + s1: (x1, y1), (x2, y2) + s2: (x3, y3), (x4, y4) """ (x1, y1), (x2, y2) = s1 (x3, y3), (x4, y4) = s2 @@ -1135,7 +1160,7 @@ def fftsurr(x, detrend=detrend_none, window=window_none): """ - Compute an FFT phase randomized surrogate of x + Compute an FFT phase randomized surrogate of *x*. """ if cbook.iterable(window): x=window*detrend(x) @@ -1168,30 +1193,36 @@ What the function here calculates may not be what you really want; *caveat emptor*. - It also seems that this function's name is badly misspelled. + It also seems that this function's name is badly misspelled. """ return np.mean(np.log(np.absolute(fprime(x)))) class FIFOBuffer: """ - A FIFO queue to hold incoming x, y data in a rotating buffer using - numpy arrays under the hood. It is assumed that you will call - asarrays much less frequently than you add data to the queue -- - otherwise another data structure will be faster + A FIFO queue to hold incoming *x*, *y* data in a rotating buffer + using numpy arrays under the hood. It is assumed that you will + call asarrays much less frequently than you add data to the queue + -- otherwise another data structure will be faster. This can be used to support plots where data is added from a real - time feed and the plot object wants grab data from the buffer and - plot it to screen less freqeuently than the incoming + time feed and the plot object wants to grab data from the buffer + and plot it to screen less freqeuently than the incoming. - If you set the dataLim attr to a matplotlib BBox (eg ax.dataLim), - the dataLim will be updated as new data come in + If you set the *dataLim* attr to + :class:`~matplotlib.transforms.BBox` (eg + :attr:`matplotlib.Axes.dataLim`), the *dataLim* will be updated as + new data come in. - TODI: add a grow method that will extend nmax + TODO: add a grow method that will extend nmax - mlab seems like the wrong place for this class. + .. note:: + + mlab seems like the wrong place for this class. """ def __init__(self, nmax): - 'buffer up to nmax points' + """ + Buffer up to *nmax* points. + """ self._xa = np.zeros((nmax,), np.float_) self._ya = np.zeros((nmax,), np.float_) self._xs = np.zeros((nmax,), np.float_) @@ -1202,11 +1233,16 @@ self.callbackd = {} def register(self, func, N): - 'call func everytime N events are passed; func signature is func(fifo)' + """ + Call *func* every time *N* events are passed; *func* signature + is ``func(fifo)``. + """ self.callbackd.setdefault(N, []).append(func) def add(self, x, y): - 'add scalar x and y to the queue' + """ + Add scalar *x* and *y* to the queue. + """ if self.dataLim is not None: xys = ((x,y),) self.dataLim.update(xys, -1) #-1 means use the default ignore setting @@ -1223,15 +1259,17 @@ self._ind += 1 def last(self): - 'get the last x, y or None, None if no data set' + """ + Get the last *x*, *y* or *None*. *None* if no data set. + """ if self._ind==0: return None, None ind = (self._ind-1) % self._nmax return self._xs[ind], self._ys[ind] def asarrays(self): """ - return x and y as arrays; their length will be the len of data - added or nmax + Return *x* and *y* as arrays; their length will be the len of + data added or *nmax*. """ if self._ind<self._nmax: return self._xs[:self._ind], self._ys[:self._ind] @@ -1245,36 +1283,41 @@ return self._xa, self._ya def update_datalim_to_current(self): - 'update the datalim in the current data in the fifo' + """ + Update the *datalim* in the current data in the fifo. + """ if self.dataLim is None: raise ValueError('You must first set the dataLim attr') x, y = self.asarrays() self.dataLim.update_numerix(x, y, True) def movavg(x,n): - 'compute the... [truncated message content] |
From: <jd...@us...> - 2008-10-27 18:05:01
|
Revision: 6337 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6337&view=rev Author: jdh2358 Date: 2008-10-27 18:04:58 +0000 (Mon, 27 Oct 2008) Log Message: ----------- fixed navigation table Modified Paths: -------------- trunk/matplotlib/doc/_templates/gallery.html trunk/matplotlib/doc/api/pyplot_api.rst trunk/matplotlib/doc/users/navigation_toolbar.rst Modified: trunk/matplotlib/doc/_templates/gallery.html =================================================================== --- trunk/matplotlib/doc/_templates/gallery.html 2008-10-26 14:33:36 UTC (rev 6336) +++ trunk/matplotlib/doc/_templates/gallery.html 2008-10-27 18:04:58 UTC (rev 6337) @@ -267,6 +267,8 @@ <a href="examples/pylab_examples/hline_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/hline_demo.png" border="0" alt="hline_demo"/></a> +<a href="examples/pylab_examples/image_clip_path.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/image_clip_path.png" border="0" alt="image_clip_path"/></a> + <a href="examples/pylab_examples/image_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/image_demo.png" border="0" alt="image_demo"/></a> <a href="examples/pylab_examples/image_demo2.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/image_demo2.png" border="0" alt="image_demo2"/></a> Modified: trunk/matplotlib/doc/api/pyplot_api.rst =================================================================== --- trunk/matplotlib/doc/api/pyplot_api.rst 2008-10-26 14:33:36 UTC (rev 6336) +++ trunk/matplotlib/doc/api/pyplot_api.rst 2008-10-27 18:04:58 UTC (rev 6337) @@ -1,8 +1,8 @@ ***************** matplotlib pyplot ***************** + - :mod:`matplotlib.pyplot` ======================== Modified: trunk/matplotlib/doc/users/navigation_toolbar.rst =================================================================== --- trunk/matplotlib/doc/users/navigation_toolbar.rst 2008-10-26 14:33:36 UTC (rev 6336) +++ trunk/matplotlib/doc/users/navigation_toolbar.rst 2008-10-27 18:04:58 UTC (rev 6337) @@ -79,9 +79,9 @@ Navigation Keyboard Shortcuts ----------------------------- -================================== ===================== +================================== ============================================== Command Keyboard Shortcut(s) -================================== ===================== +================================== ============================================== Home/Reset **h** or **r** or **home** Back **c** or **left arrow** or **backspace** Forward **v** or **right arrow** @@ -89,16 +89,12 @@ Zoom-to-rect **o** Save **s** Toggle fullscreen **f** ----------------------------------- --------------------- -**Pan/Zoom mode only** -- constrain pan/zoom to x axis hold **x** -- constrain pan/zoom to y axis hold **y** -- preserve aspect ratio hold **CONTROL** ----------------------------------- --------------------- -**In axes only** -- Toggle grid **g** -- Toggle y axis scale (log/linear) **l** -================================== ===================== +Constrain pan/zoom to x axis hold **x** +Constrain pan/zoom to y axis hold **y** +Preserve aspect ratio hold **CONTROL** +Toggle grid **g** +Toggle y axis scale (log/linear) **l** +================================== ============================================== If you are using :mod:`matplotlib.pyplot` the toolbar will be created automatically for every figure. If you are writing your own user This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-10-26 14:33:46
|
Revision: 6336 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6336&view=rev Author: jdh2358 Date: 2008-10-26 14:33:36 +0000 (Sun, 26 Oct 2008) Log Message: ----------- added glass dots skel Modified Paths: -------------- trunk/py4science/examples/glass_dots1.py trunk/py4science/examples/glass_dots2.py Added Paths: ----------- trunk/py4science/examples/glass_dots1_skel.py Modified: trunk/py4science/examples/glass_dots1.py =================================================================== --- trunk/py4science/examples/glass_dots1.py 2008-10-25 07:06:53 UTC (rev 6335) +++ trunk/py4science/examples/glass_dots1.py 2008-10-26 14:33:36 UTC (rev 6336) @@ -6,10 +6,9 @@ See L. Glass. 'Moire effect from random dots' Nature 223, 578580 (1969). """ import cmath -from numpy import cos, sin, pi, matrix -import numpy as npy +import numpy as np import numpy.linalg as linalg -from pylab import figure, show +import matplotlib.pyplot as plt def myeig(M): @@ -18,10 +17,16 @@ Solve quadratic: - lamba^2 - tau*lambda + Delta = 0 + lamba^2 - tau*lambda +/- Delta = 0 where tau = trace(M) and Delta = Determinant(M) - + + if M = | a b | + | c d | + + the trace is a+d and the determinant is a*d-b*c + + Return value is lambda1, lambda2 """ a,b = M[0,0], M[0,1] @@ -32,31 +37,29 @@ lambda1 = (tau + cmath.sqrt(tau**2 - 4*delta))/2. lambda2 = (tau - cmath.sqrt(tau**2 - 4*delta))/2. return lambda1, lambda2 - + # 2000 random x,y points in the interval[-0.5 ... 0.5] -X1 = matrix(npy.random.rand(2,2000) - )-0.5 +X1 = np.random.rand(2,2000)-0.5 -name = 'saddle' -sx, sy, angle = 1.05, 0.95, 0. +#name = 'saddle' +#sx, sy, angle = 1.05, 0.95, 0. -#name = 'center' -#sx, sy, angle = 1., 1., 2.5 +name = 'center' +sx, sy, angle = 1., 1., 2.5 #name= 'stable focus' # spiral #sx, sy, angle = 0.95, 0.95, 2.5 -theta = angle * pi/180. +theta = angle * cmath.pi/180. +S = np.array([[sx, 0], + [0, sy]]) -S = matrix([[sx, 0], - [0, sy]]) +R = np.array([[np.cos(theta), -np.sin(theta)], + [np.sin(theta), np.cos(theta)],]) -R = matrix([[cos(theta), -sin(theta)], - [sin(theta), cos(theta)],]) +M = np.dot(S, R) # rotate then stretch -M = S*R # rotate then stretch - # compute the eigenvalues using numpy linear algebra vals, vecs = linalg.eig(M) print 'numpy eigenvalues', vals @@ -66,17 +69,17 @@ print 'analytic eigenvalues', avals # transform X1 by the matrix -X2 = M*X1 +X2 = np.dot(M, X1) # plot the original x,y as green dots and the transformed x, y as red # dots -fig = figure() +fig = plt.figure() ax = fig.add_subplot(111) -x1 = X1[0].flat -y1 = X1[1].flat -x2 = X2[0].flat -y2 = X2[1].flat +x1 = X1[0] +y1 = X1[1] +x2 = X2[0] +y2 = X2[1] ax = fig.add_subplot(111) line1, line2 = ax.plot(x1, y1, 'go', x2, y2, 'ro', markersize=2) @@ -85,4 +88,4 @@ fig.savefig('glass_dots1.png', dpi=100) fig.savefig('glass_dots1.eps', dpi=100) -show() +plt.show() Added: trunk/py4science/examples/glass_dots1_skel.py =================================================================== --- trunk/py4science/examples/glass_dots1_skel.py (rev 0) +++ trunk/py4science/examples/glass_dots1_skel.py 2008-10-26 14:33:36 UTC (rev 6336) @@ -0,0 +1,80 @@ +""" +Moire patterns from random dot fields + +http://en.wikipedia.org/wiki/Moir%C3%A9_pattern + +See L. Glass. 'Moire effect from random dots' Nature 223, 578580 (1969). +""" +import cmath +import numpy as np +import numpy.linalg as linalg +import matplotlib.pyplot as plt + +# search for TODO to find the places you need to fix +TODO = None + +def myeig(M): + """ + compute eigen values and eigenvectors analytically + + Solve quadratic: + + lamba^2 - tau*lambda +/- Delta = 0 + + where tau = trace(M) and Delta = Determinant(M) + + if M = | a b | + | c d | + + the trace is a+d and the determinant is a*d-b*c + + Return value is lambda1, lambda2 + + Return value is lambda1, lambda2 + """ + # TODO : compute the real values here. 0, 1 is just a place holder + return 0, 1 + +# Generate and shape (2000,2) random x,y points in the +# interval [-0.5 ... 0.5] +X1 = TODO + +# these are the scaling factor sx, the scaling factor sy, and the +# rotation angle 0 +name = 'saddle' +sx, sy, angle = 1.05, 0.95, 0. + +# OPTIONAL: try and find other sx, sy, theta for other kinds of nodes: +# stable node, unstable node, center, spiral, saddle + +# convert theta to radians: theta = angle * pi/180 +theta = TODO + +# Create a 2D scaling array +# S = | sx 0 | +# | 0 sy | +S = TODO + +# create a 2D rotation array +# R = | cos(theta) -sin(theta) | +# | sin(theta) cos(theta) | +R = TODO + +# do a matrix multiply M = S x R where x is *matrix* multiplication +M = TODO + +# compute the eigenvalues using numpy linear algebra +vals, vecs = linalg.eig(M) +print 'numpy eigenvalues', vals + +# compare with the analytic values from myeig +avals = myeig(M) +print 'analytic eigenvalues', avals + +# transform X1 by the matrix +# X2 = M x X1 where x is *matrix* multiplication +X2 = np.dot(M, X1) + +# plot the original x,y as green dots and the transformed x, y as red +# dots +TODO Modified: trunk/py4science/examples/glass_dots2.py =================================================================== --- trunk/py4science/examples/glass_dots2.py 2008-10-25 07:06:53 UTC (rev 6335) +++ trunk/py4science/examples/glass_dots2.py 2008-10-26 14:33:36 UTC (rev 6336) @@ -1,4 +1,4 @@ -import numpy as npy +import numpy as np import numpy.linalg as linalg from pylab import figure, show @@ -12,15 +12,15 @@ self.axes = axes self.canvas = axes.figure.canvas self.canvas.mpl_connect('button_press_event', self.press) - self.canvas.mpl_connect('button_release_event', self.release) - self.canvas.mpl_connect('motion_notify_event', self.move) + self.canvas.mpl_connect('button_release_event', self.release) + self.canvas.mpl_connect('motion_notify_event', self.move) - X1 = self.X1 = npy.matrix(npy.random.rand(2,2000))-0.5 + X1 = self.X1 = np.random.rand(2,2000)-0.5 - x1 = X1[0].flat - y1 = X1[1].flat - x2 = X1[0].flat # no transformation yet - y2 = X1[1].flat + x1 = X1[0] + y1 = X1[1] + x2 = X1[0] # no transformation yet + y2 = X1[1] self.line1, self.line2 = ax.plot(x1, y1,'go', x2, y2, 'ro', markersize=2) self.xlast = None self.ylast = None @@ -37,37 +37,37 @@ self.xlast = None self.ylast = None self.draw() - + def draw(self): sx, sy, theta = self.sx, self.sy, self.theta - a = sx*npy.cos(theta) - b = -sx*npy.sin(theta) - c = sy*npy.sin(theta) - d = sy*npy.cos(theta) - M = npy.matrix([[a,b],[c,d]]) + a = sx*np.cos(theta) + b = -sx*np.sin(theta) + c = sy*np.sin(theta) + d = sy*np.cos(theta) + M = np.array([[a,b],[c,d]]) - X2 = M*self.X1 - x2 = X2[0].flat - y2 = X2[1].flat + X2 = np.dot(M, self.X1) + x2 = X2[0] + y2 = X2[1] self.line2.set_data(x2,y2) self.canvas.draw() - + def move(self, event): if not event.inaxes: return # not over axes if self.xlast is None: return # no initial data if not event.button: return # no button press - + dx = event.xdata - self.xlast - dy = event.ydata - self.ylast + dy = event.ydata - self.ylast if event.button==1: self.theta += dx elif event.button==3: self.sx += dx self.sy += dy - + self.title.set_text('sx=%1.2f, sy=%1.2f, theta=%1.2f'%(self.sx, self.sy, self.theta)) self.draw() self.xlast = event.xdata @@ -75,9 +75,9 @@ - -from pylab import subplot, show -fig = figure() + +import matplotlib.pyplot as plt +fig = plt.figure() ax = fig.add_subplot(111) t = Transformer(ax) -show() +plt.show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |