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
(7) |
2
(14) |
3
(6) |
4
(8) |
5
(6) |
6
(3) |
7
(2) |
8
(22) |
9
(9) |
10
(23) |
11
(14) |
12
(22) |
13
(7) |
14
(3) |
15
(22) |
16
(13) |
17
(18) |
18
(21) |
19
(9) |
20
|
21
(3) |
22
(6) |
23
(5) |
24
|
25
|
26
(3) |
27
|
28
(1) |
29
(11) |
30
(1) |
31
(12) |
|
|
|
From: <js...@us...> - 2008-12-18 20:20:21
|
Revision: 6678 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6678&view=rev Author: jswhit Date: 2008-12-18 20:20:18 +0000 (Thu, 18 Dec 2008) Log Message: ----------- check for already installed pyshapelib Modified Paths: -------------- trunk/toolkits/basemap/Changelog trunk/toolkits/basemap/README trunk/toolkits/basemap/setup.cfg trunk/toolkits/basemap/setup.py Modified: trunk/toolkits/basemap/Changelog =================================================================== --- trunk/toolkits/basemap/Changelog 2008-12-18 19:40:26 UTC (rev 6677) +++ trunk/toolkits/basemap/Changelog 2008-12-18 20:20:18 UTC (rev 6678) @@ -1,4 +1,6 @@ version 0.99.3 (not yet released) + * have setup.py check for already installed pyshapelib (just + like it does for httplib2 and pydap). * Basemap will now look for it's data in BASEMAPDATA. If that env var not set, it will fall back to it's default location. Modified: trunk/toolkits/basemap/README =================================================================== --- trunk/toolkits/basemap/README 2008-12-18 19:40:26 UTC (rev 6677) +++ trunk/toolkits/basemap/README 2008-12-18 20:20:18 UTC (rev 6678) @@ -100,11 +100,11 @@ by running "from mpl_toolkits.basemap import Basemap" at the python prompt. -Basemap includes two auxilliary packages, pydap (http://pydap.org, just -the client is included) and httplib2. By default, setup.py checks to +Basemap includes three auxilliary packages, pydap (http://pydap.org, just +the client is included), httplib2 and pyshapelib. By default, setup.py checks to see if these are already installed, and if so does not try to overwrite them. If you get import errors related to either of these two packages, -edit setup.cfg and set pydap and/or httplib2 to True to force +edit setup.cfg and set pydap, httplib2 and/or pyshapelib to True to force installation of the included versions. 4) To test, cd to the examples directory and run 'python simpletest.py'. Modified: trunk/toolkits/basemap/setup.cfg =================================================================== --- trunk/toolkits/basemap/setup.cfg 2008-12-18 19:40:26 UTC (rev 6677) +++ trunk/toolkits/basemap/setup.cfg 2008-12-18 20:20:18 UTC (rev 6678) @@ -2,9 +2,10 @@ # By default, basemap checks for a few dependencies and # installs them if missing. This feature can be turned off # by uncommenting the following lines. Acceptible values are: -# True: install, overwrite an existing installation +# auto: install, overwrite an existing installation # False: do not install # auto: install only if the package is unavailable. This # is the default behavior pydap = auto httplib2 = auto +pyshapelib = auto Modified: trunk/toolkits/basemap/setup.py =================================================================== --- trunk/toolkits/basemap/setup.py 2008-12-18 19:40:26 UTC (rev 6677) +++ trunk/toolkits/basemap/setup.py 2008-12-18 20:20:18 UTC (rev 6678) @@ -99,25 +99,6 @@ include_dirs=geos_include_dirs, libraries=['geos_c','geos'])) -# install shapelib and dbflib. -packages = packages + ['shapelib','dbflib'] -package_dirs['shapelib'] = os.path.join('lib','shapelib') -package_dirs['dbflib'] = os.path.join('lib','dbflib') -extensions = extensions + \ - [Extension("shapelibc", - ["pyshapelib/shapelib_wrap.c", - "pyshapelib/shapelib/shpopen.c", - "pyshapelib/shapelib/shptree.c"], - include_dirs = ["pyshapelib/shapelib"]), - Extension("shptree", - ["pyshapelib/shptreemodule.c"], - include_dirs = ["pyshapelib/shapelib"]), - Extension("dbflibc", - ["pyshapelib/dbflib_wrap.c", - "pyshapelib/shapelib/dbfopen.c"], - include_dirs = ["pyshapelib/shapelib"], - define_macros = dbf_macros()) ] - # check setup.cfg file to see how to install auxilliary packages. options = {} if os.path.exists("setup.cfg"): @@ -128,10 +109,14 @@ except: options['provide_pydap'] = 'auto' try: options['provide_httplib2'] = config.getboolean("provide_packages", "httplib2") except: options['provide_httplib2'] = 'auto' + try: options['provide_pyshapelib'] = config.getboolean("provide_packages", "pyshapelib") + except: options['provide_pyshapelib'] = 'auto' else: options['provide_pydap'] = 'auto' options['provide_httplib2'] = 'auto' + options['provide_pyshapelib'] = 'auto' + provide_pydap = options['provide_pydap'] if provide_pydap == 'auto': # install pydap stuff if not already available. # only the client is installed (not the server). @@ -171,16 +156,65 @@ except ImportError: print 'httplib2 not installed, will be installed' packages = packages + ['httplib2'] - package_dirs['httlib2'] = os.path.join('lib','httplib2') + package_dirs['httplib2'] = os.path.join('lib','httplib2') else: print 'httplib2 installed' elif provide_httplib2: # force install of httplib2 print 'forcing install of included httplib2' packages = packages + ['httplib2'] - package_dirs['httlib2'] = os.path.join('lib','httplib2') + package_dirs['httplib2'] = os.path.join('lib','httplib2') else: print 'will not install httplib2' +provide_pyshapelib = options['provide_pyshapelib'] +if provide_pyshapelib == 'auto': + print 'checking to see if pyshapelib installed ..' + try: + import shapelib + import dbflib + except ImportError: + print 'shapelib/dbflib not installed, will be installed' + packages = packages + ['shapelib','dbflib'] + package_dirs['shapelib'] = os.path.join('lib','shapelib') + package_dirs['dbflib'] = os.path.join('lib','dbflib') + extensions = extensions + \ + [Extension("shapelibc", + ["pyshapelib/shapelib_wrap.c", + "pyshapelib/shapelib/shpopen.c", + "pyshapelib/shapelib/shptree.c"], + include_dirs = ["pyshapelib/shapelib"]), + Extension("shptree", + ["pyshapelib/shptreemodule.c"], + include_dirs = ["pyshapelib/shapelib"]), + Extension("dbflibc", + ["pyshapelib/dbflib_wrap.c", + "pyshapelib/shapelib/dbfopen.c"], + include_dirs = ["pyshapelib/shapelib"], + define_macros = dbf_macros()) ] + else: + print 'pyshapelib installed' +elif provide_pyshapelib: # force install of shapelib + print 'forcing install of included pyshapelib' + packages = packages + ['shapelib','dbflib'] + package_dirs['shapelib'] = os.path.join('lib','shapelib') + package_dirs['dbflib'] = os.path.join('lib','dbflib') + extensions = extensions + \ + [Extension("shapelibc", + ["pyshapelib/shapelib_wrap.c", + "pyshapelib/shapelib/shpopen.c", + "pyshapelib/shapelib/shptree.c"], + include_dirs = ["pyshapelib/shapelib"]), + Extension("shptree", + ["pyshapelib/shptreemodule.c"], + include_dirs = ["pyshapelib/shapelib"]), + Extension("dbflibc", + ["pyshapelib/dbflib_wrap.c", + "pyshapelib/shapelib/dbfopen.c"], + include_dirs = ["pyshapelib/shapelib"], + define_macros = dbf_macros()) ] +else: + print 'will not install pyshapelib' + # Specify all the required mpl data pyproj_datafiles = ['data/epsg', 'data/esri', 'data/esri.extra', 'data/GL27', 'data/nad.lst', 'data/nad27', 'data/nad83', 'data/ntv2_out.dist', 'data/other.extra', 'data/pj_out27.dist', 'data/pj_out83.dist', 'data/proj_def.dat', 'data/README', 'data/td_out.dist', 'data/test27', 'data/test83', 'data/testntv2', 'data/testvarious', 'data/world','data/bmng.jpg','data/bmng_low.jpg'] boundaryfiles = [] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 19:40:36
|
Revision: 6677 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6677&view=rev Author: jdh2358 Date: 2008-12-18 19:40:26 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Merged revisions 6675-6676 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6675 | jdh2358 | 2008-12-18 11:19:26 -0800 (Thu, 18 Dec 2008) | 1 line fixed a small_docs bug when no args present ........ r6676 | jdh2358 | 2008-12-18 11:28:46 -0800 (Thu, 18 Dec 2008) | 1 line pushed out new 98.5.2 tarball with smalldocs fix ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/make.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6673 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6676 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-18 19:28:46 UTC (rev 6676) +++ trunk/matplotlib/CHANGELOG 2008-12-18 19:40:26 UTC (rev 6677) @@ -1,7 +1,8 @@ 2008-12-18 add new arrow style, a line + filled triangles. -JJL ================================================================== -2008-12-18 Released 0.98.5.2 from v0_98_5_maint at r6667 +2008-12-18 Re-Released 0.98.5.2 from v0_98_5_maint at r6675 + Released 0.98.5.2 from v0_98_5_maint at r6667 2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH Modified: trunk/matplotlib/doc/make.py =================================================================== --- trunk/matplotlib/doc/make.py 2008-12-18 19:28:46 UTC (rev 6676) +++ trunk/matplotlib/doc/make.py 2008-12-18 19:40:26 UTC (rev 6677) @@ -105,4 +105,5 @@ arg, funcd.keys())) func() else: + small_docs = False all() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 19:29:00
|
Revision: 6676 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6676&view=rev Author: jdh2358 Date: 2008-12-18 19:28:46 +0000 (Thu, 18 Dec 2008) Log Message: ----------- pushed out new 98.5.2 tarball with smalldocs fix Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-18 19:19:26 UTC (rev 6675) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 19:28:46 UTC (rev 6676) @@ -1,6 +1,9 @@ + ======================================================================= -Release 0.98.5.3 at r6667 +Re-Released 0.98.5.2 at r6675 +Release 0.98.5.2 at r6667 + 2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH 2008-12-18 Fix bug where a line with NULL data limits prevents This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 19:19:33
|
Revision: 6675 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6675&view=rev Author: jdh2358 Date: 2008-12-18 19:19:26 +0000 (Thu, 18 Dec 2008) Log Message: ----------- fixed a small_docs bug when no args present Modified Paths: -------------- branches/v0_98_5_maint/doc/make.py Modified: branches/v0_98_5_maint/doc/make.py =================================================================== --- branches/v0_98_5_maint/doc/make.py 2008-12-18 19:10:38 UTC (rev 6674) +++ branches/v0_98_5_maint/doc/make.py 2008-12-18 19:19:26 UTC (rev 6675) @@ -105,4 +105,5 @@ arg, funcd.keys())) func() else: + small_docs = False all() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-18 19:10:43
|
Revision: 6674 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6674&view=rev Author: mdboom Date: 2008-12-18 19:10:38 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Merged revisions 6672-6673 via svnmerge from https://matplotlib.svn.sf.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6672 | mdboom | 2008-12-18 14:07:08 -0500 (Thu, 18 Dec 2008) | 2 lines Fix how example files are added to the build. Saves about 1MB in html output. ........ r6673 | mdboom | 2008-12-18 14:08:45 -0500 (Thu, 18 Dec 2008) | 1 line removing examples from svn ........ Modified Paths: -------------- trunk/matplotlib/doc/conf.py trunk/matplotlib/doc/make.py trunk/matplotlib/doc/sphinxext/plot_directive.py trunk/matplotlib/lib/matplotlib/pyplot.py Added Paths: ----------- trunk/matplotlib/doc/sphinxext/gen_rst.py Removed Paths: ------------- trunk/matplotlib/doc/examples/ Property Changed: ---------------- trunk/matplotlib/ trunk/matplotlib/doc/pyplots/README trunk/matplotlib/doc/sphinxext/gen_gallery.py Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6668 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6673 Modified: svn:mergeinfo - /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662 + /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673 Modified: trunk/matplotlib/doc/conf.py =================================================================== --- trunk/matplotlib/doc/conf.py 2008-12-18 19:08:45 UTC (rev 6673) +++ trunk/matplotlib/doc/conf.py 2008-12-18 19:10:38 UTC (rev 6674) @@ -29,7 +29,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['mathmpl', 'math_symbol_table', 'sphinx.ext.autodoc', 'only_directives', 'plot_directive', 'inheritance_diagram', - 'gen_gallery'] + 'gen_gallery', 'gen_rst'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] Modified: trunk/matplotlib/doc/make.py =================================================================== --- trunk/matplotlib/doc/make.py 2008-12-18 19:08:45 UTC (rev 6673) +++ trunk/matplotlib/doc/make.py 2008-12-18 19:10:38 UTC (rev 6674) @@ -34,15 +34,12 @@ def html(): check_build() - if not os.path.exists('examples/index.rst'): - examples() shutil.copy('../lib/matplotlib/mpl-data/matplotlibrc', '_static/matplotlibrc') - #figs() if small_docs: options = "-D plot_formats=\"['png']\"" else: options = '' - if os.system('sphinx-build %s -b html -d build/doctrees . build/html' % options): + if os.system('sphinx-build %s -P -b html -d build/doctrees . build/html' % options): raise SystemExit("Building HTML failed.") figures_dest_path = 'build/html/pyplots' Property changes on: trunk/matplotlib/doc/pyplots/README ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662 + /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673 Property changes on: trunk/matplotlib/doc/sphinxext/gen_gallery.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662 + /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673 Copied: trunk/matplotlib/doc/sphinxext/gen_rst.py (from rev 6673, branches/v0_98_5_maint/doc/sphinxext/gen_rst.py) =================================================================== --- trunk/matplotlib/doc/sphinxext/gen_rst.py (rev 0) +++ trunk/matplotlib/doc/sphinxext/gen_rst.py 2008-12-18 19:10:38 UTC (rev 6674) @@ -0,0 +1,155 @@ +""" +generate the rst files for the examples by iterating over the pylab examples +""" +import os, glob + +import os +import re +import sys +fileList = [] + +def out_of_date(original, derived): + """ + Returns True if derivative is out-of-date wrt original, + both of which are full file paths. + + TODO: this check isn't adequate in some cases. Eg, if we discover + a bug when building the examples, the original and derived will be + unchanged but we still want to force a rebuild. + """ + return (not os.path.exists(derived) or + os.stat(derived).st_mtime < os.stat(original).st_mtime) + +noplot_regex = re.compile(r"#\s*-\*-\s*noplot\s*-\*-") + +def generate_example_rst(app): + rootdir = os.path.join(app.builder.srcdir, 'mpl_examples') + exampledir = os.path.join(app.builder.srcdir, 'examples') + if not os.path.exists(exampledir): + os.makedirs(exampledir) + + datad = {} + for root, subFolders, files in os.walk(rootdir): + for fname in files: + if ( fname.startswith('.') or fname.startswith('#') or fname.startswith('_') or + fname.find('.svn')>=0 or not fname.endswith('.py') ): + continue + + fullpath = os.path.join(root,fname) + contents = file(fullpath).read() + # indent + relpath = os.path.split(root)[-1] + datad.setdefault(relpath, []).append((fullpath, fname, contents)) + + subdirs = datad.keys() + subdirs.sort() + + fhindex = file(os.path.join(exampledir, 'index.rst'), 'w') + fhindex.write("""\ +.. _examples-index: + +#################### +Matplotlib Examples +#################### + +.. htmlonly:: + + :Release: |version| + :Date: |today| + +.. toctree:: + :maxdepth: 2 + +""") + + for subdir in subdirs: + rstdir = os.path.join(exampledir, subdir) + if not os.path.exists(rstdir): + os.makedirs(rstdir) + + outputdir = os.path.join(app.builder.outdir, 'examples') + if not os.path.exists(outputdir): + os.makedirs(outputdir) + + outputdir = os.path.join(outputdir, subdir) + if not os.path.exists(outputdir): + os.makedirs(outputdir) + + subdirIndexFile = os.path.join(rstdir, 'index.rst') + fhsubdirIndex = file(subdirIndexFile, 'w') + fhindex.write(' %s/index.rst\n\n'%subdir) + + fhsubdirIndex.write("""\ +.. _%s-examples-index: + +############################################## +%s Examples +############################################## + +.. htmlonly:: + + :Release: |version| + :Date: |today| + +.. toctree:: + :maxdepth: 1 + +"""%(subdir, subdir)) + + print subdir + + data = datad[subdir] + data.sort() + + for fullpath, fname, contents in data: + basename, ext = os.path.splitext(fname) + outputfile = os.path.join(outputdir, fname) + #thumbfile = os.path.join(thumb_dir, '%s.png'%basename) + #print ' static_dir=%s, basename=%s, fullpath=%s, fname=%s, thumb_dir=%s, thumbfile=%s'%(static_dir, basename, fullpath, fname, thumb_dir, thumbfile) + + rstfile = '%s.rst'%basename + outrstfile = os.path.join(rstdir, rstfile) + + fhsubdirIndex.write(' %s\n'%rstfile) + + if (not out_of_date(fullpath, outputfile) and + not out_of_date(fullpath, outrstfile)): + continue + + print ' %s'%fname + + fh = file(outrstfile, 'w') + fh.write('.. _%s-%s:\n\n'%(subdir, basename)) + title = '%s example code: %s'%(subdir, fname) + #title = '<img src=%s> %s example code: %s'%(thumbfile, subdir, fname) + + + fh.write(title + '\n') + fh.write('='*len(title) + '\n\n') + + do_plot = (subdir in ('api', + 'pylab_examples', + 'units') and + not noplot_regex.search(contents)) + + if do_plot: + fh.write("\n\n.. plot:: %s\n\n::\n\n" % fullpath) + else: + fh.write("[`source code <%s>`_]\n\n::\n\n" % fname) + fhstatic = file(outputfile, 'w') + fhstatic.write(contents) + fhstatic.close() + + # indent the contents + contents = '\n'.join([' %s'%row.rstrip() for row in contents.split('\n')]) + fh.write(contents) + + fh.write('\n\nKeywords: python, matplotlib, pylab, example, codex (see :ref:`how-to-search-examples`)') + fh.close() + + fhsubdirIndex.close() + + fhindex.close() + +def setup(app): + app.connect('builder-inited', generate_example_rst) Modified: trunk/matplotlib/doc/sphinxext/plot_directive.py =================================================================== --- trunk/matplotlib/doc/sphinxext/plot_directive.py 2008-12-18 19:08:45 UTC (rev 6673) +++ trunk/matplotlib/doc/sphinxext/plot_directive.py 2008-12-18 19:10:38 UTC (rev 6674) @@ -34,6 +34,41 @@ import matplotlib.image as image from matplotlib import _pylab_helpers +if hasattr(os.path, 'relpath'): + relpath = os.path.relpath +else: + def relpath(target, base=os.curdir): + """ + Return a relative path to the target from either the current dir or an optional base dir. + Base can be a directory specified either as absolute or relative to current dir. + """ + + if not os.path.exists(target): + raise OSError, 'Target does not exist: '+target + + if not os.path.isdir(base): + raise OSError, 'Base is not a directory or does not exist: '+base + + base_list = (os.path.abspath(base)).split(os.sep) + target_list = (os.path.abspath(target)).split(os.sep) + + # On the windows platform the target may be on a completely different drive from the base. + if os.name in ['nt','dos','os2'] and base_list[0] <> target_list[0]: + raise OSError, 'Target is on a different drive to base. Target: '+target_list[0].upper()+', base: '+base_list[0].upper() + + # Starting from the filepath root, work out how much of the filepath is + # shared by base and target. + for i in range(min(len(base_list), len(target_list))): + if base_list[i] <> target_list[i]: break + else: + # If we broke out of the loop, i is pointing to the first differing path elements. + # If we didn't break out of the loop, i is pointing to identical path elements. + # Increment i so that in all cases it points to the first differing path elements. + i+=1 + + rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] + return os.path.join(*rel_list) + def write_char(s): sys.stdout.write(s) sys.stdout.flush() @@ -186,6 +221,7 @@ reference = directives.uri(arguments[0]) basedir, fname = os.path.split(reference) basename, ext = os.path.splitext(fname) + basedir = relpath(basedir, setup.app.builder.srcdir) # Get the directory of the rst file, and determine the relative # path from the resulting html file to the plot_directive links Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2008-12-18 19:08:45 UTC (rev 6673) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2008-12-18 19:10:38 UTC (rev 6674) @@ -1148,10 +1148,9 @@ def plotting(): """ Plotting commands - - ============ ================================================= + =============== ========================================================= Command Description - ========= ================================================= + =============== ========================================================= axes Create a new axes axis Set or return the current axis limits bar make a bar chart @@ -1204,7 +1203,7 @@ title add a title to the current axes xlabel add an xlabel to the current axes ylabel add a ylabel to the current axes - ============ ================================================= + =============== ========================================================= The following commands will set the default colormap accordingly: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-18 19:08:49
|
Revision: 6673 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6673&view=rev Author: mdboom Date: 2008-12-18 19:08:45 +0000 (Thu, 18 Dec 2008) Log Message: ----------- removing examples from svn Removed Paths: ------------- branches/v0_98_5_maint/doc/examples/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-18 19:07:12
|
Revision: 6672 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6672&view=rev Author: mdboom Date: 2008-12-18 19:07:08 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Fix how example files are added to the build. Saves about 1MB in html output. Modified Paths: -------------- branches/v0_98_5_maint/doc/conf.py branches/v0_98_5_maint/doc/make.py branches/v0_98_5_maint/doc/sphinxext/plot_directive.py branches/v0_98_5_maint/lib/matplotlib/pyplot.py Added Paths: ----------- branches/v0_98_5_maint/doc/sphinxext/gen_rst.py Modified: branches/v0_98_5_maint/doc/conf.py =================================================================== --- branches/v0_98_5_maint/doc/conf.py 2008-12-18 17:41:11 UTC (rev 6671) +++ branches/v0_98_5_maint/doc/conf.py 2008-12-18 19:07:08 UTC (rev 6672) @@ -29,7 +29,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['mathmpl', 'math_symbol_table', 'sphinx.ext.autodoc', 'only_directives', 'plot_directive', 'inheritance_diagram', - 'gen_gallery'] + 'gen_gallery', 'gen_rst'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] Modified: branches/v0_98_5_maint/doc/make.py =================================================================== --- branches/v0_98_5_maint/doc/make.py 2008-12-18 17:41:11 UTC (rev 6671) +++ branches/v0_98_5_maint/doc/make.py 2008-12-18 19:07:08 UTC (rev 6672) @@ -34,15 +34,12 @@ def html(): check_build() - if not os.path.exists('examples/index.rst'): - examples() shutil.copy('../lib/matplotlib/mpl-data/matplotlibrc', '_static/matplotlibrc') - #figs() if small_docs: options = "-D plot_formats=\"['png']\"" else: options = '' - if os.system('sphinx-build %s -b html -d build/doctrees . build/html' % options): + if os.system('sphinx-build %s -P -b html -d build/doctrees . build/html' % options): raise SystemExit("Building HTML failed.") figures_dest_path = 'build/html/pyplots' Copied: branches/v0_98_5_maint/doc/sphinxext/gen_rst.py (from rev 6660, branches/v0_98_5_maint/doc/examples/gen_rst.py) =================================================================== --- branches/v0_98_5_maint/doc/sphinxext/gen_rst.py (rev 0) +++ branches/v0_98_5_maint/doc/sphinxext/gen_rst.py 2008-12-18 19:07:08 UTC (rev 6672) @@ -0,0 +1,155 @@ +""" +generate the rst files for the examples by iterating over the pylab examples +""" +import os, glob + +import os +import re +import sys +fileList = [] + +def out_of_date(original, derived): + """ + Returns True if derivative is out-of-date wrt original, + both of which are full file paths. + + TODO: this check isn't adequate in some cases. Eg, if we discover + a bug when building the examples, the original and derived will be + unchanged but we still want to force a rebuild. + """ + return (not os.path.exists(derived) or + os.stat(derived).st_mtime < os.stat(original).st_mtime) + +noplot_regex = re.compile(r"#\s*-\*-\s*noplot\s*-\*-") + +def generate_example_rst(app): + rootdir = os.path.join(app.builder.srcdir, 'mpl_examples') + exampledir = os.path.join(app.builder.srcdir, 'examples') + if not os.path.exists(exampledir): + os.makedirs(exampledir) + + datad = {} + for root, subFolders, files in os.walk(rootdir): + for fname in files: + if ( fname.startswith('.') or fname.startswith('#') or fname.startswith('_') or + fname.find('.svn')>=0 or not fname.endswith('.py') ): + continue + + fullpath = os.path.join(root,fname) + contents = file(fullpath).read() + # indent + relpath = os.path.split(root)[-1] + datad.setdefault(relpath, []).append((fullpath, fname, contents)) + + subdirs = datad.keys() + subdirs.sort() + + fhindex = file(os.path.join(exampledir, 'index.rst'), 'w') + fhindex.write("""\ +.. _examples-index: + +#################### +Matplotlib Examples +#################### + +.. htmlonly:: + + :Release: |version| + :Date: |today| + +.. toctree:: + :maxdepth: 2 + +""") + + for subdir in subdirs: + rstdir = os.path.join(exampledir, subdir) + if not os.path.exists(rstdir): + os.makedirs(rstdir) + + outputdir = os.path.join(app.builder.outdir, 'examples') + if not os.path.exists(outputdir): + os.makedirs(outputdir) + + outputdir = os.path.join(outputdir, subdir) + if not os.path.exists(outputdir): + os.makedirs(outputdir) + + subdirIndexFile = os.path.join(rstdir, 'index.rst') + fhsubdirIndex = file(subdirIndexFile, 'w') + fhindex.write(' %s/index.rst\n\n'%subdir) + + fhsubdirIndex.write("""\ +.. _%s-examples-index: + +############################################## +%s Examples +############################################## + +.. htmlonly:: + + :Release: |version| + :Date: |today| + +.. toctree:: + :maxdepth: 1 + +"""%(subdir, subdir)) + + print subdir + + data = datad[subdir] + data.sort() + + for fullpath, fname, contents in data: + basename, ext = os.path.splitext(fname) + outputfile = os.path.join(outputdir, fname) + #thumbfile = os.path.join(thumb_dir, '%s.png'%basename) + #print ' static_dir=%s, basename=%s, fullpath=%s, fname=%s, thumb_dir=%s, thumbfile=%s'%(static_dir, basename, fullpath, fname, thumb_dir, thumbfile) + + rstfile = '%s.rst'%basename + outrstfile = os.path.join(rstdir, rstfile) + + fhsubdirIndex.write(' %s\n'%rstfile) + + if (not out_of_date(fullpath, outputfile) and + not out_of_date(fullpath, outrstfile)): + continue + + print ' %s'%fname + + fh = file(outrstfile, 'w') + fh.write('.. _%s-%s:\n\n'%(subdir, basename)) + title = '%s example code: %s'%(subdir, fname) + #title = '<img src=%s> %s example code: %s'%(thumbfile, subdir, fname) + + + fh.write(title + '\n') + fh.write('='*len(title) + '\n\n') + + do_plot = (subdir in ('api', + 'pylab_examples', + 'units') and + not noplot_regex.search(contents)) + + if do_plot: + fh.write("\n\n.. plot:: %s\n\n::\n\n" % fullpath) + else: + fh.write("[`source code <%s>`_]\n\n::\n\n" % fname) + fhstatic = file(outputfile, 'w') + fhstatic.write(contents) + fhstatic.close() + + # indent the contents + contents = '\n'.join([' %s'%row.rstrip() for row in contents.split('\n')]) + fh.write(contents) + + fh.write('\n\nKeywords: python, matplotlib, pylab, example, codex (see :ref:`how-to-search-examples`)') + fh.close() + + fhsubdirIndex.close() + + fhindex.close() + +def setup(app): + app.connect('builder-inited', generate_example_rst) Property changes on: branches/v0_98_5_maint/doc/sphinxext/gen_rst.py ___________________________________________________________________ Added: svn:mergeinfo + /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 Modified: branches/v0_98_5_maint/doc/sphinxext/plot_directive.py =================================================================== --- branches/v0_98_5_maint/doc/sphinxext/plot_directive.py 2008-12-18 17:41:11 UTC (rev 6671) +++ branches/v0_98_5_maint/doc/sphinxext/plot_directive.py 2008-12-18 19:07:08 UTC (rev 6672) @@ -34,6 +34,41 @@ import matplotlib.image as image from matplotlib import _pylab_helpers +if hasattr(os.path, 'relpath'): + relpath = os.path.relpath +else: + def relpath(target, base=os.curdir): + """ + Return a relative path to the target from either the current dir or an optional base dir. + Base can be a directory specified either as absolute or relative to current dir. + """ + + if not os.path.exists(target): + raise OSError, 'Target does not exist: '+target + + if not os.path.isdir(base): + raise OSError, 'Base is not a directory or does not exist: '+base + + base_list = (os.path.abspath(base)).split(os.sep) + target_list = (os.path.abspath(target)).split(os.sep) + + # On the windows platform the target may be on a completely different drive from the base. + if os.name in ['nt','dos','os2'] and base_list[0] <> target_list[0]: + raise OSError, 'Target is on a different drive to base. Target: '+target_list[0].upper()+', base: '+base_list[0].upper() + + # Starting from the filepath root, work out how much of the filepath is + # shared by base and target. + for i in range(min(len(base_list), len(target_list))): + if base_list[i] <> target_list[i]: break + else: + # If we broke out of the loop, i is pointing to the first differing path elements. + # If we didn't break out of the loop, i is pointing to identical path elements. + # Increment i so that in all cases it points to the first differing path elements. + i+=1 + + rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] + return os.path.join(*rel_list) + def write_char(s): sys.stdout.write(s) sys.stdout.flush() @@ -186,6 +221,7 @@ reference = directives.uri(arguments[0]) basedir, fname = os.path.split(reference) basename, ext = os.path.splitext(fname) + basedir = relpath(basedir, setup.app.builder.srcdir) # Get the directory of the rst file, and determine the relative # path from the resulting html file to the plot_directive links Modified: branches/v0_98_5_maint/lib/matplotlib/pyplot.py =================================================================== --- branches/v0_98_5_maint/lib/matplotlib/pyplot.py 2008-12-18 17:41:11 UTC (rev 6671) +++ branches/v0_98_5_maint/lib/matplotlib/pyplot.py 2008-12-18 19:07:08 UTC (rev 6672) @@ -1138,9 +1138,9 @@ def plotting(): """ Plotting commands - ============ ================================================= + =============== ========================================================= Command Description - ========= ================================================= + =============== ========================================================= axes Create a new axes axis Set or return the current axis limits bar make a bar chart @@ -1193,7 +1193,7 @@ title add a title to the current axes xlabel add an xlabel to the current axes ylabel add a ylabel to the current axes - ============ ================================================= + =============== ========================================================= The following commands will set the default colormap accordingly: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 17:41:14
|
Revision: 6671 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6671&view=rev Author: jdh2358 Date: 2008-12-18 17:41:11 +0000 (Thu, 18 Dec 2008) Log Message: ----------- removed references to configobj and traits from setup.cfg Modified Paths: -------------- trunk/matplotlib/release/osx/data/setup.cfg Modified: trunk/matplotlib/release/osx/data/setup.cfg =================================================================== --- trunk/matplotlib/release/osx/data/setup.cfg 2008-12-18 17:40:19 UTC (rev 6670) +++ trunk/matplotlib/release/osx/data/setup.cfg 2008-12-18 17:41:11 UTC (rev 6671) @@ -25,10 +25,6 @@ pytz = True dateutil = True -## Experimental config package support, this should only be enabled by -## matplotlib developers, for matplotlib development -enthought.traits = False -configobj = False [gui_support] # Matplotlib supports multiple GUI toolkits, including Cocoa, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 17:40:23
|
Revision: 6670 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6670&view=rev Author: jdh2358 Date: 2008-12-18 17:40:19 +0000 (Thu, 18 Dec 2008) Log Message: ----------- updates to osx makefile Modified Paths: -------------- trunk/matplotlib/release/osx/Makefile trunk/matplotlib/release/osx/data/setup.cfg Modified: trunk/matplotlib/release/osx/Makefile =================================================================== --- trunk/matplotlib/release/osx/Makefile 2008-12-18 17:32:22 UTC (rev 6669) +++ trunk/matplotlib/release/osx/Makefile 2008-12-18 17:40:19 UTC (rev 6670) @@ -2,7 +2,7 @@ ZLIBVERSION=1.2.3 PNGVERSION=1.2.33 FREETYPEVERSION=2.3.7 -MPLVERSION=0.98.5.1 +MPLVERSION=0.98.5.2 MPLSRC=matplotlib-${MPLVERSION} MACOSX_DEPLOYMENT_TARGET=10.4 @@ -91,8 +91,16 @@ cd dist && \ zip -ro matplotlib-${MPLVERSION}-py2.5-macosx10.5.zip matplotlib-${MPLVERSION}-py2.5-macosx10.5.mpkg +upload: + rm -rf upload &&\ + mkdir upload &&\ + cp matplotlib-${MPLVERSION}.tar.gz upload/ &&\ + cp matplotlib-${MPLVERSION}/dist/matplotlib-${MPLVERSION}_r0-py2.5-macosx-10.5-i386.egg upload/matplotlib-${MPLVERSION}-py2.5.egg &&\ + cp matplotlib-${MPLVERSION}/dist/matplotlib-${MPLVERSION}-py2.5-macosx10.5.zip upload/matplotlib-${MPLVERSION}-py2.5-mpkg.zip&&\ + scp upload/* jd...@fr...:uploads/ + all: - make clean fetch_deps dependencies installers + make clean fetch_deps dependencies installers upload Modified: trunk/matplotlib/release/osx/data/setup.cfg =================================================================== --- trunk/matplotlib/release/osx/data/setup.cfg 2008-12-18 17:32:22 UTC (rev 6669) +++ trunk/matplotlib/release/osx/data/setup.cfg 2008-12-18 17:40:19 UTC (rev 6670) @@ -25,6 +25,11 @@ pytz = True dateutil = True +## Experimental config package support, this should only be enabled by +## matplotlib developers, for matplotlib development +enthought.traits = False +configobj = False + [gui_support] # Matplotlib supports multiple GUI toolkits, including Cocoa, # GTK, Fltk, MacOSX, Qt, Qt4, Tk, and WX. Support for many of This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 17:32:26
|
Revision: 6669 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6669&view=rev Author: jdh2358 Date: 2008-12-18 17:32:22 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Merged revisions 6665-6668 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6665 | jdh2358 | 2008-12-18 08:29:51 -0800 (Thu, 18 Dec 2008) | 1 line removed some configobj and traits detritus ........ r6666 | jdh2358 | 2008-12-18 08:31:44 -0800 (Thu, 18 Dec 2008) | 1 line tagging release 0.98.5.2 ........ r6667 | jdh2358 | 2008-12-18 09:02:41 -0800 (Thu, 18 Dec 2008) | 1 line removed mpl_data ........ r6668 | jdh2358 | 2008-12-18 09:03:47 -0800 (Thu, 18 Dec 2008) | 1 line retagging for 98.5.2 ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/api/api_changes.rst trunk/matplotlib/setup.py trunk/matplotlib/setupext.py Removed Paths: ------------- trunk/matplotlib/doc/mpl_data Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6662 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6668 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-18 17:03:47 UTC (rev 6668) +++ trunk/matplotlib/CHANGELOG 2008-12-18 17:32:22 UTC (rev 6669) @@ -1,5 +1,10 @@ 2008-12-18 add new arrow style, a line + filled triangles. -JJL +================================================================== +2008-12-18 Released 0.98.5.2 from v0_98_5_maint at r6667 + +2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH + 2008-12-18 Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly - MGD Modified: trunk/matplotlib/doc/api/api_changes.rst =================================================================== --- trunk/matplotlib/doc/api/api_changes.rst 2008-12-18 17:03:47 UTC (rev 6668) +++ trunk/matplotlib/doc/api/api_changes.rst 2008-12-18 17:32:22 UTC (rev 6669) @@ -15,6 +15,7 @@ Changes for 0.98.x ================== +* Removed the configobj and experiemtnal traits rc support * Modified :func:`matplotlib.mlab.psd`, :func:`matplotlib.mlab.csd`, :func:`matplotlib.mlab.cohere`, and :func:`matplotlib.mlab.specgram` Deleted: trunk/matplotlib/doc/mpl_data =================================================================== --- trunk/matplotlib/doc/mpl_data 2008-12-18 17:03:47 UTC (rev 6668) +++ trunk/matplotlib/doc/mpl_data 2008-12-18 17:32:22 UTC (rev 6669) @@ -1 +0,0 @@ -link ../lib/matplotlib/mpl-data/ \ No newline at end of file Modified: trunk/matplotlib/setup.py =================================================================== --- trunk/matplotlib/setup.py 2008-12-18 17:03:47 UTC (rev 6668) +++ trunk/matplotlib/setup.py 2008-12-18 17:32:22 UTC (rev 6669) @@ -35,12 +35,12 @@ from distutils.core import setup from setupext import build_agg, build_gtkagg, build_tkagg, build_wxagg,\ build_macosx, build_ft2font, build_image, build_windowing, build_path, \ - build_contour, build_delaunay, build_nxutils, build_traits, build_gdk, \ + build_contour, build_delaunay, build_nxutils, build_gdk, \ build_ttconv, print_line, print_status, print_message, \ print_raw, check_for_freetype, check_for_libpng, check_for_gtk, \ check_for_tk, check_for_wx, check_for_macosx, check_for_numpy, \ - check_for_qt, check_for_qt4, check_for_cairo, check_for_traits, \ - check_provide_pytz, check_provide_dateutil, check_for_configobj, \ + check_for_qt, check_for_qt4, check_for_cairo, \ + check_provide_pytz, check_provide_dateutil,\ check_for_dvipng, check_for_ghostscript, check_for_latex, \ check_for_pdftops, check_for_datetime, options, build_png #import distutils.sysconfig @@ -217,15 +217,7 @@ check_for_latex() check_for_pdftops() -# TODO: comment out for mpl release: print_raw("") -print_raw("EXPERIMENTAL CONFIG PACKAGE DEPENDENCIES") -has_configobj = check_for_configobj() -has_traits = check_for_traits() -if has_configobj and has_traits: - packages.append('matplotlib.config') - -print_raw("") print_raw("[Edit setup.cfg to suppress the above messages]") print_line() Modified: trunk/matplotlib/setupext.py =================================================================== --- trunk/matplotlib/setupext.py 2008-12-18 17:03:47 UTC (rev 6668) +++ trunk/matplotlib/setupext.py 2008-12-18 17:32:22 UTC (rev 6669) @@ -83,7 +83,6 @@ BUILT_CONTOUR = False BUILT_DELAUNAY = False BUILT_NXUTILS = False -BUILT_TRAITS = False BUILT_CONTOUR = False BUILT_GDK = False BUILT_PATH = False @@ -434,36 +433,6 @@ print_status("dateutil", "present, version unknown") return False -def check_for_configobj(): - try: - import configobj - except ImportError: - print_status("configobj", "no") - return False - else: - print_status("configobj", configobj.__version__) - return True - -def check_for_traits(): - try: - from enthought import traits - try: - from enthought.traits import version - except: - print_status("enthought.traits", "unknown and incompatible version: < 2.0") - return False - else: - # traits 2 and 3 store their version strings in different places: - try: - version = version.version - except AttributeError: - version = version.__version__ - print_status("enthought.traits", version) - return True - except ImportError: - print_status("enthought.traits", "no") - return False - def check_for_dvipng(): try: stdin, stdout = run_child_process('dvipng -version') @@ -1316,24 +1285,7 @@ BUILT_IMAGE = True -def build_traits(ext_modules, packages): - global BUILT_TRAITS - if BUILT_TRAITS: - return # only build it if you you haven't already - ctraits = Extension('enthought.traits.ctraits', - ['lib/enthought/traits/ctraits.c']) - ext_modules.append(ctraits) - packages.extend(['enthought', - 'enthought/etsconfig', - 'enthought/traits', - 'enthought/traits/ui', - 'enthought/traits/ui/extras', - 'enthought/traits/ui/null', - 'enthought/traits/ui/tk', - ]) - BUILT_TRAITS = True - def build_delaunay(ext_modules, packages): global BUILT_DELAUNAY if BUILT_DELAUNAY: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 17:03:51
|
Revision: 6668 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6668&view=rev Author: jdh2358 Date: 2008-12-18 17:03:47 +0000 (Thu, 18 Dec 2008) Log Message: ----------- retagging for 98.5.2 Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-18 17:02:41 UTC (rev 6667) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 17:03:47 UTC (rev 6668) @@ -1,7 +1,7 @@ ======================================================================= -Release 0.98.5.2 at r6665 +Release 0.98.5.3 at r6667 -2008-12-18 Removed configobj and experimental traits - JDH +2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH 2008-12-18 Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly - MGD This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 17:02:45
|
Revision: 6667 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6667&view=rev Author: jdh2358 Date: 2008-12-18 17:02:41 +0000 (Thu, 18 Dec 2008) Log Message: ----------- removed mpl_data Removed Paths: ------------- branches/v0_98_5_maint/doc/mpl_data Deleted: branches/v0_98_5_maint/doc/mpl_data =================================================================== --- branches/v0_98_5_maint/doc/mpl_data 2008-12-18 16:31:44 UTC (rev 6666) +++ branches/v0_98_5_maint/doc/mpl_data 2008-12-18 17:02:41 UTC (rev 6667) @@ -1 +0,0 @@ -link ../lib/matplotlib/mpl-data/ \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 16:31:49
|
Revision: 6666 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6666&view=rev Author: jdh2358 Date: 2008-12-18 16:31:44 +0000 (Thu, 18 Dec 2008) Log Message: ----------- tagging release 0.98.5.2 Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG branches/v0_98_5_maint/doc/api/api_changes.rst Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-18 16:29:51 UTC (rev 6665) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 16:31:44 UTC (rev 6666) @@ -1,4 +1,8 @@ +======================================================================= +Release 0.98.5.2 at r6665 +2008-12-18 Removed configobj and experimental traits - JDH + 2008-12-18 Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly - MGD Modified: branches/v0_98_5_maint/doc/api/api_changes.rst =================================================================== --- branches/v0_98_5_maint/doc/api/api_changes.rst 2008-12-18 16:29:51 UTC (rev 6665) +++ branches/v0_98_5_maint/doc/api/api_changes.rst 2008-12-18 16:31:44 UTC (rev 6666) @@ -8,6 +8,7 @@ Changes for 0.98.x ================== +* Removed the configobj and experiemtnal traits rc support * Modified :func:`matplotlib.mlab.psd`, :func:`matplotlib.mlab.csd`, :func:`matplotlib.mlab.cohere`, and :func:`matplotlib.mlab.specgram` This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 16:29:54
|
Revision: 6665 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6665&view=rev Author: jdh2358 Date: 2008-12-18 16:29:51 +0000 (Thu, 18 Dec 2008) Log Message: ----------- removed some configobj and traits detritus Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG branches/v0_98_5_maint/setup.py branches/v0_98_5_maint/setupext.py Removed Paths: ------------- branches/v0_98_5_maint/lib/configobj.py branches/v0_98_5_maint/lib/enthought/ Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-18 15:38:33 UTC (rev 6664) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 16:29:51 UTC (rev 6665) @@ -1,3 +1,4 @@ + 2008-12-18 Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly - MGD Deleted: branches/v0_98_5_maint/lib/configobj.py =================================================================== --- branches/v0_98_5_maint/lib/configobj.py 2008-12-18 15:38:33 UTC (rev 6664) +++ branches/v0_98_5_maint/lib/configobj.py 2008-12-18 16:29:51 UTC (rev 6665) @@ -1,2279 +0,0 @@ -# configobj.py -# A config file reader/writer that supports nested sections in config files. -# Copyright (C) 2005-2006 Michael Foord, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# nico AT tekNico DOT net - -# ConfigObj 4 -# http://www.voidspace.org.uk/python/configobj.html - -# Released subject to the BSD License -# Please see http://www.voidspace.org.uk/python/license.shtml - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# For information about bugfixes, updates and support, please join the -# ConfigObj mailing list: -# http://lists.sourceforge.net/lists/listinfo/configobj-develop -# Comments, suggestions and bug reports welcome. - -from __future__ import generators - -import sys -INTP_VER = sys.version_info[:2] -if INTP_VER < (2, 2): - raise RuntimeError("Python v.2.2 or later needed") - -import os, re -compiler = None -try: - import compiler -except ImportError: - # for IronPython - pass -from types import StringTypes -from warnings import warn -try: - from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE -except ImportError: - # Python 2.2 does not have these - # UTF-8 - BOM_UTF8 = '\xef\xbb\xbf' - # UTF-16, little endian - BOM_UTF16_LE = '\xff\xfe' - # UTF-16, big endian - BOM_UTF16_BE = '\xfe\xff' - if sys.byteorder == 'little': - # UTF-16, native endianness - BOM_UTF16 = BOM_UTF16_LE - else: - # UTF-16, native endianness - BOM_UTF16 = BOM_UTF16_BE - -# A dictionary mapping BOM to -# the encoding to decode with, and what to set the -# encoding attribute to. -BOMS = { - BOM_UTF8: ('utf_8', None), - BOM_UTF16_BE: ('utf16_be', 'utf_16'), - BOM_UTF16_LE: ('utf16_le', 'utf_16'), - BOM_UTF16: ('utf_16', 'utf_16'), - } -# All legal variants of the BOM codecs. -# TODO: the list of aliases is not meant to be exhaustive, is there a -# better way ? -BOM_LIST = { - 'utf_16': 'utf_16', - 'u16': 'utf_16', - 'utf16': 'utf_16', - 'utf-16': 'utf_16', - 'utf16_be': 'utf16_be', - 'utf_16_be': 'utf16_be', - 'utf-16be': 'utf16_be', - 'utf16_le': 'utf16_le', - 'utf_16_le': 'utf16_le', - 'utf-16le': 'utf16_le', - 'utf_8': 'utf_8', - 'u8': 'utf_8', - 'utf': 'utf_8', - 'utf8': 'utf_8', - 'utf-8': 'utf_8', - } - -# Map of encodings to the BOM to write. -BOM_SET = { - 'utf_8': BOM_UTF8, - 'utf_16': BOM_UTF16, - 'utf16_be': BOM_UTF16_BE, - 'utf16_le': BOM_UTF16_LE, - None: BOM_UTF8 - } - -try: - from validate import VdtMissingValue -except ImportError: - VdtMissingValue = None - -try: - enumerate -except NameError: - def enumerate(obj): - """enumerate for Python 2.2.""" - i = -1 - for item in obj: - i += 1 - yield i, item - -try: - True, False -except NameError: - True, False = 1, 0 - - -__version__ = '4.4.0-mpl' - -__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $' - -__docformat__ = "restructuredtext en" - -__all__ = ( - '__version__', - 'DEFAULT_INDENT_TYPE', - 'DEFAULT_INTERPOLATION', - 'ConfigObjError', - 'NestingError', - 'ParseError', - 'DuplicateError', - 'ConfigspecError', - 'ConfigObj', - 'SimpleVal', - 'InterpolationError', - 'InterpolationLoopError', - 'MissingInterpolationOption', - 'RepeatSectionError', - 'UnreprError', - 'UnknownType', - '__docformat__', - 'flatten_errors', -) - -DEFAULT_INTERPOLATION = 'configparser' -DEFAULT_INDENT_TYPE = ' ' -MAX_INTERPOL_DEPTH = 10 - -OPTION_DEFAULTS = { - 'interpolation': True, - 'raise_errors': False, - 'list_values': True, - 'create_empty': False, - 'file_error': False, - 'configspec': None, - 'stringify': True, - # option may be set to one of ('', ' ', '\t') - 'indent_type': None, - 'encoding': None, - 'default_encoding': None, - 'unrepr': False, - 'write_empty_values': False, -} - - -def getObj(s): - s = "a=" + s - if compiler is None: - raise ImportError('compiler module not available') - p = compiler.parse(s) - return p.getChildren()[1].getChildren()[0].getChildren()[1] - -class UnknownType(Exception): - pass - -class Builder: - - def build(self, o): - m = getattr(self, 'build_' + o.__class__.__name__, None) - if m is None: - raise UnknownType(o.__class__.__name__) - return m(o) - - def build_List(self, o): - return map(self.build, o.getChildren()) - - def build_Const(self, o): - return o.value - - def build_Dict(self, o): - d = {} - i = iter(map(self.build, o.getChildren())) - for el in i: - d[el] = i.next() - return d - - def build_Tuple(self, o): - return tuple(self.build_List(o)) - - def build_Name(self, o): - if o.name == 'None': - return None - if o.name == 'True': - return True - if o.name == 'False': - return False - - # An undefinted Name - raise UnknownType('Undefined Name') - - def build_Add(self, o): - real, imag = map(self.build_Const, o.getChildren()) - try: - real = float(real) - except TypeError: - raise UnknownType('Add') - if not isinstance(imag, complex) or imag.real != 0.0: - raise UnknownType('Add') - return real+imag - - def build_Getattr(self, o): - parent = self.build(o.expr) - return getattr(parent, o.attrname) - - def build_UnarySub(self, o): - return -self.build_Const(o.getChildren()[0]) - - def build_UnaryAdd(self, o): - return self.build_Const(o.getChildren()[0]) - -def unrepr(s): - if not s: - return s - return Builder().build(getObj(s)) - -def _splitlines(instring): - """Split a string on lines, without losing line endings or truncating.""" - - -class ConfigObjError(SyntaxError): - """ - This is the base class for all errors that ConfigObj raises. - It is a subclass of SyntaxError. - """ - def __init__(self, message='', line_number=None, line=''): - self.line = line - self.line_number = line_number - self.message = message - SyntaxError.__init__(self, message) - -class NestingError(ConfigObjError): - """ - This error indicates a level of nesting that doesn't match. - """ - -class ParseError(ConfigObjError): - """ - This error indicates that a line is badly written. - It is neither a valid ``key = value`` line, - nor a valid section marker line. - """ - -class DuplicateError(ConfigObjError): - """ - The keyword or section specified already exists. - """ - -class ConfigspecError(ConfigObjError): - """ - An error occured whilst parsing a configspec. - """ - -class InterpolationError(ConfigObjError): - """Base class for the two interpolation errors.""" - -class InterpolationLoopError(InterpolationError): - """Maximum interpolation depth exceeded in string interpolation.""" - - def __init__(self, option): - InterpolationError.__init__( - self, - 'interpolation loop detected in value "%s".' % option) - -class RepeatSectionError(ConfigObjError): - """ - This error indicates additional sections in a section with a - ``__many__`` (repeated) section. - """ - -class MissingInterpolationOption(InterpolationError): - """A value specified for interpolation was missing.""" - - def __init__(self, option): - InterpolationError.__init__( - self, - 'missing option "%s" in interpolation.' % option) - -class UnreprError(ConfigObjError): - """An error parsing in unrepr mode.""" - - -class InterpolationEngine(object): - """ - A helper class to help perform string interpolation. - - This class is an abstract base class; its descendants perform - the actual work. - """ - - # compiled regexp to use in self.interpolate() - _KEYCRE = re.compile(r"%\(([^)]*)\)s") - - def __init__(self, section): - # the Section instance that "owns" this engine - self.section = section - - def interpolate(self, key, value): - def recursive_interpolate(key, value, section, backtrail): - """The function that does the actual work. - - ``value``: the string we're trying to interpolate. - ``section``: the section in which that string was found - ``backtrail``: a dict to keep track of where we've been, - to detect and prevent infinite recursion loops - - This is similar to a depth-first-search algorithm. - """ - # Have we been here already? - if backtrail.has_key((key, section.name)): - # Yes - infinite loop detected - raise InterpolationLoopError(key) - # Place a marker on our backtrail so we won't come back here again - backtrail[(key, section.name)] = 1 - - # Now start the actual work - match = self._KEYCRE.search(value) - while match: - # The actual parsing of the match is implementation-dependent, - # so delegate to our helper function - k, v, s = self._parse_match(match) - if k is None: - # That's the signal that no further interpolation is needed - replacement = v - else: - # Further interpolation may be needed to obtain final value - replacement = recursive_interpolate(k, v, s, backtrail) - # Replace the matched string with its final value - start, end = match.span() - value = ''.join((value[:start], replacement, value[end:])) - new_search_start = start + len(replacement) - # Pick up the next interpolation key, if any, for next time - # through the while loop - match = self._KEYCRE.search(value, new_search_start) - - # Now safe to come back here again; remove marker from backtrail - del backtrail[(key, section.name)] - - return value - - # Back in interpolate(), all we have to do is kick off the recursive - # function with appropriate starting values - value = recursive_interpolate(key, value, self.section, {}) - return value - - def _fetch(self, key): - """Helper function to fetch values from owning section. - - Returns a 2-tuple: the value, and the section where it was found. - """ - # switch off interpolation before we try and fetch anything ! - save_interp = self.section.main.interpolation - self.section.main.interpolation = False - - # Start at section that "owns" this InterpolationEngine - current_section = self.section - while True: - # try the current section first - val = current_section.get(key) - if val is not None: - break - # try "DEFAULT" next - val = current_section.get('DEFAULT', {}).get(key) - if val is not None: - break - # move up to parent and try again - # top-level's parent is itself - if current_section.parent is current_section: - # reached top level, time to give up - break - current_section = current_section.parent - - # restore interpolation to previous value before returning - self.section.main.interpolation = save_interp - if val is None: - raise MissingInterpolationOption(key) - return val, current_section - - def _parse_match(self, match): - """Implementation-dependent helper function. - - Will be passed a match object corresponding to the interpolation - key we just found (e.g., "%(foo)s" or "$foo"). Should look up that - key in the appropriate config file section (using the ``_fetch()`` - helper function) and return a 3-tuple: (key, value, section) - - ``key`` is the name of the key we're looking for - ``value`` is the value found for that key - ``section`` is a reference to the section where it was found - - ``key`` and ``section`` should be None if no further - interpolation should be performed on the resulting value - (e.g., if we interpolated "$$" and returned "$"). - """ - raise NotImplementedError - - -class ConfigParserInterpolation(InterpolationEngine): - """Behaves like ConfigParser.""" - _KEYCRE = re.compile(r"%\(([^)]*)\)s") - - def _parse_match(self, match): - key = match.group(1) - value, section = self._fetch(key) - return key, value, section - - -class TemplateInterpolation(InterpolationEngine): - """Behaves like string.Template.""" - _delimiter = '$' - _KEYCRE = re.compile(r""" - \$(?: - (?P<escaped>\$) | # Two $ signs - (?P<named>[_a-z][_a-z0-9]*) | # $name format - {(?P<braced>[^}]*)} # ${name} format - ) - """, re.IGNORECASE | re.VERBOSE) - - def _parse_match(self, match): - # Valid name (in or out of braces): fetch value from section - key = match.group('named') or match.group('braced') - if key is not None: - value, section = self._fetch(key) - return key, value, section - # Escaped delimiter (e.g., $$): return single delimiter - if match.group('escaped') is not None: - # Return None for key and section to indicate it's time to stop - return None, self._delimiter, None - # Anything else: ignore completely, just return it unchanged - return None, match.group(), None - -interpolation_engines = { - 'configparser': ConfigParserInterpolation, - 'template': TemplateInterpolation, -} - -class Section(dict): - """ - A dictionary-like object that represents a section in a config file. - - It does string interpolation if the 'interpolation' attribute - of the 'main' object is set to True. - - Interpolation is tried first from this object, then from the 'DEFAULT' - section of this object, next from the parent and its 'DEFAULT' section, - and so on until the main object is reached. - - A Section will behave like an ordered dictionary - following the - order of the ``scalars`` and ``sections`` attributes. - You can use this to change the order of members. - - Iteration follows the order: scalars, then sections. - """ - - def __init__(self, parent, depth, main, indict=None, name=None): - """ - * parent is the section above - * depth is the depth level of this section - * main is the main ConfigObj - * indict is a dictionary to initialise the section with - """ - if indict is None: - indict = {} - dict.__init__(self) - # used for nesting level *and* interpolation - self.parent = parent - # used for the interpolation attribute - self.main = main - # level of nesting depth of this Section - self.depth = depth - # the sequence of scalar values in this Section - self.scalars = [] - # the sequence of sections in this Section - self.sections = [] - # purely for information - self.name = name - # for comments :-) - self.comments = {} - self.inline_comments = {} - # for the configspec - self.configspec = {} - self._order = [] - self._configspec_comments = {} - self._configspec_inline_comments = {} - self._cs_section_comments = {} - self._cs_section_inline_comments = {} - # for defaults - self.defaults = [] - # - # we do this explicitly so that __setitem__ is used properly - # (rather than just passing to ``dict.__init__``) - for entry in indict: - self[entry] = indict[entry] - - def _interpolate(self, key, value): - try: - # do we already have an interpolation engine? - engine = self._interpolation_engine - except AttributeError: - # not yet: first time running _interpolate(), so pick the engine - name = self.main.interpolation - if name == True: # note that "if name:" would be incorrect here - # backwards-compatibility: interpolation=True means use default - name = DEFAULT_INTERPOLATION - name = name.lower() # so that "Template", "template", etc. all work - class_ = interpolation_engines.get(name, None) - if class_ is None: - # invalid value for self.main.interpolation - self.main.interpolation = False - return value - else: - # save reference to engine so we don't have to do this again - engine = self._interpolation_engine = class_(self) - # let the engine do the actual work - return engine.interpolate(key, value) - - def __getitem__(self, key): - """Fetch the item and do string interpolation.""" - val = dict.__getitem__(self, key) - if self.main.interpolation and isinstance(val, StringTypes): - return self._interpolate(key, val) - return val - - def __setitem__(self, key, value, unrepr=False): - """ - Correctly set a value. - - Making dictionary values Section instances. - (We have to special case 'Section' instances - which are also dicts) - - Keys must be strings. - Values need only be strings (or lists of strings) if - ``main.stringify`` is set. - - `unrepr`` must be set when setting a value to a dictionary, without - creating a new sub-section. - """ - if not isinstance(key, StringTypes): - raise ValueError, 'The key "%s" is not a string.' % key - # add the comment - if not self.comments.has_key(key): - self.comments[key] = [] - self.inline_comments[key] = '' - # remove the entry from defaults - if key in self.defaults: - self.defaults.remove(key) - # - if isinstance(value, Section): - if not self.has_key(key): - self.sections.append(key) - dict.__setitem__(self, key, value) - elif isinstance(value, dict) and not unrepr: - # First create the new depth level, - # then create the section - if not self.has_key(key): - self.sections.append(key) - new_depth = self.depth + 1 - dict.__setitem__( - self, - key, - Section( - self, - new_depth, - self.main, - indict=value, - name=key)) - else: - if not self.has_key(key): - self.scalars.append(key) - if not self.main.stringify: - if isinstance(value, StringTypes): - pass - elif isinstance(value, (list, tuple)): - for entry in value: - if not isinstance(entry, StringTypes): - raise TypeError, ( - 'Value is not a string "%s".' % entry) - else: - raise TypeError, 'Value is not a string "%s".' % value - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - """Remove items from the sequence when deleting.""" - dict. __delitem__(self, key) - if key in self.scalars: - self.scalars.remove(key) - else: - self.sections.remove(key) - del self.comments[key] - del self.inline_comments[key] - - def get(self, key, default=None): - """A version of ``get`` that doesn't bypass string interpolation.""" - try: - return self[key] - except KeyError: - return default - - def update(self, indict): - """ - A version of update that uses our ``__setitem__``. - """ - for entry in indict: - self[entry] = indict[entry] - - def pop(self, key, *args): - """ """ - val = dict.pop(self, key, *args) - if key in self.scalars: - del self.comments[key] - del self.inline_comments[key] - self.scalars.remove(key) - elif key in self.sections: - del self.comments[key] - del self.inline_comments[key] - self.sections.remove(key) - if self.main.interpolation and isinstance(val, StringTypes): - return self._interpolate(key, val) - return val - - def popitem(self): - """Pops the first (key,val)""" - sequence = (self.scalars + self.sections) - if not sequence: - raise KeyError, ": 'popitem(): dictionary is empty'" - key = sequence[0] - val = self[key] - del self[key] - return key, val - - def clear(self): - """ - A version of clear that also affects scalars/sections - Also clears comments and configspec. - - Leaves other attributes alone : - depth/main/parent are not affected - """ - dict.clear(self) - self.scalars = [] - self.sections = [] - self.comments = {} - self.inline_comments = {} - self.configspec = {} - - def setdefault(self, key, default=None): - """A version of setdefault that sets sequence if appropriate.""" - try: - return self[key] - except KeyError: - self[key] = default - return self[key] - - def items(self): - """ """ - return zip((self.scalars + self.sections), self.values()) - - def keys(self): - """ """ - return (self.scalars + self.sections) - - def values(self): - """ """ - return [self[key] for key in (self.scalars + self.sections)] - - def iteritems(self): - """ """ - return iter(self.items()) - - def iterkeys(self): - """ """ - return iter((self.scalars + self.sections)) - - __iter__ = iterkeys - - def itervalues(self): - """ """ - return iter(self.values()) - - def __repr__(self): - return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) - for key in (self.scalars + self.sections)]) - - __str__ = __repr__ - - # Extra methods - not in a normal dictionary - - def dict(self): - """ - Return a deepcopy of self as a dictionary. - - All members that are ``Section`` instances are recursively turned to - ordinary dictionaries - by calling their ``dict`` method. - - >>> n = a.dict() - >>> n == a - 1 - >>> n is a - 0 - """ - newdict = {} - for entry in self: - this_entry = self[entry] - if isinstance(this_entry, Section): - this_entry = this_entry.dict() - elif isinstance(this_entry, list): - # create a copy rather than a reference - this_entry = list(this_entry) - elif isinstance(this_entry, tuple): - # create a copy rather than a reference - this_entry = tuple(this_entry) - newdict[entry] = this_entry - return newdict - - def merge(self, indict): - """ - A recursive update - useful for merging config files. - - >>> a = '''[section1] - ... option1 = True - ... [[subsection]] - ... more_options = False - ... # end of file'''.splitlines() - >>> b = '''# File is user.ini - ... [section1] - ... option1 = False - ... # end of file'''.splitlines() - >>> c1 = ConfigObj(b) - >>> c2 = ConfigObj(a) - >>> c2.merge(c1) - >>> c2 - {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}} - """ - for key, val in indict.items(): - if (key in self and isinstance(self[key], dict) and - isinstance(val, dict)): - self[key].merge(val) - else: - self[key] = val - - def rename(self, oldkey, newkey): - """ - Change a keyname to another, without changing position in sequence. - - Implemented so that transformations can be made on keys, - as well as on values. (used by encode and decode) - - Also renames comments. - """ - if oldkey in self.scalars: - the_list = self.scalars - elif oldkey in self.sections: - the_list = self.sections - else: - raise KeyError, 'Key "%s" not found.' % oldkey - pos = the_list.index(oldkey) - # - val = self[oldkey] - dict.__delitem__(self, oldkey) - dict.__setitem__(self, newkey, val) - the_list.remove(oldkey) - the_list.insert(pos, newkey) - comm = self.comments[oldkey] - inline_comment = self.inline_comments[oldkey] - del self.comments[oldkey] - del self.inline_comments[oldkey] - self.comments[newkey] = comm - self.inline_comments[newkey] = inline_comment - - def walk(self, function, raise_errors=True, - call_on_sections=False, **keywargs): - """ - Walk every member and call a function on the keyword and value. - - Return a dictionary of the return values - - If the function raises an exception, raise the errror - unless ``raise_errors=False``, in which case set the return value to - ``False``. - - Any unrecognised keyword arguments you pass to walk, will be pased on - to the function you pass in. - - Note: if ``call_on_sections`` is ``True`` then - on encountering a - subsection, *first* the function is called for the *whole* subsection, - and then recurses into it's members. This means your function must be - able to handle strings, dictionaries and lists. This allows you - to change the key of subsections as well as for ordinary members. The - return value when called on the whole subsection has to be discarded. - - See the encode and decode methods for examples, including functions. - - .. caution:: - - You can use ``walk`` to transform the names of members of a section - but you mustn't add or delete members. - - >>> config = '''[XXXXsection] - ... XXXXkey = XXXXvalue'''.splitlines() - >>> cfg = ConfigObj(config) - >>> cfg - {'XXXXsection': {'XXXXkey': 'XXXXvalue'}} - >>> def transform(section, key): - ... val = section[key] - ... newkey = key.replace('XXXX', 'CLIENT1') - ... section.rename(key, newkey) - ... if isinstance(val, (tuple, list, dict)): - ... pass - ... else: - ... val = val.replace('XXXX', 'CLIENT1') - ... section[newkey] = val - >>> cfg.walk(transform, call_on_sections=True) - {'CLIENT1section': {'CLIENT1key': None}} - >>> cfg - {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}} - """ - out = {} - # scalars first - for i in range(len(self.scalars)): - entry = self.scalars[i] - try: - val = function(self, entry, **keywargs) - # bound again in case name has changed - entry = self.scalars[i] - out[entry] = val - except Exception: - if raise_errors: - raise - else: - entry = self.scalars[i] - out[entry] = False - # then sections - for i in range(len(self.sections)): - entry = self.sections[i] - if call_on_sections: - try: - function(self, entry, **keywargs) - except Exception: - if raise_errors: - raise - else: - entry = self.sections[i] - out[entry] = False - # bound again in case name has changed - entry = self.sections[i] - # previous result is discarded - out[entry] = self[entry].walk( - function, - raise_errors=raise_errors, - call_on_sections=call_on_sections, - **keywargs) - return out - - def decode(self, encoding): - """ - Decode all strings and values to unicode, using the specified encoding. - - Works with subsections and list values. - - Uses the ``walk`` method. - - Testing ``encode`` and ``decode``. - >>> m = ConfigObj(a) - >>> m.decode('ascii') - >>> def testuni(val): - ... for entry in val: - ... if not isinstance(entry, unicode): - ... print >> sys.stderr, type(entry) - ... raise AssertionError, 'decode failed.' - ... if isinstance(val[entry], dict): - ... testuni(val[entry]) - ... elif not isinstance(val[entry], unicode): - ... raise AssertionError, 'decode failed.' - >>> testuni(m) - >>> m.encode('ascii') - >>> a == m - 1 - """ - warn('use of ``decode`` is deprecated.', DeprecationWarning) - def decode(section, key, encoding=encoding, warn=True): - """ """ - val = section[key] - if isinstance(val, (list, tuple)): - newval = [] - for entry in val: - newval.append(entry.decode(encoding)) - elif isinstance(val, dict): - newval = val - else: - newval = val.decode(encoding) - newkey = key.decode(encoding) - section.rename(key, newkey) - section[newkey] = newval - # using ``call_on_sections`` allows us to modify section names - self.walk(decode, call_on_sections=True) - - def encode(self, encoding): - """ - Encode all strings and values from unicode, - using the specified encoding. - - Works with subsections and list values. - Uses the ``walk`` method. - """ - warn('use of ``encode`` is deprecated.', DeprecationWarning) - def encode(section, key, encoding=encoding): - """ """ - val = section[key] - if isinstance(val, (list, tuple)): - newval = [] - for entry in val: - newval.append(entry.encode(encoding)) - elif isinstance(val, dict): - newval = val - else: - newval = val.encode(encoding) - newkey = key.encode(encoding) - section.rename(key, newkey) - section[newkey] = newval - self.walk(encode, call_on_sections=True) - - def istrue(self, key): - """A deprecated version of ``as_bool``.""" - warn('use of ``istrue`` is deprecated. Use ``as_bool`` method ' - 'instead.', DeprecationWarning) - return self.as_bool(key) - - def as_bool(self, key): - """ - Accepts a key as input. The corresponding value must be a string or - the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to - retain compatibility with Python 2.2. - - If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns - ``True``. - - If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns - ``False``. - - ``as_bool`` is not case sensitive. - - Any other input will raise a ``ValueError``. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_bool('a') - Traceback (most recent call last): - ValueError: Value "fish" is neither True nor False - >>> a['b'] = 'True' - >>> a.as_bool('b') - 1 - >>> a['b'] = 'off' - >>> a.as_bool('b') - 0 - """ - val = self[key] - if val == True: - return True - elif val == False: - return False - else: - try: - if not isinstance(val, StringTypes): - raise KeyError - else: - return self.main._bools[val.lower()] - except KeyError: - raise ValueError('Value "%s" is neither True nor False' % val) - - def as_int(self, key): - """ - A convenience method which coerces the specified value to an integer. - - If the value is an invalid literal for ``int``, a ``ValueError`` will - be raised. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_int('a') - Traceback (most recent call last): - ValueError: invalid literal for int(): fish - >>> a['b'] = '1' - >>> a.as_int('b') - 1 - >>> a['b'] = '3.2' - >>> a.as_int('b') - Traceback (most recent call last): - ValueError: invalid literal for int(): 3.2 - """ - return int(self[key]) - - def as_float(self, key): - """ - A convenience method which coerces the specified value to a float. - - If the value is an invalid literal for ``float``, a ``ValueError`` will - be raised. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_float('a') - Traceback (most recent call last): - ValueError: invalid literal for float(): fish - >>> a['b'] = '1' - >>> a.as_float('b') - 1.0 - >>> a['b'] = '3.2' - >>> a.as_float('b') - 3.2000000000000002 - """ - return float(self[key]) - - -class ConfigObj(Section): - """An object to read, create, and write config files.""" - - _keyword = re.compile(r'''^ # line start - (\s*) # indentation - ( # keyword - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'"=].*?) # no quotes - ) - \s*=\s* # divider - (.*) # value (including list values and comments) - $ # line end - ''', - re.VERBOSE) - - _sectionmarker = re.compile(r'''^ - (\s*) # 1: indentation - ((?:\[\s*)+) # 2: section marker open - ( # 3: section name open - (?:"\s*\S.*?\s*")| # at least one non-space with double quotes - (?:'\s*\S.*?\s*')| # at least one non-space with single quotes - (?:[^'"\s].*?) # at least one non-space unquoted - ) # section name close - ((?:\s*\])+) # 4: section marker close - \s*(\#.*)? # 5: optional comment - $''', - re.VERBOSE) - - # this regexp pulls list values out as a single string - # or single values and comments - # FIXME: this regex adds a '' to the end of comma terminated lists - # workaround in ``_handle_value`` - _valueexp = re.compile(r'''^ - (?: - (?: - ( - (?: - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#][^,\#]*?) # unquoted - ) - \s*,\s* # comma - )* # match all list items ending in a comma (if any) - ) - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#\s][^,]*?)| # unquoted - (?:(?<!,)) # Empty value - )? # last item in a list - or string value - )| - (,) # alternatively a single comma - empty list - ) - \s*(\#.*)? # optional comment - $''', - re.VERBOSE) - - # use findall to get the members of a list value - _listvalueexp = re.compile(r''' - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#].*?) # unquoted - ) - \s*,\s* # comma - ''', - re.VERBOSE) - - # this regexp is used for the value - # when lists are switched off - _nolistvalue = re.compile(r'''^ - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'"\#].*?)| # unquoted - (?:) # Empty value - ) - \s*(\#.*)? # optional comment - $''', - re.VERBOSE) - - # regexes for finding triple quoted values on one line - _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$") - _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$') - _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$") - _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$') - - _triple_quote = { - "'''": (_single_line_single, _multi_line_single), - '"""': (_single_line_double, _multi_line_double), - } - - # Used by the ``istrue`` Section method - _bools = { - 'yes': True, 'no': False, - 'on': True, 'off': False, - '1': True, '0': False, - 'true': True, 'false': False, - } - - def __init__(self, infile=None, options=None, **kwargs): - """ - Parse or create a config file object. - - ``ConfigObj(infile=None, options=None, **kwargs)`` - """ - if infile is None: - infile = [] - if options is None: - options = {} - else: - options = dict(options) - # keyword arguments take precedence over an options dictionary - options.update(kwargs) - # init the superclass - Section.__init__(self, self, 0, self) - # - defaults = OPTION_DEFAULTS.copy() - for entry in options.keys(): - if entry not in defaults.keys(): - raise TypeError, 'Unrecognised option "%s".' % entry - # TODO: check the values too. - # - # Add any explicit options to the defaults - defaults.update(options) - # - # initialise a few variables - self.filename = None - self._errors = [] - self.raise_errors = defaults['raise_errors'] - self.interpolation = defaults['interpolation'] - self.list_values = defaults['list_values'] - self.create_empty = defaults['create_empty'] - self.file_error = defaults['file_error'] - self.stringify = defaults['stringify'] - self.indent_type = defaults['indent_type'] - self.encoding = defaults['encoding'] - self.default_encoding = defaults['default_encoding'] - self.BOM = False - self.newlines = None - self.write_empty_values = defaults['write_empty_values'] - self.unrepr = defaults['unrepr'] - # - self.initial_comment = [] - self.final_comment = [] - # - self._terminated = False - # - if isinstance(infile, StringTypes): - self.filename = infile - if os.path.isfile(infile): - infile = open(infile).read() or [] - elif self.file_error: - # raise an error if the file doesn't exist - raise IOError, 'Config file not found: "%s".' % self.filename - else: - # file doesn't already exist - if self.create_empty: - # this is a good test that the filename specified - # isn't impossible - like on a non existent device - h = open(infile, 'w') - h.write('') - h.close() - infile = [] - elif isinstance(infile, (list, tuple)): - infile = list(infile) - elif isinstance(infile, dict): - # initialise self - # the Section class handles creating subsections - if isinstance(infile, ConfigObj): - # get a copy of our ConfigObj - infile = infile.dict() - for entry in infile: - self[entry] = infile[entry] - del self._errors - if defaults['configspec'] is not None: - self._handle_configspec(defaults['configspec']) - else: - self.configspec = None - return - elif hasattr(infile, 'read'): - # This supports file like objects - infile = infile.read() or [] - # needs splitting into lines - but needs doing *after* decoding - # in case it's not an 8 bit encoding - else: - raise TypeError, ('infile must be a filename,' - ' file like object, or list of lines.') - # - if infile: - # don't do it for the empty ConfigObj - infile = self._handle_bom(infile) - # infile is now *always* a list - # - # Set the newlines attribute (first line ending it finds) - # and strip trailing '\n' or '\r' from lines - for line in infile: - if (not line) or (line[-1] not in ('\r', '\n', '\r\n')): - continue - for end in ('\r\n', '\n', '\r'): - if line.endswith(end): - self.newlines = end - break - break - if infile[-1] and infile[-1] in ('\r', '\n', '\r\n'): - self._terminated = True - infile = [line.rstrip('\r\n') for line in infile] - # - self._parse(infile) - # if we had any errors, now is the time to raise them - if self._errors: - info = "at line %s." % self._errors[0].line_number - if len(self._errors) > 1: - msg = ("Parsing failed with several errors.\nFirst error %s" % - info) - error = ConfigObjError(msg) - else: - error = self._errors[0] - # set the errors attribute; it's a list of tuples: - # (error_type, message, line_number) - error.errors = self._errors - # set the config attribute - error.config = self - raise error - # delete private attributes - del self._errors - # - if defaults['configspec'] is None: - self.configspec = None - else: - self._handle_configspec(defaults['configspec']) - - def __repr__(self): - return 'ConfigObj({%s})' % ', '.join( - [('%s: %s' % (repr(key), repr(self[key]))) for key in - (self.scalars + self.sections)]) - - def _handle_bom(self, infile): - """ - Handle any BOM, and decode if necessary. - - If an encoding is specified, that *must* be used - but the BOM should - still be removed (and the BOM attribute set). - - (If the encoding is wrongly specified, then a BOM for an alternative - encoding won't be discovered or removed.) - - If an encoding is not specified, UTF8 or UTF16 BOM will be detected and - removed. The BOM attribute will be set. UTF16 will be decoded to - unicode. - - NOTE: This method must not be called with an empty ``infile``. - - Specifying the *wrong* encoding is likely to cause a - ``UnicodeDecodeError``. - - ``infile`` must always be returned as a list of lines, but may be - passed in as a single string. - """ - if ((self.encoding is not None) and - (self.encoding.lower() not in BOM_LIST)): - # No need to check for a BOM - # the encoding specified doesn't have one - # just decode - return self._decode(infile, self.encoding) - # - if isinstance(infile, (list, tuple)): - line = infile[0] - else: - line = infile - if self.encoding is not None: - # encoding explicitly supplied - # And it could have an associated BOM - # TODO: if encoding is just UTF16 - we ought to check for both - # TODO: big endian and little endian versions. - enc = BOM_LIST[self.encoding.lower()] - if enc == 'utf_16': - # For UTF16 we try big endian and little endian - for BOM, (encoding, final_encoding) in BOMS.items(): - if not final_encoding: - # skip UTF8 - continue - if infile.startswith(BOM): - ### BOM discovered - ##self.BOM = True - # Don't need to remove BOM - return self._decode(infile, encoding) - # - # If we get this far, will *probably* raise a DecodeError - # As it doesn't appear to start with a BOM - return self._decode(infile, self.encoding) - # - # Must be UTF8 - BOM = BOM_SET[enc] - if not line.startswith(BOM): - return self._decode(infile, self.encoding) - # - newline = line[len(BOM):] - # - # BOM removed - if isinstance(infile, (list, tuple)): - infile[0] = newline - else: - infile = newline - self.BOM = True - return self._decode(infile, self.encoding) - # - # No encoding specified - so we need to check for UTF8/UTF16 - for BOM, (encoding, final_encoding) in BOMS.items(): - if not line.startswith(BOM): - continue - else: - # BOM discovered - self.encoding = final_encoding - if not final_encoding: - self.BOM = True - # UTF8 - # remove BOM - newline = line[len(BOM):] - if isinstance(infile, (list, tuple)): - infile[0] = newline - else: - infile = newline - # UTF8 - don't decode - if isinstance(infile, StringTypes): - return infile.splitlines(True) - else: - return infile - # UTF16 - have to decode - return self._decode(infile, encoding) - # - # No BOM discovered and no encoding specified, just return - if isinstance(infile, StringTypes): - # infile read from a file will be a single string - return infile.splitlines(True) - else: - return infile - - def _a_to_u(self, aString): - """Decode ASCII strings to unicode if a self.encoding is specified.""" - if self.encoding: - return aString.decode('ascii') - else: - return aString - - def _decode(self, infile, encoding): - """ - Decode infile to unicode. Using the specified encoding. - - if is a string, it also needs converting to a list. - """ - if isinstance(infile, StringTypes): - # can't be unicode - # NOTE: Could raise a ``UnicodeDecodeError`` - return infile.decode(encoding).splitlines(True) - for i, line in enumerate(infile): - if not isinstance(line, unicode): - # NOTE: The isinstance test here handles mixed lists of unicode/string - # NOTE: But the decode will break on any non-string values - # NOTE: Or could raise a ``UnicodeDecodeError`` - infile[i] = line.decode(encoding) - return infile - - def _decode_element(self, line): - """Decode element to unicode if necessary.""" - if not self.encoding: - return line - if isinstance(line, str) and self.default_encoding: - return line.decode(self.default_encoding) - return line - - def _str(self, value): - """ - Used by ``stringify`` within validate, to turn non-string values - into strings. - """ - if not isinstance(value, StringTypes): - return str(value) - else: - return value - - def _parse(self, infile): - """Actually parse the config file.""" - temp_list_values = self.list_values - if self.unrepr: - self.list_values = False - comment_list = [] - done_start = False - this_section = self - maxline = len(infile) - 1 - cur_index = -1 - reset_comment = False - while cur_index < maxline: - if reset_comment: - comment_list = [] - cur_index += 1 - line = infile[cur_index] - sline = line.strip() - # do we have anything on the line ? - if not sline or sline.startswith('#'): - reset_comment = False - comment_list.append(line) - continue - if not done_start: - # preserve initial comment - self.initial_comment = comment_list - comment_list = [] - done_start = True - reset_comment = True - # first we check if it's a section marker - mat = self._sectionmarker.match(line) - if mat is not None: - # is a section line - (indent, sect_open, sect_name, sect_close, comment) = ( - mat.groups()) - if indent and (self.indent_type is None): - self.indent_type = indent - cur_depth = sect_open.count('[') - if cur_depth != sect_close.count(']'): - self._handle_error( - "Cannot compute the section depth at line %s.", - NestingError, infile, cur_index) - continue - # - if cur_depth < this_section.depth: - # the new section is dropping back to a previous level - try: - parent = self._match_depth( - this_section, - cur_depth).parent - except SyntaxError: - self._handle_error( - "Cannot compute nesting level at line %s.", - NestingError, infile, cur_index) - continue - elif cur_depth == this_section.depth: - # the new section is a sibling of the current section - parent = this_section.parent - elif cur_depth == this_section.depth + 1: - # the new section is a child the current section - parent = this_section - else: - self._handle_error( - "Section too nested at line %s.", - NestingError, infile, cur_index) - # - sect_name = self._unquote(sect_name) - if parent.has_key(sect_name): - self._handle_error( - 'Duplicate section name at line %s.', - DuplicateError, infile, cur_index) - continue - # create the new section - this_section = Section( - parent, - cur_depth, - self, - name=sect_name) - parent[sect_name] = this_section - parent.inline_comments[sect_name] = comment - parent.comments[sect_name] = comment_list - continue - # - # it's not a section marker, - # so it should be a valid ``key = value`` line - mat = self._keyword.match(line) - if mat is None: - # it neither matched as a keyword - # or a section marker - self._handle_error( - 'Invalid line at line "%s".', - ParseError, infile, cur_index) - else: - # is a keyword value - # value will include any inline comment - (indent, key, value) = mat.groups() - if indent and (self.indent_type is None): - self.indent_type = indent - # check for a multiline value - if value[:3] in ['"""', "'''"]: - try: - (value, comment, cur_index) = self._multiline( - value, infile, cur_index, maxline) - except SyntaxError: - self._handle_error( - 'Parse error in value at line %s.', - ParseError, infile, cur_index) - continue - else: - if self.unrepr: - comment = '' - try: - value = unrepr(value) - except Exception, e: - if type(e) == UnknownType: - msg = 'Unknown name or type in value at line %s.' - else: - msg = 'Parse error in value at line %s.' - self._handle_error(msg, UnreprError, infile, - cur_index) - continue - else: - if self.unrepr: - comment = '' - try: - value = unrepr(value) - except Exception, e: - if isinstance(e, UnknownType): - msg = 'Unknown name or type in value at line %s.' - else: - msg = 'Parse error in value at line %s.' - self._handle_error(msg, UnreprError, infile, - cur_index) - continue - else: - # extract comment and lists - try: - (value, comment) = self._handle_value(value) - except SyntaxError: - self._handle_error( - 'Parse error in value at line %s.', - ParseError, infile, cur_index) - continue - # - key = self._unquote(key) - if this_section.has_key(key): - self._handle_error( - 'Duplicate keyword name at line %s.', - DuplicateError, infile, cur_index) - continue - # add the key. - # we set unrepr because if we have got this far we will never - # be creating a new section - this_section.__setitem__(key, value, unrepr=True) - this_section.inline_comments[key] = comment - this_section.comments[key] = comment_list - continue - # - if self.indent_type is None: - # no indentation used, set the type accordingly - self.indent_type = '' - # - if self._terminated: - comment_list.append('') - # preserve the final comment - if not self and not self.initial_comment: - self.initial_comment = comment_list - elif not reset_comment: - self.final_comment = comment_list - self.list_values = temp_list_values - - def _match_depth(self, sect, depth): - """ - Given a section and a depth level, walk back through the sections - parents to see if the depth level matches a previous section. - - Return a reference to the right section, - or raise a SyntaxError. - """ - while depth < sect.depth: - if sect is sect.parent: - # we've reached the top level already - raise SyntaxError - sect = sect.parent - if sect.depth == depth: - return sect - # shouldn't get here - raise SyntaxError - - def _handle_error(self, text, ErrorClass, infile, cur_index): - """ - Handle an error according to the error settings. - - Either raise the error or store it. - The error will have occured at ``cur_index`` - """ - line = infile[cur_index] - cur_index += 1 - message = text % cur_index - error = ErrorClass(message, cur_index, line) - if self.raise_errors: - # raise the error - parsing stops here - raise error - # store the error - # reraise when parsing has finished - self._errors.append(error) - - def _unquote(self, value): - """Return an unquoted version of a value""" - if (value[0] == value[-1]) and (value[0] in ('"', "'")): - value = value[1:-1] - return value - - def _quote(self, value, multiline=True): - """ - Return a safely quoted version of a value. - - Raise a ConfigObjError if the value cannot be safely quoted. - If multiline is ``True`` (default) then use triple quotes - if necessary. - - Don't quote values that don't need it. - Recursively quote members of a list and return a comma joined list. - Multiline is ``False`` for lists. - Obey list syntax for empty and single member lists. - - If ``list_values=False`` then the value is only quoted if it contains - a ... [truncated message content] |
From: <lee...@us...> - 2008-12-18 15:38:43
|
Revision: 6664 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6664&view=rev Author: leejjoon Date: 2008-12-18 15:38:33 +0000 (Thu, 18 Dec 2008) Log Message: ----------- add new arrow style (a line + filled triangles) Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py trunk/matplotlib/lib/matplotlib/bezier.py trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-18 13:47:19 UTC (rev 6663) +++ trunk/matplotlib/CHANGELOG 2008-12-18 15:38:33 UTC (rev 6664) @@ -1,3 +1,5 @@ +2008-12-18 add new arrow style, a line + filled triangles. -JJL + 2008-12-18 Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly - MGD Modified: trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py 2008-12-18 13:47:19 UTC (rev 6663) +++ trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py 2008-12-18 15:38:33 UTC (rev 6664) @@ -3,23 +3,26 @@ styles = mpatches.ArrowStyle.get_styles() -figheight = (len(styles)+.5) -fig1 = plt.figure(1, (4, figheight)) -fontsize = 0.3 * fig1.dpi +ncol=2 +nrow = len(styles) // ncol + 1 +figheight = (nrow+0.5) +fig1 = plt.figure(1, (4.*ncol/1.5, figheight/1.5)) +fontsize = 0.2 * 70 ax = fig1.add_axes([0, 0, 1, 1], frameon=False, aspect=1.) -ax.set_xlim(0, 4) +ax.set_xlim(0, 4*ncol) ax.set_ylim(0, figheight) for i, (stylename, styleclass) in enumerate(sorted(styles.items())): - y = (float(len(styles)) -0.25 - i) # /figheight - p = mpatches.Circle((3.2, y), 0.2, fc="w") + x = 3.2 + (i//nrow)*4 + y = (figheight - 0.7 - i%nrow) # /figheight + p = mpatches.Circle((x, y), 0.2, fc="w") ax.add_patch(p) - ax.annotate(stylename, (3.2, y), - (2., y), + ax.annotate(stylename, (x, y), + (x-1.2, y), #xycoords="figure fraction", textcoords="figure fraction", ha="right", va="center", size=fontsize, Modified: trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py 2008-12-18 13:47:19 UTC (rev 6663) +++ trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py 2008-12-18 15:38:33 UTC (rev 6664) @@ -4,8 +4,8 @@ styles = mpatch.BoxStyle.get_styles() figheight = (len(styles)+.5) -fig1 = plt.figure(1, (4, figheight)) -fontsize = 0.4 * fig1.dpi +fig1 = plt.figure(1, (4/1.5, figheight/1.5)) +fontsize = 0.3 * 72 for i, (stylename, styleclass) in enumerate(styles.items()): fig1.text(0.5, (float(len(styles)) - 0.5 - i)/figheight, stylename, @@ -15,3 +15,4 @@ bbox=dict(boxstyle=stylename, fc="w", ec="k")) plt.draw() plt.show() + Modified: trunk/matplotlib/lib/matplotlib/bezier.py =================================================================== --- trunk/matplotlib/lib/matplotlib/bezier.py 2008-12-18 13:47:19 UTC (rev 6663) +++ trunk/matplotlib/lib/matplotlib/bezier.py 2008-12-18 15:38:33 UTC (rev 6664) @@ -468,6 +468,38 @@ +def make_path_regular(p): + """ + fill in the codes if None. + """ + c = p.codes + if c is None: + c = np.empty(p.vertices.shape, "i") + c.fill(Path.LINETO) + c[0] = Path.MOVETO + + return Path(p.vertices, c) + else: + return p + +def concatenate_paths(paths): + """ + concatenate list of paths into a single path. + """ + + vertices = [] + codes = [] + for p in paths: + p = make_path_regular(p) + vertices.append(p.vertices) + codes.append(p.codes) + + _path = Path(np.concatenate(vertices), + np.concatenate(codes)) + return _path + + + if 0: path = Path([(0, 0), (1, 0), (2, 2)], [Path.MOVETO, Path.CURVE3, Path.CURVE3]) @@ -476,3 +508,4 @@ ax = gca() + Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-12-18 13:47:19 UTC (rev 6663) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-12-18 15:38:33 UTC (rev 6664) @@ -2176,6 +2176,7 @@ from matplotlib.bezier import get_intersection, inside_circle, get_parallels from matplotlib.bezier import make_wedged_bezier2 from matplotlib.bezier import split_path_inout, get_cos_sin +from matplotlib.bezier import make_path_regular, concatenate_paths class ConnectionStyle(_Style): @@ -2627,12 +2628,15 @@ def transmute(self, path, mutation_size, linewidth): """ The transmute method is a very core of the ArrowStyle - 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. + 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. The linewidth may be used to adjust + the the path so that it does not pass beyond the given + points. It returns a tuple of a Path instance and a + boolean. The boolean value indicate whether the path can + be filled or not. The return value can also be a list of paths + and list of booleans of a same length. """ raise NotImplementedError('Derived must override') @@ -2646,6 +2650,8 @@ and take care of the aspect ratio. """ + path = make_path_regular(path) + if aspect_ratio is not None: # Squeeze the given height by the aspect_ratio @@ -2654,12 +2660,19 @@ 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_mutated.vertices, path_mutated.codes - # Restore the height - vertices[:,1] = vertices[:,1] * aspect_ratio - return Path(vertices, codes), closed + path_mutated, fillable = self.transmute(path_shrinked, + linewidth, + mutation_size) + if cbook.iterable(fillable): + path_list = [] + for p in zip(path_mutated): + v, c = p.vertices, p.codes + # Restore the height + v[:,1] = v[:,1] * aspect_ratio + path_list.append(Path(v, c)) + return path_list, fillable + else: + return path_mutated, fillable else: return self.transmute(path, mutation_size, linewidth) @@ -2669,21 +2682,24 @@ """ 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. + most two paths representing the arrow head at the begin point and the + at the end point. The arrow heads can be either open or closed. """ def __init__(self, beginarrow=None, endarrow=None, + fillbegin=False, fillend=False, 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*. + true. *head_length* and *head_width* determines the size + of the arrow relative to the *mutation scale*. The + arrowhead at the begin (or end) is closed if fillbegin (or + fillend) is True. """ self.beginarrow, self.endarrow = beginarrow, endarrow self.head_length, self.head_width = \ head_length, head_width + self.fillbegin, self.fillend = fillbegin, fillend super(ArrowStyle._Curve, self).__init__() @@ -2783,18 +2799,35 @@ # 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]) + _path = [Path(np.concatenate([[(x0+ddxA, y0+ddyA)], + path.vertices[1:-1], + [(x3+ddxB, y3+ddyB)]]), + path.codes)] + _fillable = [False] + + if self.beginarrow: + if self.fillbegin: + p = np.concatenate([verticesA, [verticesA[0], verticesA[0]], ]) + c = np.concatenate([codesA, [Path.LINETO, Path.CLOSEPOLY]]) + _path.append(Path(p, c)) + _fillable.append(True) + else: + _path.append(Path(verticesA, codesA)) + _fillable.append(False) + + if self.endarrow: + if self.fillend: + _fillable.append(True) + p = np.concatenate([verticesB, [verticesB[0], verticesB[0]], ]) + c = np.concatenate([codesB, [Path.LINETO, Path.CLOSEPOLY]]) + _path.append(Path(p, c)) + else: + _fillable.append(False) + _path.append(Path(verticesB, codesB)) + + return _path, _fillable - p = Path(vertices, codes) - return p, False - - class Curve(_Curve): """ A simple curve without any arrow head. @@ -2872,6 +2905,73 @@ _style_list["<->"] = CurveAB + + class CurveFilledA(_Curve): + """ + An arrow with filled triangle head at the begin. + """ + + def __init__(self, head_length=.4, head_width=.2): + """ + *head_length* + length of the arrow head + + *head_width* + width of the arrow head + """ + + super(ArrowStyle.CurveFilledA, self).__init__( \ + beginarrow=True, endarrow=False, + fillbegin=True, fillend=False, + head_length=head_length, head_width=head_width ) + + _style_list["<|-"] = CurveFilledA + + + class CurveFilledB(_Curve): + """ + An arrow with filled triangle head at the end. + """ + + def __init__(self, head_length=.4, head_width=.2): + """ + *head_length* + length of the arrow head + + *head_width* + width of the arrow head + """ + + super(ArrowStyle.CurveFilledB, self).__init__( \ + beginarrow=False, endarrow=True, + fillbegin=False, fillend=True, + head_length=head_length, head_width=head_width ) + + _style_list["-|>"] = CurveFilledB + + + class CurveFilledAB(_Curve): + """ + An arrow with filled triangle heads both at the begin and the end point. + """ + + def __init__(self, head_length=.4, head_width=.2): + """ + *head_length* + length of the arrow head + + *head_width* + width of the arrow head + """ + + super(ArrowStyle.CurveFilledAB, self).__init__( \ + beginarrow=True, endarrow=True, + fillbegin=True, fillend=True, + head_length=head_length, head_width=head_width ) + + _style_list["<|-|>"] = CurveFilledAB + + class _Bracket(_Base): def __init__(self, bracketA=None, bracketB=None, @@ -3201,6 +3301,7 @@ + class FancyArrowPatch(Patch): """ A fancy arrow patch. It draws an arrow using the :class:ArrowStyle. @@ -3423,9 +3524,14 @@ get_path_in_displaycoord() medthod to retrieve the arrow path in the disaply coord. """ - _path = self.get_path_in_displaycoord() + _path, fillable = self.get_path_in_displaycoord() + + if cbook.iterable(fillable): + _path = concatenate_paths(_path) + 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 @@ -3445,16 +3551,16 @@ - _path, closed = self.get_arrowstyle()(_path, - self.get_mutation_scale(), - self.get_linewidth(), - self.get_mutation_aspect() - ) + _path, fillable = self.get_arrowstyle()(_path, + self.get_mutation_scale(), + self.get_linewidth(), + self.get_mutation_aspect() + ) - if not closed: - self.fill = False + #if not fillable: + # self.fill = False - return _path + return _path, fillable @@ -3463,12 +3569,7 @@ #renderer.open_group('patch') gc = renderer.new_gc() - fill_orig = self.fill - path = self.get_path_in_displaycoord() - affine = transforms.IdentityTransform() - - if cbook.is_string_like(self._edgecolor) and self._edgecolor.lower()=='none': gc.set_linewidth(0) else: @@ -3494,8 +3595,22 @@ gc.set_hatch(self._hatch ) - renderer.draw_path(gc, path, affine, rgbFace) + path, fillable = self.get_path_in_displaycoord() - self.fill = fill_orig + if not cbook.iterable(fillable): + path = [path] + fillable = [fillable] + - #renderer.close_group('patch') + affine = transforms.IdentityTransform() + + renderer.open_group('patch', self.get_gid()) + + for p, f in zip(path, fillable): + if f: + renderer.draw_path(gc, p, affine, rgbFace) + else: + renderer.draw_path(gc, p, affine, None) + + + renderer.close_group('patch') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-18 13:47:23
|
Revision: 6663 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6663&view=rev Author: mdboom Date: 2008-12-18 13:47:19 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Merged revisions 6660-6662 via svnmerge from https://matplotlib.svn.sf.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6660 | jdh2358 | 2008-12-18 07:10:51 -0500 (Thu, 18 Dec 2008) | 1 line applied maxosx backend update ........ r6661 | mdboom | 2008-12-18 08:41:35 -0500 (Thu, 18 Dec 2008) | 2 lines Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly ........ r6662 | mdboom | 2008-12-18 08:42:13 -0500 (Thu, 18 Dec 2008) | 2 lines Fix docstring typo. ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/_templates/index.html trunk/matplotlib/doc/api/font_manager_api.rst trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py trunk/matplotlib/src/_macosx.m trunk/matplotlib/src/_path.cpp Property Changed: ---------------- trunk/matplotlib/ trunk/matplotlib/doc/pyplots/README trunk/matplotlib/doc/sphinxext/gen_gallery.py Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6658 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6662 Modified: svn:mergeinfo - /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652 + /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-18 13:42:13 UTC (rev 6662) +++ trunk/matplotlib/CHANGELOG 2008-12-18 13:47:19 UTC (rev 6663) @@ -1,3 +1,11 @@ +2008-12-18 Fix bug where a line with NULL data limits prevents + subsequent data limits from calculating correctly - MGD + +2008-12-17 Major documentation generator changes - MGD + +2008-12-17 Applied macosx backend patch with support for path + collections, quadmesh, etc... - JDH + 2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate -JJL Modified: trunk/matplotlib/doc/_templates/index.html =================================================================== --- trunk/matplotlib/doc/_templates/index.html 2008-12-18 13:42:13 UTC (rev 6662) +++ trunk/matplotlib/doc/_templates/index.html 2008-12-18 13:47:19 UTC (rev 6663) @@ -1,5 +1,5 @@ {% extends "layout.html" %} -{% set title = 'Overview' %} +{% set title = 'matplotlib: python plotting' %} {% block body %} Modified: trunk/matplotlib/doc/api/font_manager_api.rst =================================================================== --- trunk/matplotlib/doc/api/font_manager_api.rst 2008-12-18 13:42:13 UTC (rev 6662) +++ trunk/matplotlib/doc/api/font_manager_api.rst 2008-12-18 13:47:19 UTC (rev 6663) @@ -11,7 +11,7 @@ :show-inheritance: :mod:`matplotlib.fontconfig_pattern` -======================================== +==================================== .. automodule:: matplotlib.fontconfig_pattern :members: Property changes on: trunk/matplotlib/doc/pyplots/README ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652 + /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662 Property changes on: trunk/matplotlib/doc/sphinxext/gen_gallery.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 + /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662 Modified: trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2008-12-18 13:42:13 UTC (rev 6662) +++ trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2008-12-18 13:47:19 UTC (rev 6663) @@ -10,6 +10,7 @@ from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.mathtext import MathTextParser +from matplotlib.colors import colorConverter @@ -48,34 +49,47 @@ self.width, self.height = width, height def draw_path(self, gc, path, transform, rgbFace=None): - path = transform.transform_path(path) - for points, code in path.iter_segments(): - if code == Path.MOVETO: - gc.moveto(points) - elif code == Path.LINETO: - gc.lineto(points) - elif code == Path.CURVE3: - gc.curve3(points) - elif code == Path.CURVE4: - gc.curve4(points) - elif code == Path.CLOSEPOLY: - gc.closepoly() if rgbFace is not None: rgbFace = tuple(rgbFace) - gc.stroke(rgbFace) + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc + gc.draw_path(path, transform, rgbFace) + def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): + if rgbFace is not None: + rgbFace = tuple(rgbFace) + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc + gc.draw_markers(marker_path, marker_trans, path, trans, rgbFace) + + def draw_path_collection(self, *args): + gc = self.gc + args = args[:13] + gc.draw_path_collection(*args) + + def draw_quad_mesh(self, *args): + gc = self.gc + gc.draw_quad_mesh(*args) + def new_gc(self): self.gc.reset() return self.gc def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None): - self.gc.set_clip_rectangle(bbox) im.flipud_out() nrows, ncols, data = im.as_rgba_str() - self.gc.draw_image(x, y, nrows, ncols, data) + self.gc.draw_image(x, y, nrows, ncols, data, bbox, clippath, clippath_trans) im.flipud_out() def draw_tex(self, gc, x, y, s, prop, angle): + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc # todo, handle props, angle, origins size = prop.get_size_in_points() texmanager = self.get_texmanager() @@ -88,12 +102,20 @@ gc.draw_mathtext(x, y, angle, Z) def _draw_mathtext(self, gc, x, y, s, prop, angle): + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc size = prop.get_size_in_points() ox, oy, width, height, descent, image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) gc.draw_mathtext(x, y, angle, 255 - image.as_array()) def draw_text(self, gc, x, y, s, prop, angle, ismath=False): + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc if ismath: self._draw_mathtext(gc, x, y, s, prop, angle) else: @@ -143,6 +165,11 @@ GraphicsContextBase.__init__(self) _macosx.GraphicsContext.__init__(self) + def set_foreground(self, fg, isRGB=False): + if not isRGB: + fg = colorConverter.to_rgb(fg) + _macosx.GraphicsContext.set_foreground(self, fg) + def set_clip_rectangle(self, box): GraphicsContextBase.set_clip_rectangle(self, box) if not box: return @@ -152,20 +179,8 @@ GraphicsContextBase.set_clip_path(self, path) if not path: return path = path.get_fully_transformed_path() - for points, code in path.iter_segments(): - if code == Path.MOVETO: - self.moveto(points) - elif code == Path.LINETO: - self.lineto(points) - elif code == Path.CURVE3: - self.curve3(points) - elif code == Path.CURVE4: - self.curve4(points) - elif code == Path.CLOSEPOLY: - self.closepoly() - self.clip_path() + _macosx.GraphicsContext.set_clip_path(self, path) - ######################################################################## # # The following functions and classes are for pylab and implement Modified: trunk/matplotlib/src/_macosx.m =================================================================== --- trunk/matplotlib/src/_macosx.m 2008-12-18 13:42:13 UTC (rev 6662) +++ trunk/matplotlib/src/_macosx.m 2008-12-18 13:47:19 UTC (rev 6663) @@ -4,15 +4,33 @@ #include <Python.h> #include "numpy/arrayobject.h" -static int nwin = 0; +static int nwin = 0; /* The number of open windows */ +static int ngc = 0; /* The number of graphics contexts in use */ -/* Varius NSApplicationDefined event subtypes */ +/* For drawing Unicode strings with ATSUI */ +static ATSUStyle style = NULL; +static ATSUTextLayout layout = NULL; + +/* CGFloat was defined in Mac OS X 10.5 */ +#ifndef CGFloat +#define CGFloat float +#endif + + +/* Various NSApplicationDefined event subtypes */ #define STDIN_READY 0 #define SIGINT_CALLED 1 #define STOP_EVENT_LOOP 2 #define WINDOW_CLOSING 3 +/* Path definitions */ +#define STOP 0 +#define MOVETO 1 +#define LINETO 2 +#define CURVE3 3 +#define CURVE4 4 +#define CLOSEPOLY 5 /* -------------------------- Helper function ---------------------------- */ static void stdin_ready(CFReadStreamRef readStream, CFStreamEventType eventType, void* context) @@ -147,8 +165,44 @@ return 1; } -static char show__doc__[] = "Show all the figures and enter the main loop.\nThis function does not return until all Matplotlib windows are closed,\nand is normally not needed in interactive sessions."; +static int _init_atsui(void) +{ + OSStatus status; + status = ATSUCreateStyle(&style); + if (status!=noErr) + { + PyErr_SetString(PyExc_RuntimeError, "ATSUCreateStyle failed"); + return 0; + } + + status = ATSUCreateTextLayout(&layout); + if (status!=noErr) + { + status = ATSUDisposeStyle(style); + if (status!=noErr) + PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); + PyErr_SetString(PyExc_RuntimeError, "ATSUCreateTextLayout failed"); + return 0; + } + + + return 1; +} + +static void _dealloc_atsui(void) +{ + OSStatus status; + + status = ATSUDisposeStyle(style); + if (status!=noErr) + PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); + + status = ATSUDisposeTextLayout(layout); + if (status!=noErr) + PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeTextLayout failed", 1); +} + /* ---------------------------- Cocoa classes ---------------------------- */ @@ -220,76 +274,38 @@ typedef struct { PyObject_HEAD CGContextRef cr; - PyObject* converter; /* Convert color specifications to r,g,b triples */ CGPatternRef pattern; /* For drawing hatches */ - ATSUStyle style; /* For drawing Unicode strings with ATSUI */ - ATSUTextLayout layout; /* For drawing Unicode strings with ATSUI */ } GraphicsContext; static PyObject* GraphicsContext_new(PyTypeObject* type, PyObject *args, PyObject *kwds) { - OSStatus status; - GraphicsContext* self = (GraphicsContext*)type->tp_alloc(type, 0); if (!self) return NULL; self->cr = NULL; - PyObject* module = PyImport_AddModule("matplotlib.colors"); - if (!module) return NULL; - PyObject* dict = PyObject_GetAttrString(module, "__dict__"); - if (!dict) return NULL; - PyObject* colorConverter = PyDict_GetItemString(dict, "colorConverter"); - Py_DECREF(dict); - if (!colorConverter) - { - PyErr_SetString(PyExc_KeyError, - "failed to find colorConverter in matplotlib.colors"); - return NULL; - } - self->converter = PyObject_GetAttrString(colorConverter, "to_rgb"); - if (!self->converter) return NULL; - self->pattern = NULL; - status = ATSUCreateStyle(&self->style); - if (status!=noErr) + if (ngc==0) { - Py_DECREF(self->converter); - PyErr_SetString(PyExc_RuntimeError, "ATSUCreateStyle failed"); - return NULL; + int ok = _init_atsui(); + if (!ok) + { + return NULL; + } } + ngc++; - status = ATSUCreateTextLayout(&self->layout); - if (status!=noErr) - { - Py_DECREF(self->converter); - status = ATSUDisposeStyle(self->style); - if (status!=noErr) - PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); - PyErr_SetString(PyExc_RuntimeError, "ATSUCreateTextLayout failed"); - return NULL; - } - return (PyObject*) self; } static void GraphicsContext_dealloc(GraphicsContext *self) { - Py_DECREF(self->converter); + CGPatternRelease(self->pattern); - if (self->pattern) CGPatternRelease(self->pattern); + ngc--; + if (ngc==0) _dealloc_atsui(); - OSStatus status; - - status = ATSUDisposeStyle(self->style); - if (status!=noErr) - PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); - - status = ATSUDisposeTextLayout(self->layout); - if (status!=noErr) - PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeTextLayout failed", 1); - self->ob_type->tp_free((PyObject*)self); } @@ -308,6 +324,13 @@ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } + + if (self->pattern) + { + CGPatternRelease(self->pattern); + self->pattern = NULL; + } + CGContextRestoreGState(cr); CGContextSaveGState(cr); Py_INCREF(Py_None); @@ -400,7 +423,7 @@ } static PyObject* -GraphicsContext_clip_path (GraphicsContext* self) +GraphicsContext_set_clip_path (GraphicsContext* self, PyObject* args) { CGContextRef cr = self->cr; if (!cr) @@ -408,29 +431,216 @@ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - CGContextRestoreGState(cr); - CGContextSaveGState(cr); - CGContextClip(cr); - Py_INCREF(Py_None); - return Py_None; -} +#ifdef BUH + CGContextRestoreGState(cr); /* FIXME */ + CGContextSaveGState(cr); /* FIXME */ +#endif -static PyObject* -GraphicsContext_set_dashes (GraphicsContext* self, PyObject* args) -{ - float phase = 0.0; - PyObject* offset; - PyObject* dashes; + PyObject* path; - if (!PyArg_ParseTuple(args, "OO", &offset, &dashes)) return NULL; + if(!PyArg_ParseTuple(args, "O", &path)) return NULL; - CGContextRef cr = self->cr; - if (!cr) + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); return NULL; } + Py_DECREF(vertices); /* Don't keep a reference here */ + PyObject* codes = PyObject_GetAttrString(path, "codes"); + if (codes==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no codes"); + return NULL; + } + Py_DECREF(codes); /* Don't keep a reference here */ + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return NULL; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return NULL; + } + + npy_intp n = PyArray_DIM(coordinates, 0); + + if (n==0) /* Nothing to do here */ + { + Py_DECREF(coordinates); + return NULL; + } + + PyArrayObject* codelist = NULL; + if (codes != Py_None) + { + codelist = (PyArrayObject*)PyArray_FromObject(codes, + NPY_UINT8, 1, 1); + if (!codelist) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid codes array"); + return NULL; + } + } + + CGFloat x, y; + + if (codelist==NULL) + { + npy_intp i; + npy_uint8 code = MOVETO; + for (i = 0; i < n; i++) + { + x = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 1)); + if (isnan(x) || isnan(y)) + { + code = MOVETO; + } + else + { + switch (code) + { + case MOVETO: + CGContextMoveToPoint(cr, x, y); + break; + case LINETO: + CGContextAddLineToPoint(cr, x, y); + break; + } + code = LINETO; + } + } + } + else + { + npy_intp i = 0; + BOOL was_nan = false; + npy_uint8 code; + CGFloat x1, y1, x2, y2, x3, y3; + while (i < n) + { + code = *(npy_uint8*)PyArray_GETPTR1(codelist, i); + if (code == CLOSEPOLY) + { + CGContextClosePath(cr); + i++; + } + else if (code == STOP) + { + break; + } + else if (was_nan) + { + if (code==CURVE3) i++; + else if (code==CURVE4) i+=2; + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + CGContextMoveToPoint(cr, x1, y1); + was_nan = false; + } + } + else if (code==MOVETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + CGContextMoveToPoint(cr, x1, y1); + was_nan = false; + } + } + else if (code==LINETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + CGContextAddLineToPoint(cr, x1, y1); + was_nan = false; + } + } + else if (code==CURVE3) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2)) + { + was_nan = true; + } + else + { + CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2); + was_nan = false; + } + } + else if (code==CURVE4) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2) || isnan(x3) || isnan(y3)) + { + was_nan = true; + } + else + { + CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3); + was_nan = false; + } + } + } + Py_DECREF(codelist); + } + + Py_DECREF(coordinates); + + CGContextClip(cr); + Py_INCREF(Py_None); + return Py_None; +} + +static BOOL +_set_dashes(CGContextRef cr, PyObject* offset, PyObject* dashes) +{ + float phase = 0.0; if (offset!=Py_None) { if (PyFloat_Check(offset)) phase = PyFloat_AsDouble(offset); @@ -439,7 +649,7 @@ { PyErr_SetString(PyExc_TypeError, "offset should be a floating point value"); - return NULL; + return false; } } @@ -451,7 +661,7 @@ { PyErr_SetString(PyExc_TypeError, "dashes should be a tuple or a list"); - return NULL; + return false; } int n = PyTuple_GET_SIZE(dashes); int i; @@ -460,7 +670,7 @@ { PyErr_SetString(PyExc_MemoryError, "Failed to store dashes"); Py_DECREF(dashes); - return NULL; + return false; } for (i = 0; i < n; i++) { @@ -476,7 +686,7 @@ { free(lengths); PyErr_SetString(PyExc_TypeError, "Failed to read dashes"); - return NULL; + return false; } CGContextSetLineDash(cr, phase, lengths, n); free(lengths); @@ -484,29 +694,40 @@ else CGContextSetLineDash(cr, phase, NULL, 0); - Py_INCREF(Py_None); - return Py_None; + return true; } static PyObject* -GraphicsContext_set_foreground(GraphicsContext* self, PyObject* args, PyObject* keywords) -{ float r, g, b; - PyObject* fg; - int isRGB = 0; - static char* kwlist[] = {"fg", "isRGB", NULL}; - if(!PyArg_ParseTupleAndKeywords(args, keywords, "O|i", kwlist, - &fg, &isRGB)) return NULL; - if (isRGB) +GraphicsContext_set_dashes (GraphicsContext* self, PyObject* args) +{ + PyObject* offset; + PyObject* dashes; + + if (!PyArg_ParseTuple(args, "OO", &offset, &dashes)) return NULL; + + CGContextRef cr = self->cr; + if (!cr) { - if(!PyArg_ParseTuple(fg, "fff", &r, &g, &b)) return NULL; + PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); + return NULL; } + + BOOL ok = _set_dashes(cr, offset, dashes); + if (ok) + { + Py_INCREF(Py_None); + return Py_None; + } else - { fg = PyObject_CallFunctionObjArgs(self->converter, fg, NULL); - if(!fg) return NULL; - if(!PyArg_ParseTuple(fg, "fff", &r, &g, &b)) return NULL; - Py_DECREF(fg); - } + return NULL; +} +static PyObject* +GraphicsContext_set_foreground(GraphicsContext* self, PyObject* args) +{ + float r, g, b; + if(!PyArg_ParseTuple(args, "(fff)", &r, &g, &b)) return NULL; + CGContextRef cr = self->cr; if (!cr) { @@ -538,7 +759,7 @@ return Py_None; } -static void drawHatch (void *info, CGContextRef cr) +static void _draw_hatch (void *info, CGContextRef cr) { int i; @@ -603,12 +824,20 @@ Py_DECREF(string); } +static void _release_hatch(void* info) +{ + PyObject* hatches = info; + Py_DECREF(hatches); +} + static PyObject* GraphicsContext_set_hatch(GraphicsContext* self, PyObject* args) { PyObject* hatches; const float size = 12.0; - static const CGPatternCallbacks callbacks = {0, &drawHatch, NULL}; + static const CGPatternCallbacks callbacks = {0, + &_draw_hatch, + &_release_hatch}; CGContextRef cr = self->cr; if (!cr) @@ -687,122 +916,828 @@ return Py_None; } -static PyObject* -GraphicsContext_moveto(GraphicsContext* self, PyObject* args) -{ - float x; - float y; - - if(!PyArg_ParseTuple(args, "(ff)", &x, &y)) return NULL; - - CGContextRef cr = self->cr; - if (!cr) +static int +_convert_affine_transform(PyObject* object, CGAffineTransform* transform) +/* Reads a Numpy affine transformation matrix and returns + * a CGAffineTransform. + */ +{ + PyArrayObject* matrix = NULL; + if (object==Py_None) { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); - return NULL; + PyErr_SetString(PyExc_ValueError, + "Found affine transformation matrix equal to None"); + return 0; } - CGContextMoveToPoint(cr, x, y); + matrix = (PyArrayObject*) PyArray_FromObject(object, NPY_DOUBLE, 2, 2); + if (!matrix) + { + PyErr_SetString(PyExc_ValueError, + "Invalid affine transformation matrix"); + return 0; + } + if (PyArray_NDIM(matrix) != 2 || PyArray_DIM(matrix, 0) != 3 || PyArray_DIM(matrix, 1) != 3) + { + Py_DECREF(matrix); + PyErr_SetString(PyExc_ValueError, + "Affine transformation matrix has invalid dimensions"); + return 0; + } - Py_INCREF(Py_None); - return Py_None; + size_t stride0 = (size_t)PyArray_STRIDE(matrix, 0); + size_t stride1 = (size_t)PyArray_STRIDE(matrix, 1); + char* row0 = PyArray_BYTES(matrix); + char* row1 = row0 + stride0; + + double a = *(double*)(row0); + row0 += stride1; + double c = *(double*)(row0); + row0 += stride1; + double e = *(double*)(row0); + double b = *(double*)(row1); + row1 += stride1; + double d = *(double*)(row1); + row1 += stride1; + double f = *(double*)(row1); + *transform = CGAffineTransformMake(a, b, c, d, e, f); + + Py_DECREF(matrix); + return 1; } -static PyObject* -GraphicsContext_lineto(GraphicsContext* self, PyObject* args) +static int +_draw_path(CGContextRef cr, PyObject* path, CGAffineTransform affine) { - float x; - float y; + CGPoint point; - if(!PyArg_ParseTuple(args, "(ff)", &x, &y)) return NULL; + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); + return -1; + } + Py_DECREF(vertices); /* Don't keep a reference here */ - CGContextRef cr = self->cr; - if (!cr) + PyObject* codes = PyObject_GetAttrString(path, "codes"); + if (codes==NULL) { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); - return NULL; + PyErr_SetString(PyExc_AttributeError, "path has no codes"); + return -1; } - CGContextAddLineToPoint(cr, x, y); + Py_DECREF(codes); /* Don't keep a reference here */ - Py_INCREF(Py_None); - return Py_None; + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return -1; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return -1; + } + + npy_intp n = PyArray_DIM(coordinates, 0); + + if (n==0) /* Nothing to do here */ + { + Py_DECREF(coordinates); + return 0; + } + + PyArrayObject* codelist = NULL; + if (codes != Py_None) + { + codelist = (PyArrayObject*)PyArray_FromObject(codes, + NPY_UINT8, 1, 1); + if (!codelist) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid codes array"); + return -1; + } + } + + if (codelist==NULL) + { + npy_intp i; + npy_uint8 code = MOVETO; + for (i = 0; i < n; i++) + { + point.x = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 0)); + point.y = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 1)); + if (isnan(point.x) || isnan(point.y)) + { + code = MOVETO; + } + else + { + point = CGPointApplyAffineTransform(point, affine); + switch (code) + { + case MOVETO: + CGContextMoveToPoint(cr, point.x, point.y); + break; + case LINETO: + CGContextAddLineToPoint(cr, point.x, point.y); + break; + } + code = LINETO; + } + } + } + else + { + npy_intp i = 0; + BOOL was_nan = false; + npy_uint8 code; + CGFloat x1, y1, x2, y2, x3, y3; + while (i < n) + { + code = *(npy_uint8*)PyArray_GETPTR1(codelist, i); + if (code == CLOSEPOLY) + { + CGContextClosePath(cr); + i++; + } + else if (code == STOP) + { + break; + } + else if (was_nan) + { + if (code==CURVE3) i++; + else if (code==CURVE4) i+=2; + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextMoveToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==MOVETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextMoveToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==LINETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextAddLineToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==CURVE3) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + x1 = point.x; + y1 = point.y; + point.x = x2; + point.y = y2; + point = CGPointApplyAffineTransform(point, affine); + x2 = point.x; + y2 = point.y; + CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2); + was_nan = false; + } + } + else if (code==CURVE4) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2) || isnan(x3) || isnan(y3)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + x1 = point.x; + y1 = point.y; + point.x = x2; + point.y = y2; + point = CGPointApplyAffineTransform(point, affine); + x2 = point.x; + y2 = point.y; + point.x = x3; + point.y = y3; + point = CGPointApplyAffineTransform(point, affine); + x3 = point.x; + y3 = point.y; + CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3); + was_nan = false; + } + } + } + } + + Py_DECREF(coordinates); + Py_XDECREF(codelist); + return n; } static PyObject* -GraphicsContext_curve3(GraphicsContext* self, PyObject* args) -{ - float cpx; - float cpy; - float x; - float y; +GraphicsContext_draw_path (GraphicsContext* self, PyObject* args) +{ + PyObject* path; + PyObject* transform; + PyObject* rgbFace; + int ok; + CGContextRef cr = self->cr; + if (!cr) { PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - if(!PyArg_ParseTuple(args, "(ffff)", &cpx, - &cpy, - &x, - &y)) return NULL; + if(!PyArg_ParseTuple(args, "OO|O", + &path, + &transform, + &rgbFace)) return NULL; - CGContextAddQuadCurveToPoint(cr, cpx, cpy, x, y); + if(rgbFace==Py_None) rgbFace = NULL; + CGAffineTransform affine; + ok = _convert_affine_transform(transform, &affine); + if (!ok) return NULL; + + int n = _draw_path(cr, path, affine); + if (n==-1) return NULL; + + if (n > 0) + { + if(rgbFace) + { + float r, g, b; + ok = PyArg_ParseTuple(rgbFace, "fff", &r, &g, &b); + if (!ok) + { + return NULL; + } + CGContextSaveGState(cr); + if(self->pattern) + { + float components[4]; + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = 1.0; + CGContextSetFillPattern(cr, self->pattern, components); + CGPatternRelease(self->pattern); + self->pattern = nil; + } + else CGContextSetRGBFillColor(cr, r, g, b, 1.0); + CGContextDrawPath(cr, kCGPathFillStroke); + CGContextRestoreGState(cr); + } + else CGContextStrokePath(cr); + } + Py_INCREF(Py_None); return Py_None; } static PyObject* -GraphicsContext_curve4 (GraphicsContext* self, PyObject* args) +GraphicsContext_draw_markers (GraphicsContext* self, PyObject* args) { - float cp1x; - float cp1y; - float cp2x; - float cp2y; - float x; - float y; + PyObject* marker_path; + PyObject* marker_transform; + PyObject* path; + PyObject* transform; + PyObject* rgbFace; + int ok; + float r, g, b; + CGContextRef cr = self->cr; + if (!cr) { PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - if(!PyArg_ParseTuple(args, "(ffffff)", &cp1x, - &cp1y, - &cp2x, - &cp2y, - &x, - &y)) return NULL; + if(!PyArg_ParseTuple(args, "OOOO|O", + &marker_path, + &marker_transform, + &path, + &transform, + &rgbFace)) return NULL; - CGContextAddCurveToPoint(cr, cp1x, cp1y, cp2x, cp2y, x, y); + if(rgbFace==Py_None) rgbFace = NULL; + if (rgbFace) + { + ok = PyArg_ParseTuple(rgbFace, "fff", &r, &g, &b); + if (!ok) + { + return NULL; + } + if(self->pattern) + { + float components[4]; + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = 1.0; + CGContextSetFillPattern(cr, self->pattern, components); + CGPatternRelease(self->pattern); + self->pattern = nil; + } + else CGContextSetRGBFillColor(cr, r, g, b, 1.0); + } + + CGAffineTransform affine; + ok = _convert_affine_transform(transform, &affine); + if (!ok) return NULL; + + CGAffineTransform marker_affine; + ok = _convert_affine_transform(marker_transform, &marker_affine); + if (!ok) return NULL; + + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); + return NULL; + } + Py_DECREF(vertices); /* Don't keep a reference here */ + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return NULL; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return NULL; + } + + npy_intp i; + npy_intp n = PyArray_DIM(coordinates, 0); + CGPoint point; + CGAffineTransform t; + int m = 0; + for (i = 0; i < n; i++) + { + point.x = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + point.y = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + point = CGPointApplyAffineTransform(point, affine); + t = marker_affine; + t.tx += point.x; + t.ty += point.y; + m = _draw_path(cr, marker_path, t); + + if (m > 0) + { + if(rgbFace) CGContextDrawPath(cr, kCGPathFillStroke); + else CGContextStrokePath(cr); + } + } + + Py_DECREF(coordinates); + Py_INCREF(Py_None); return Py_None; } +static BOOL _clip(CGContextRef cr, PyObject* object) +{ + if (object == Py_None) return true; + + PyArrayObject* array = NULL; + array = (PyArrayObject*) PyArray_FromObject(object, PyArray_DOUBLE, 2, 2); + if (!array) + { + PyErr_SetString(PyExc_ValueError, "failed to read clipping bounding box"); + return false; + } + + if (PyArray_NDIM(array)!=2 || PyArray_DIM(array, 0)!=2 || PyArray_DIM(array, 1)!=2) + { + Py_DECREF(array); + PyErr_SetString(PyExc_ValueError, "clipping bounding box should be a 2x2 array"); + return false; + } + + const double l = *(double*)PyArray_GETPTR2(array, 0, 0); + const double b = *(double*)PyArray_GETPTR2(array, 0, 1); + const double r = *(double*)PyArray_GETPTR2(array, 1, 0); + const double t = *(double*)PyArray_GETPTR2(array, 1, 1); + + Py_DECREF(array); + + CGRect rect; + rect.origin.x = (CGFloat) l; + rect.origin.y = (CGFloat) b; + rect.size.width = (CGFloat) (r-l); + rect.size.height = (CGFloat) (t-b); + + CGContextClipToRect(cr, rect); + + return true; +} + + static PyObject* -GraphicsContext_closepoly (GraphicsContext* self) +GraphicsContext_draw_path_collection (GraphicsContext* self, PyObject* args) { + PyObject* master_transform_obj; + PyObject* cliprect; + PyObject* clippath; + PyObject* clippath_transform; + PyObject* paths; + PyObject* transforms_obj; + PyObject* offsets_obj; + PyObject* offset_transform_obj; + PyObject* facecolors_obj; + PyObject* edgecolors_obj; + PyObject* linewidths; + PyObject* linestyles; + PyObject* antialiaseds; + CGContextRef cr = self->cr; + if (!cr) { PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - CGContextClosePath(cr); + if(!PyArg_ParseTuple(args, "OOOOOOOOOOOOO", &master_transform_obj, + &cliprect, + &clippath, + &clippath_transform, + &paths, + &transforms_obj, + &offsets_obj, + &offset_transform_obj, + &facecolors_obj, + &edgecolors_obj, + &linewidths, + &linestyles, + &antialiaseds)) + return NULL; + CGContextSaveGState(cr); + + CGAffineTransform transform; + CGAffineTransform master_transform; + CGAffineTransform offset_transform; + CGAffineTransform* transforms = NULL; + + if (!_convert_affine_transform(master_transform_obj, &master_transform)) return NULL; + if (!_convert_affine_transform(offset_transform_obj, &offset_transform)) return NULL; + + if (!_clip(cr, cliprect)) return NULL; + if (clippath!=Py_None) + { + if (!_convert_affine_transform(clippath_transform, &transform)) return NULL; + int n = _draw_path(cr, clippath, transform); + if (n==-1) return NULL; + else if (n > 0) CGContextClip(cr); + } + + PyArrayObject* offsets = NULL; + PyArrayObject* facecolors = NULL; + PyArrayObject* edgecolors = NULL; + + /* ------------------- Check offsets array ---------------------------- */ + + offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj, NPY_DOUBLE, 0, 2); + if (!offsets || + (PyArray_NDIM(offsets)==2 && PyArray_DIM(offsets, 1)!=2) || + (PyArray_NDIM(offsets)==1 && PyArray_DIM(offsets, 0)!=0)) + { + PyErr_SetString(PyExc_ValueError, "Offsets array must be Nx2"); + goto error; + } + + /* ------------------- Check facecolors array ------------------------- */ + + facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj, + NPY_DOUBLE, 1, 2); + if (!facecolors || + (PyArray_NDIM(facecolors)==1 && PyArray_DIM(facecolors, 0)!=0) || + (PyArray_NDIM(facecolors)==2 && PyArray_DIM(facecolors, 1)!=4)) + { + PyErr_SetString(PyExc_ValueError, "Facecolors must by a Nx4 numpy array or empty"); + goto error; + } + + /* ------------------- Check edgecolors array ------------------------- */ + + edgecolors = (PyArrayObject*)PyArray_FromObject(edgecolors_obj, + NPY_DOUBLE, 1, 2); + if (!edgecolors || + (PyArray_NDIM(edgecolors)==1 && PyArray_DIM(edgecolors, 0)!=0) || + (PyArray_NDIM(edgecolors)==2 && PyArray_DIM(edgecolors, 1)!=4)) + { + PyErr_SetString(PyExc_ValueError, "Edgecolors must by a Nx4 numpy array or empty"); + goto error; + } + + /* ------------------- Check the other arguments ---------------------- */ + + if (!PySequence_Check(paths)) + { + PyErr_SetString(PyExc_ValueError, "paths must be a sequence object"); + goto error; + } + if (!PySequence_Check(transforms_obj)) + { + PyErr_SetString(PyExc_ValueError, "transforms must be a sequence object"); + goto error; + } + if (!PySequence_Check(linewidths)) + { + PyErr_SetString(PyExc_ValueError, "linewidths must be a sequence object"); + goto error; + } + if (!PySequence_Check(linestyles)) + { + PyErr_SetString(PyExc_ValueError, "linestyles must be a sequence object"); + goto error; + } + if (!PySequence_Check(antialiaseds)) + { + PyErr_SetString(PyExc_ValueError, "antialiaseds must be a sequence object"); + goto error; + } + + size_t Npaths = (size_t) PySequence_Size(paths); + size_t Noffsets = (size_t) PyArray_DIM(offsets, 0); + size_t N = Npaths > Noffsets ? Npaths : Noffsets; + size_t Ntransforms = (size_t) PySequence_Size(transforms_obj); + size_t Nfacecolors = (size_t) PyArray_DIM(facecolors, 0); + size_t Nedgecolors = (size_t) PyArray_DIM(edgecolors, 0); + size_t Nlinewidths = (size_t) PySequence_Size(linewidths); + size_t Nlinestyles = (size_t) PySequence_Size(linestyles); + size_t Naa = (size_t) PySequence_Size(antialiaseds); + if (N < Ntransforms) Ntransforms = N; + if (N < Nlinestyles) Nlinestyles = N; + if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0) + { + goto success; + } + + size_t i = 0; + + /* Convert all of the transforms up front */ + if (Ntransforms > 0) + { + transforms = malloc(Ntransforms*sizeof(CGAffineTransform)); + if (!transforms) goto error; + for (i = 0; i < Ntransforms; i++) + { + PyObject* transform_obj = PySequence_ITEM(transforms_obj, i); + if(!_convert_affine_transform(transform_obj, &transforms[i])) goto error; + transforms[i] = CGAffineTransformConcat(transforms[i], master_transform); + } + } + + CGPoint offset; + PyObject* path; + + /* Preset graphics context properties if possible */ + if (Naa==1) + { + switch(PyObject_IsTrue(PySequence_ITEM(antialiaseds, 0))) + { + case 1: CGContextSetShouldAntialias(cr, true); break; + case 0: CGContextSetShouldAntialias(cr, false); break; + case -1: + { + PyErr_SetString(PyExc_ValueError, + "Failed to read antialiaseds array"); + goto error; + } + } + } + + if (Nlinewidths==1) + { + double linewidth = PyFloat_AsDouble(PySequence_ITEM(linewidths, 0)); + CGContextSetLineWidth(cr, (CGFloat)linewidth); + } + else if (Nlinewidths==0) + CGContextSetLineWidth(cr, 0.0); + + if (Nlinestyles==1) + { + PyObject* offset; + PyObject* dashes; + PyObject* linestyle = PySequence_ITEM(linestyles, 0); + if (!PyArg_ParseTuple(linestyle, "OO", &offset, &dashes)) goto error; + if (!_set_dashes(cr, offset, dashes)) goto error; + } + + if (Nedgecolors==1) + { + const double r = *(double*)PyArray_GETPTR2(edgecolors, 0, 0); + const double g = *(double*)PyArray_GETPTR2(edgecolors, 0, 1); + const double b = *(double*)PyArray_GETPTR2(edgecolors, 0, 2); + const double a = *(double*)PyArray_GETPTR2(edgecolors, 0, 3); + CGContextSetRGBStrokeColor(cr, r, g, b, a); + } + + if (Nfacecolors==1) + { + const double r = *(double*)PyArray_GETPTR2(facecolors, 0, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, 0, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, 0, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, 0, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + } + + for (i = 0; i < N; i++) + { + + if (Ntransforms) + { + transform = transforms[i % Ntransforms]; + } + else + { + transform = master_transform; + } + + if (Noffsets) + { + offset.x = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0)); + offset.y = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1)); + offset = CGPointApplyAffineTransform(offset, offset_transform); + transform.tx += offset.x; + transform.ty += offset.y; + } + + if (Naa > 1) + { + switch(PyObject_IsTrue(PySequence_ITEM(antialiaseds, i % Naa))) + { + case 1: CGContextSetShouldAntialias(cr, true); break; + case 0: CGContextSetShouldAntialias(cr, false); break; + case -1: + { + PyErr_SetString(PyExc_ValueError, + "Failed to read antialiaseds array"); + goto error; + } + } + } + + path = PySequence_ITEM(paths, i % Npaths); + int n = _draw_path(cr, path, transform); + if (n==-1) goto error; + else if (n==0) continue; + + if (Nlinewidths > 1) + { + double linewidth = PyFloat_AsDouble(PySequence_ITEM(linewidths, i % Nlinewidths)); + CGContextSetLineWidth(cr, (CGFloat)linewidth); + } + + if (Nlinestyles > 1) + { + PyObject* offset; + PyObject* dashes; + PyObject* linestyle = PySequence_ITEM(linestyles, i % Nlinestyles); + if (!PyArg_ParseTuple(linestyle, "OO", &offset, &dashes)) goto error; + if (!_set_dashes(cr, offset, dashes)) goto error; + } + + if (Nedgecolors > 1) + { + npy_intp fi = i % Nedgecolors; + const double r = *(double*)PyArray_GETPTR2(edgecolors, fi, 0); + const double g = *(double*)PyArray_GETPTR2(edgecolors, fi, 1); + const double b = *(double*)PyArray_GETPTR2(edgecolors, fi, 2); + const double a = *(double*)PyArray_GETPTR2(edgecolors, fi, 3); + CGContextSetRGBStrokeColor(cr, r, g, b, a); + } + + if (Nfacecolors > 1) + { + npy_intp fi = i % Nfacecolors; + const double r = *(double*)PyArray_GETPTR2(facecolors, fi, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, fi, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, fi, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, fi, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + CGContextDrawPath(cr, kCGPathFillStroke); + } + else if (Nfacecolors==1) + CGContextDrawPath(cr, kCGPathFillStroke); + else + CGContextStrokePath(cr); + } + +success: + CGContextRestoreGState(cr); + if (transforms) free(transforms); + Py_DECREF(offsets); + Py_DECREF(facecolors); + Py_DECREF(edgecolors); + Py_INCREF(Py_None); return Py_None; + +error: + CGContextRestoreGState(cr); + if (transforms) free(transforms); + Py_XDECREF(offsets); + Py_XDECREF(facecolors); + Py_XDECREF(edgecolors); + + return NULL; } static PyObject* -GraphicsContext_stroke (GraphicsContext* self, PyObject* args) +GraphicsContext_draw_quad_mesh (GraphicsContext* self, PyObject* args) { - PyObject* color; + PyObject* master_transform_obj; + PyObject* cliprect; + PyObject* clippath; + PyObject* clippath_transform; + int meshWidth; + int meshHeight; + PyObject* vertices; + PyObject* offsets_obj; + PyObject* offset_transform_obj; + PyObject* facecolors_obj; + int antialiased; + int showedges; + CGContextRef cr = self->cr; if (!cr) @@ -811,35 +1746,219 @@ return NULL; } - if(!PyArg_ParseTuple(args, "O", &color)) return NULL; + if(!PyArg_ParseTuple(args, "OOOOiiOOOOii", + &master_transform_obj, + &cliprect, + &clippath, + &clippath_transform, + &meshWidth, + &meshHeight, + &vertices, + &offsets_obj, + &offset_transform_obj, + &facecolors_obj, + &antialiased, + &showedges)) return NULL; - if(color!=Py_None) + PyArrayObject* offsets = NULL; + PyArrayObject* facecolors = NULL; + + CGAffineTransform transform; + CGAffineTransform master_transform; + CGAffineTransform offset_transform; + + if (!_convert_affine_transform(master_transform_obj, &master_transform)) + return NULL; + if (!_convert_affine_transform(offset_transform_obj, &offset_transform)) + return NULL; + + /* clipping */ + + if (!_clip(cr, cliprect)) return NULL; + if (clippath!=Py_None) { - float r, g, b; - if(!PyArg_ParseTuple(color, "fff", &r, &g, &b)) return NULL; - if(self->pattern) + if (!_convert_affine_transform(clippath_transform, &transform)) + return NULL; + int n = _draw_path(cr, clippath, transform); + if (n==-1) return NULL; + else if (n > 0) CGContextClip(cr); + } + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 3, 3); + if (!coordinates || + PyArray_NDIM(coordinates) != 3 || PyArray_DIM(coordinates, 2) != 2) + { + PyErr_SetString(PyExc_ValueError, "Invalid coordinates array"); + goto error; + } + + /* ------------------- Check offsets array ---------------------------- */ + + offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj, NPY_DOUBLE, 0, 2); + if (!offsets || + (PyArray_NDIM(offsets)==2 && PyArray_DIM(offsets, 1)!=2) || + (PyArray_NDIM(offsets)==1 && PyArray_DIM(offsets, 0)!=0)) + { + PyErr_SetString(PyExc_ValueError, "Offsets array must be Nx2"); + goto error; + } + + /* ------------------- Check facecolors array ------------------------- */ + + facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj, + NPY_DOUBLE, 1, 2); + if (!facecolors || + (PyArray_NDIM(facecolors)==1 && PyArray_DIM(facecolors, 0)!=0) || + (PyArray_NDIM(facecolors)==2 && PyArray_DIM(facecolors, 1)!=4)) + { + PyErr_SetString(PyExc_ValueError, "Facecolors must by a Nx4 numpy array or empty"); + goto error; + } + + /* ------------------- Check the other arguments ---------------------- */ + + size_t Noffsets = (size_t) PyArray_DIM(offsets, 0); + size_t Npaths = meshWidth * meshHeight; + size_t Nfacecolors = (size_t) PyArray_DIM(facecolors, 0); + if ((Nfacecolors == 0 && !showedges) || Npaths == 0) + { + /* Nothing to do here */ + goto success; + } + + size_t i = 0; + size_t iw = 0; + size_t ih = 0; + + CGPoint offset; + + /* Preset graphics context properties if possible */ + if (antialiased) CGContextSetShouldAntialias(cr, true); + else CGContextSetShouldAntialias(cr, false); + + CGContextSetLineWidth(cr, 0.0); + + if (Nfacecolors==1) + { + const double r = *(double*)PyArray_GETPTR2(facecolors, 0, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, 0, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, 0, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, 0, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + if (antialiased && !showedges) { - float components[4]; - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = 1.0; - CGContextSetFillPattern(cr, self->pattern, components); - CGPatternRelease (self->pattern); - self->pattern = nil; + CGContextSetRGBStrokeColor(cr, r, g, b, a); } - else CGContextSetRGBFillColor(cr, r, g, b, 1.0); - CGContextDrawPath(cr, kCGPathFillStroke); } - else CGContextStrokePath(cr); + if (showedges) + { + CGContextSetRGBStrokeColor(cr, 0, 0, 0, 1); + } + + for (ih = 0; ih < meshHeight; ih++) + { + for (iw = 0; iw < meshWidth; iw++, i++) + { + + transform = master_transform; + + if (Noffsets) + { + offset.x = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0)); + offset.y = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1)); + offset = CGPointApplyAffineTransform(offset, offset_transform); + transform.tx += offset.x; + transform.ty += offset.y; + } + + CGPoint p; + CGPoint points[4]; + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[0] = CGPointApplyAffineTransform(p, transform); + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw+1, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw+1, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[1] = CGPointApplyAffineTransform(p, transform); + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw+1, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw+1, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[2] = CGPointApplyAffineTransform(p, transform); + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[3] = CGPointApplyAffineTransform(p, transf... [truncated message content] |
From: <md...@us...> - 2008-12-18 13:42:17
|
Revision: 6662 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6662&view=rev Author: mdboom Date: 2008-12-18 13:42:13 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Fix docstring typo. Modified Paths: -------------- branches/v0_98_5_maint/doc/api/font_manager_api.rst Modified: branches/v0_98_5_maint/doc/api/font_manager_api.rst =================================================================== --- branches/v0_98_5_maint/doc/api/font_manager_api.rst 2008-12-18 13:41:35 UTC (rev 6661) +++ branches/v0_98_5_maint/doc/api/font_manager_api.rst 2008-12-18 13:42:13 UTC (rev 6662) @@ -11,7 +11,7 @@ :show-inheritance: :mod:`matplotlib.fontconfig_pattern` -======================================== +==================================== .. automodule:: matplotlib.fontconfig_pattern :members: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-18 13:41:39
|
Revision: 6661 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6661&view=rev Author: mdboom Date: 2008-12-18 13:41:35 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Fix bug where a line with NULL data limits prevents subsequent data limits from calculating correctly Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG branches/v0_98_5_maint/src/_path.cpp Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-18 12:10:51 UTC (rev 6660) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 13:41:35 UTC (rev 6661) @@ -1,3 +1,8 @@ +2008-12-18 Fix bug where a line with NULL data limits prevents + subsequent data limits from calculating correctly - MGD + +2008-12-17 Major documentation generator changes - MGD + 2008-12-17 Applied macosx backend patch with support for path collections, quadmesh, etc... - JDH @@ -3,4 +8,5 @@ 2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate -JJL + 2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL Modified: branches/v0_98_5_maint/src/_path.cpp =================================================================== --- branches/v0_98_5_maint/src/_path.cpp 2008-12-18 12:10:51 UTC (rev 6660) +++ branches/v0_98_5_maint/src/_path.cpp 2008-12-18 13:41:35 UTC (rev 6661) @@ -403,10 +403,25 @@ } else { - extents_data[0] = std::min(x0, x1); - extents_data[1] = std::min(y0, y1); - extents_data[2] = std::max(x0, x1); - extents_data[3] = std::max(y0, y1); + if (x0 > x1) + { + extents_data[0] = std::numeric_limits<double>::infinity(); + extents_data[2] = -std::numeric_limits<double>::infinity(); + } + else + { + extents_data[0] = x0; + extents_data[2] = x1; + } + if (y0 > y1) { + extents_data[1] = std::numeric_limits<double>::infinity(); + extents_data[3] = -std::numeric_limits<double>::infinity(); + } + else + { + extents_data[1] = y0; + extents_data[3] = y1; + } minpos_data[0] = xm; minpos_data[1] = ym; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-18 12:23:14
|
Revision: 6660 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6660&view=rev Author: jdh2358 Date: 2008-12-18 12:10:51 +0000 (Thu, 18 Dec 2008) Log Message: ----------- applied maxosx backend update Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG branches/v0_98_5_maint/doc/_templates/index.html branches/v0_98_5_maint/lib/matplotlib/__init__.py branches/v0_98_5_maint/lib/matplotlib/backends/backend_macosx.py branches/v0_98_5_maint/src/_macosx.m Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-18 09:02:11 UTC (rev 6659) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 12:10:51 UTC (rev 6660) @@ -1,6 +1,8 @@ +2008-12-17 Applied macosx backend patch with support for path + collections, quadmesh, etc... - JDH + 2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate -JJL - 2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL ====================================================================== Modified: branches/v0_98_5_maint/doc/_templates/index.html =================================================================== --- branches/v0_98_5_maint/doc/_templates/index.html 2008-12-18 09:02:11 UTC (rev 6659) +++ branches/v0_98_5_maint/doc/_templates/index.html 2008-12-18 12:10:51 UTC (rev 6660) @@ -1,5 +1,5 @@ {% extends "layout.html" %} -{% set title = 'Overview' %} +{% set title = 'matplotlib: python plotting' %} {% block body %} Modified: branches/v0_98_5_maint/lib/matplotlib/__init__.py =================================================================== --- branches/v0_98_5_maint/lib/matplotlib/__init__.py 2008-12-18 09:02:11 UTC (rev 6659) +++ branches/v0_98_5_maint/lib/matplotlib/__init__.py 2008-12-18 12:10:51 UTC (rev 6660) @@ -89,7 +89,7 @@ """ from __future__ import generators -__version__ = '0.98.5.1' +__version__ = '0.98.5.2' __revision__ = '$Revision$' __date__ = '$Date$' Modified: branches/v0_98_5_maint/lib/matplotlib/backends/backend_macosx.py =================================================================== --- branches/v0_98_5_maint/lib/matplotlib/backends/backend_macosx.py 2008-12-18 09:02:11 UTC (rev 6659) +++ branches/v0_98_5_maint/lib/matplotlib/backends/backend_macosx.py 2008-12-18 12:10:51 UTC (rev 6660) @@ -10,6 +10,7 @@ from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.mathtext import MathTextParser +from matplotlib.colors import colorConverter @@ -48,34 +49,47 @@ self.width, self.height = width, height def draw_path(self, gc, path, transform, rgbFace=None): - path = transform.transform_path(path) - for points, code in path.iter_segments(): - if code == Path.MOVETO: - gc.moveto(points) - elif code == Path.LINETO: - gc.lineto(points) - elif code == Path.CURVE3: - gc.curve3(points) - elif code == Path.CURVE4: - gc.curve4(points) - elif code == Path.CLOSEPOLY: - gc.closepoly() if rgbFace is not None: rgbFace = tuple(rgbFace) - gc.stroke(rgbFace) + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc + gc.draw_path(path, transform, rgbFace) + def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): + if rgbFace is not None: + rgbFace = tuple(rgbFace) + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc + gc.draw_markers(marker_path, marker_trans, path, trans, rgbFace) + + def draw_path_collection(self, *args): + gc = self.gc + args = args[:13] + gc.draw_path_collection(*args) + + def draw_quad_mesh(self, *args): + gc = self.gc + gc.draw_quad_mesh(*args) + def new_gc(self): self.gc.reset() return self.gc def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None): - self.gc.set_clip_rectangle(bbox) im.flipud_out() nrows, ncols, data = im.as_rgba_str() - self.gc.draw_image(x, y, nrows, ncols, data) + self.gc.draw_image(x, y, nrows, ncols, data, bbox, clippath, clippath_trans) im.flipud_out() def draw_tex(self, gc, x, y, s, prop, angle): + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc # todo, handle props, angle, origins size = prop.get_size_in_points() texmanager = self.get_texmanager() @@ -88,12 +102,20 @@ gc.draw_mathtext(x, y, angle, Z) def _draw_mathtext(self, gc, x, y, s, prop, angle): + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc size = prop.get_size_in_points() ox, oy, width, height, descent, image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) gc.draw_mathtext(x, y, angle, 255 - image.as_array()) def draw_text(self, gc, x, y, s, prop, angle, ismath=False): + if gc!=self.gc: + n = self.gc.level() - gc.level() + for i in range(n): self.gc.restore() + self.gc = gc if ismath: self._draw_mathtext(gc, x, y, s, prop, angle) else: @@ -143,6 +165,11 @@ GraphicsContextBase.__init__(self) _macosx.GraphicsContext.__init__(self) + def set_foreground(self, fg, isRGB=False): + if not isRGB: + fg = colorConverter.to_rgb(fg) + _macosx.GraphicsContext.set_foreground(self, fg) + def set_clip_rectangle(self, box): GraphicsContextBase.set_clip_rectangle(self, box) if not box: return @@ -152,20 +179,8 @@ GraphicsContextBase.set_clip_path(self, path) if not path: return path = path.get_fully_transformed_path() - for points, code in path.iter_segments(): - if code == Path.MOVETO: - self.moveto(points) - elif code == Path.LINETO: - self.lineto(points) - elif code == Path.CURVE3: - self.curve3(points) - elif code == Path.CURVE4: - self.curve4(points) - elif code == Path.CLOSEPOLY: - self.closepoly() - self.clip_path() + _macosx.GraphicsContext.set_clip_path(self, path) - ######################################################################## # # The following functions and classes are for pylab and implement Modified: branches/v0_98_5_maint/src/_macosx.m =================================================================== --- branches/v0_98_5_maint/src/_macosx.m 2008-12-18 09:02:11 UTC (rev 6659) +++ branches/v0_98_5_maint/src/_macosx.m 2008-12-18 12:10:51 UTC (rev 6660) @@ -4,15 +4,33 @@ #include <Python.h> #include "numpy/arrayobject.h" -static int nwin = 0; +static int nwin = 0; /* The number of open windows */ +static int ngc = 0; /* The number of graphics contexts in use */ -/* Varius NSApplicationDefined event subtypes */ +/* For drawing Unicode strings with ATSUI */ +static ATSUStyle style = NULL; +static ATSUTextLayout layout = NULL; + +/* CGFloat was defined in Mac OS X 10.5 */ +#ifndef CGFloat +#define CGFloat float +#endif + + +/* Various NSApplicationDefined event subtypes */ #define STDIN_READY 0 #define SIGINT_CALLED 1 #define STOP_EVENT_LOOP 2 #define WINDOW_CLOSING 3 +/* Path definitions */ +#define STOP 0 +#define MOVETO 1 +#define LINETO 2 +#define CURVE3 3 +#define CURVE4 4 +#define CLOSEPOLY 5 /* -------------------------- Helper function ---------------------------- */ static void stdin_ready(CFReadStreamRef readStream, CFStreamEventType eventType, void* context) @@ -147,8 +165,44 @@ return 1; } -static char show__doc__[] = "Show all the figures and enter the main loop.\nThis function does not return until all Matplotlib windows are closed,\nand is normally not needed in interactive sessions."; +static int _init_atsui(void) +{ + OSStatus status; + status = ATSUCreateStyle(&style); + if (status!=noErr) + { + PyErr_SetString(PyExc_RuntimeError, "ATSUCreateStyle failed"); + return 0; + } + + status = ATSUCreateTextLayout(&layout); + if (status!=noErr) + { + status = ATSUDisposeStyle(style); + if (status!=noErr) + PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); + PyErr_SetString(PyExc_RuntimeError, "ATSUCreateTextLayout failed"); + return 0; + } + + + return 1; +} + +static void _dealloc_atsui(void) +{ + OSStatus status; + + status = ATSUDisposeStyle(style); + if (status!=noErr) + PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); + + status = ATSUDisposeTextLayout(layout); + if (status!=noErr) + PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeTextLayout failed", 1); +} + /* ---------------------------- Cocoa classes ---------------------------- */ @@ -220,76 +274,38 @@ typedef struct { PyObject_HEAD CGContextRef cr; - PyObject* converter; /* Convert color specifications to r,g,b triples */ CGPatternRef pattern; /* For drawing hatches */ - ATSUStyle style; /* For drawing Unicode strings with ATSUI */ - ATSUTextLayout layout; /* For drawing Unicode strings with ATSUI */ } GraphicsContext; static PyObject* GraphicsContext_new(PyTypeObject* type, PyObject *args, PyObject *kwds) { - OSStatus status; - GraphicsContext* self = (GraphicsContext*)type->tp_alloc(type, 0); if (!self) return NULL; self->cr = NULL; - PyObject* module = PyImport_AddModule("matplotlib.colors"); - if (!module) return NULL; - PyObject* dict = PyObject_GetAttrString(module, "__dict__"); - if (!dict) return NULL; - PyObject* colorConverter = PyDict_GetItemString(dict, "colorConverter"); - Py_DECREF(dict); - if (!colorConverter) - { - PyErr_SetString(PyExc_KeyError, - "failed to find colorConverter in matplotlib.colors"); - return NULL; - } - self->converter = PyObject_GetAttrString(colorConverter, "to_rgb"); - if (!self->converter) return NULL; - self->pattern = NULL; - status = ATSUCreateStyle(&self->style); - if (status!=noErr) + if (ngc==0) { - Py_DECREF(self->converter); - PyErr_SetString(PyExc_RuntimeError, "ATSUCreateStyle failed"); - return NULL; + int ok = _init_atsui(); + if (!ok) + { + return NULL; + } } + ngc++; - status = ATSUCreateTextLayout(&self->layout); - if (status!=noErr) - { - Py_DECREF(self->converter); - status = ATSUDisposeStyle(self->style); - if (status!=noErr) - PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); - PyErr_SetString(PyExc_RuntimeError, "ATSUCreateTextLayout failed"); - return NULL; - } - return (PyObject*) self; } static void GraphicsContext_dealloc(GraphicsContext *self) { - Py_DECREF(self->converter); + CGPatternRelease(self->pattern); - if (self->pattern) CGPatternRelease(self->pattern); + ngc--; + if (ngc==0) _dealloc_atsui(); - OSStatus status; - - status = ATSUDisposeStyle(self->style); - if (status!=noErr) - PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1); - - status = ATSUDisposeTextLayout(self->layout); - if (status!=noErr) - PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeTextLayout failed", 1); - self->ob_type->tp_free((PyObject*)self); } @@ -308,6 +324,13 @@ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } + + if (self->pattern) + { + CGPatternRelease(self->pattern); + self->pattern = NULL; + } + CGContextRestoreGState(cr); CGContextSaveGState(cr); Py_INCREF(Py_None); @@ -400,7 +423,7 @@ } static PyObject* -GraphicsContext_clip_path (GraphicsContext* self) +GraphicsContext_set_clip_path (GraphicsContext* self, PyObject* args) { CGContextRef cr = self->cr; if (!cr) @@ -408,29 +431,216 @@ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - CGContextRestoreGState(cr); - CGContextSaveGState(cr); - CGContextClip(cr); - Py_INCREF(Py_None); - return Py_None; -} +#ifdef BUH + CGContextRestoreGState(cr); /* FIXME */ + CGContextSaveGState(cr); /* FIXME */ +#endif -static PyObject* -GraphicsContext_set_dashes (GraphicsContext* self, PyObject* args) -{ - float phase = 0.0; - PyObject* offset; - PyObject* dashes; + PyObject* path; - if (!PyArg_ParseTuple(args, "OO", &offset, &dashes)) return NULL; + if(!PyArg_ParseTuple(args, "O", &path)) return NULL; - CGContextRef cr = self->cr; - if (!cr) + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); return NULL; } + Py_DECREF(vertices); /* Don't keep a reference here */ + PyObject* codes = PyObject_GetAttrString(path, "codes"); + if (codes==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no codes"); + return NULL; + } + Py_DECREF(codes); /* Don't keep a reference here */ + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return NULL; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return NULL; + } + + npy_intp n = PyArray_DIM(coordinates, 0); + + if (n==0) /* Nothing to do here */ + { + Py_DECREF(coordinates); + return NULL; + } + + PyArrayObject* codelist = NULL; + if (codes != Py_None) + { + codelist = (PyArrayObject*)PyArray_FromObject(codes, + NPY_UINT8, 1, 1); + if (!codelist) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid codes array"); + return NULL; + } + } + + CGFloat x, y; + + if (codelist==NULL) + { + npy_intp i; + npy_uint8 code = MOVETO; + for (i = 0; i < n; i++) + { + x = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 1)); + if (isnan(x) || isnan(y)) + { + code = MOVETO; + } + else + { + switch (code) + { + case MOVETO: + CGContextMoveToPoint(cr, x, y); + break; + case LINETO: + CGContextAddLineToPoint(cr, x, y); + break; + } + code = LINETO; + } + } + } + else + { + npy_intp i = 0; + BOOL was_nan = false; + npy_uint8 code; + CGFloat x1, y1, x2, y2, x3, y3; + while (i < n) + { + code = *(npy_uint8*)PyArray_GETPTR1(codelist, i); + if (code == CLOSEPOLY) + { + CGContextClosePath(cr); + i++; + } + else if (code == STOP) + { + break; + } + else if (was_nan) + { + if (code==CURVE3) i++; + else if (code==CURVE4) i+=2; + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + CGContextMoveToPoint(cr, x1, y1); + was_nan = false; + } + } + else if (code==MOVETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + CGContextMoveToPoint(cr, x1, y1); + was_nan = false; + } + } + else if (code==LINETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + CGContextAddLineToPoint(cr, x1, y1); + was_nan = false; + } + } + else if (code==CURVE3) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2)) + { + was_nan = true; + } + else + { + CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2); + was_nan = false; + } + } + else if (code==CURVE4) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2) || isnan(x3) || isnan(y3)) + { + was_nan = true; + } + else + { + CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3); + was_nan = false; + } + } + } + Py_DECREF(codelist); + } + + Py_DECREF(coordinates); + + CGContextClip(cr); + Py_INCREF(Py_None); + return Py_None; +} + +static BOOL +_set_dashes(CGContextRef cr, PyObject* offset, PyObject* dashes) +{ + float phase = 0.0; if (offset!=Py_None) { if (PyFloat_Check(offset)) phase = PyFloat_AsDouble(offset); @@ -439,7 +649,7 @@ { PyErr_SetString(PyExc_TypeError, "offset should be a floating point value"); - return NULL; + return false; } } @@ -451,7 +661,7 @@ { PyErr_SetString(PyExc_TypeError, "dashes should be a tuple or a list"); - return NULL; + return false; } int n = PyTuple_GET_SIZE(dashes); int i; @@ -460,7 +670,7 @@ { PyErr_SetString(PyExc_MemoryError, "Failed to store dashes"); Py_DECREF(dashes); - return NULL; + return false; } for (i = 0; i < n; i++) { @@ -476,7 +686,7 @@ { free(lengths); PyErr_SetString(PyExc_TypeError, "Failed to read dashes"); - return NULL; + return false; } CGContextSetLineDash(cr, phase, lengths, n); free(lengths); @@ -484,29 +694,40 @@ else CGContextSetLineDash(cr, phase, NULL, 0); - Py_INCREF(Py_None); - return Py_None; + return true; } static PyObject* -GraphicsContext_set_foreground(GraphicsContext* self, PyObject* args, PyObject* keywords) -{ float r, g, b; - PyObject* fg; - int isRGB = 0; - static char* kwlist[] = {"fg", "isRGB", NULL}; - if(!PyArg_ParseTupleAndKeywords(args, keywords, "O|i", kwlist, - &fg, &isRGB)) return NULL; - if (isRGB) +GraphicsContext_set_dashes (GraphicsContext* self, PyObject* args) +{ + PyObject* offset; + PyObject* dashes; + + if (!PyArg_ParseTuple(args, "OO", &offset, &dashes)) return NULL; + + CGContextRef cr = self->cr; + if (!cr) { - if(!PyArg_ParseTuple(fg, "fff", &r, &g, &b)) return NULL; + PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); + return NULL; } + + BOOL ok = _set_dashes(cr, offset, dashes); + if (ok) + { + Py_INCREF(Py_None); + return Py_None; + } else - { fg = PyObject_CallFunctionObjArgs(self->converter, fg, NULL); - if(!fg) return NULL; - if(!PyArg_ParseTuple(fg, "fff", &r, &g, &b)) return NULL; - Py_DECREF(fg); - } + return NULL; +} +static PyObject* +GraphicsContext_set_foreground(GraphicsContext* self, PyObject* args) +{ + float r, g, b; + if(!PyArg_ParseTuple(args, "(fff)", &r, &g, &b)) return NULL; + CGContextRef cr = self->cr; if (!cr) { @@ -538,7 +759,7 @@ return Py_None; } -static void drawHatch (void *info, CGContextRef cr) +static void _draw_hatch (void *info, CGContextRef cr) { int i; @@ -603,12 +824,20 @@ Py_DECREF(string); } +static void _release_hatch(void* info) +{ + PyObject* hatches = info; + Py_DECREF(hatches); +} + static PyObject* GraphicsContext_set_hatch(GraphicsContext* self, PyObject* args) { PyObject* hatches; const float size = 12.0; - static const CGPatternCallbacks callbacks = {0, &drawHatch, NULL}; + static const CGPatternCallbacks callbacks = {0, + &_draw_hatch, + &_release_hatch}; CGContextRef cr = self->cr; if (!cr) @@ -687,122 +916,828 @@ return Py_None; } -static PyObject* -GraphicsContext_moveto(GraphicsContext* self, PyObject* args) -{ - float x; - float y; - - if(!PyArg_ParseTuple(args, "(ff)", &x, &y)) return NULL; - - CGContextRef cr = self->cr; - if (!cr) +static int +_convert_affine_transform(PyObject* object, CGAffineTransform* transform) +/* Reads a Numpy affine transformation matrix and returns + * a CGAffineTransform. + */ +{ + PyArrayObject* matrix = NULL; + if (object==Py_None) { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); - return NULL; + PyErr_SetString(PyExc_ValueError, + "Found affine transformation matrix equal to None"); + return 0; } - CGContextMoveToPoint(cr, x, y); + matrix = (PyArrayObject*) PyArray_FromObject(object, NPY_DOUBLE, 2, 2); + if (!matrix) + { + PyErr_SetString(PyExc_ValueError, + "Invalid affine transformation matrix"); + return 0; + } + if (PyArray_NDIM(matrix) != 2 || PyArray_DIM(matrix, 0) != 3 || PyArray_DIM(matrix, 1) != 3) + { + Py_DECREF(matrix); + PyErr_SetString(PyExc_ValueError, + "Affine transformation matrix has invalid dimensions"); + return 0; + } - Py_INCREF(Py_None); - return Py_None; + size_t stride0 = (size_t)PyArray_STRIDE(matrix, 0); + size_t stride1 = (size_t)PyArray_STRIDE(matrix, 1); + char* row0 = PyArray_BYTES(matrix); + char* row1 = row0 + stride0; + + double a = *(double*)(row0); + row0 += stride1; + double c = *(double*)(row0); + row0 += stride1; + double e = *(double*)(row0); + double b = *(double*)(row1); + row1 += stride1; + double d = *(double*)(row1); + row1 += stride1; + double f = *(double*)(row1); + *transform = CGAffineTransformMake(a, b, c, d, e, f); + + Py_DECREF(matrix); + return 1; } -static PyObject* -GraphicsContext_lineto(GraphicsContext* self, PyObject* args) +static int +_draw_path(CGContextRef cr, PyObject* path, CGAffineTransform affine) { - float x; - float y; + CGPoint point; - if(!PyArg_ParseTuple(args, "(ff)", &x, &y)) return NULL; + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); + return -1; + } + Py_DECREF(vertices); /* Don't keep a reference here */ - CGContextRef cr = self->cr; - if (!cr) + PyObject* codes = PyObject_GetAttrString(path, "codes"); + if (codes==NULL) { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); - return NULL; + PyErr_SetString(PyExc_AttributeError, "path has no codes"); + return -1; } - CGContextAddLineToPoint(cr, x, y); + Py_DECREF(codes); /* Don't keep a reference here */ - Py_INCREF(Py_None); - return Py_None; + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return -1; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return -1; + } + + npy_intp n = PyArray_DIM(coordinates, 0); + + if (n==0) /* Nothing to do here */ + { + Py_DECREF(coordinates); + return 0; + } + + PyArrayObject* codelist = NULL; + if (codes != Py_None) + { + codelist = (PyArrayObject*)PyArray_FromObject(codes, + NPY_UINT8, 1, 1); + if (!codelist) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid codes array"); + return -1; + } + } + + if (codelist==NULL) + { + npy_intp i; + npy_uint8 code = MOVETO; + for (i = 0; i < n; i++) + { + point.x = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 0)); + point.y = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 1)); + if (isnan(point.x) || isnan(point.y)) + { + code = MOVETO; + } + else + { + point = CGPointApplyAffineTransform(point, affine); + switch (code) + { + case MOVETO: + CGContextMoveToPoint(cr, point.x, point.y); + break; + case LINETO: + CGContextAddLineToPoint(cr, point.x, point.y); + break; + } + code = LINETO; + } + } + } + else + { + npy_intp i = 0; + BOOL was_nan = false; + npy_uint8 code; + CGFloat x1, y1, x2, y2, x3, y3; + while (i < n) + { + code = *(npy_uint8*)PyArray_GETPTR1(codelist, i); + if (code == CLOSEPOLY) + { + CGContextClosePath(cr); + i++; + } + else if (code == STOP) + { + break; + } + else if (was_nan) + { + if (code==CURVE3) i++; + else if (code==CURVE4) i+=2; + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextMoveToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==MOVETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextMoveToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==LINETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextAddLineToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==CURVE3) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + x1 = point.x; + y1 = point.y; + point.x = x2; + point.y = y2; + point = CGPointApplyAffineTransform(point, affine); + x2 = point.x; + y2 = point.y; + CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2); + was_nan = false; + } + } + else if (code==CURVE4) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2) || isnan(x3) || isnan(y3)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + x1 = point.x; + y1 = point.y; + point.x = x2; + point.y = y2; + point = CGPointApplyAffineTransform(point, affine); + x2 = point.x; + y2 = point.y; + point.x = x3; + point.y = y3; + point = CGPointApplyAffineTransform(point, affine); + x3 = point.x; + y3 = point.y; + CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3); + was_nan = false; + } + } + } + } + + Py_DECREF(coordinates); + Py_XDECREF(codelist); + return n; } static PyObject* -GraphicsContext_curve3(GraphicsContext* self, PyObject* args) -{ - float cpx; - float cpy; - float x; - float y; +GraphicsContext_draw_path (GraphicsContext* self, PyObject* args) +{ + PyObject* path; + PyObject* transform; + PyObject* rgbFace; + int ok; + CGContextRef cr = self->cr; + if (!cr) { PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - if(!PyArg_ParseTuple(args, "(ffff)", &cpx, - &cpy, - &x, - &y)) return NULL; + if(!PyArg_ParseTuple(args, "OO|O", + &path, + &transform, + &rgbFace)) return NULL; - CGContextAddQuadCurveToPoint(cr, cpx, cpy, x, y); + if(rgbFace==Py_None) rgbFace = NULL; + CGAffineTransform affine; + ok = _convert_affine_transform(transform, &affine); + if (!ok) return NULL; + + int n = _draw_path(cr, path, affine); + if (n==-1) return NULL; + + if (n > 0) + { + if(rgbFace) + { + float r, g, b; + ok = PyArg_ParseTuple(rgbFace, "fff", &r, &g, &b); + if (!ok) + { + return NULL; + } + CGContextSaveGState(cr); + if(self->pattern) + { + float components[4]; + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = 1.0; + CGContextSetFillPattern(cr, self->pattern, components); + CGPatternRelease(self->pattern); + self->pattern = nil; + } + else CGContextSetRGBFillColor(cr, r, g, b, 1.0); + CGContextDrawPath(cr, kCGPathFillStroke); + CGContextRestoreGState(cr); + } + else CGContextStrokePath(cr); + } + Py_INCREF(Py_None); return Py_None; } static PyObject* -GraphicsContext_curve4 (GraphicsContext* self, PyObject* args) +GraphicsContext_draw_markers (GraphicsContext* self, PyObject* args) { - float cp1x; - float cp1y; - float cp2x; - float cp2y; - float x; - float y; + PyObject* marker_path; + PyObject* marker_transform; + PyObject* path; + PyObject* transform; + PyObject* rgbFace; + int ok; + float r, g, b; + CGContextRef cr = self->cr; + if (!cr) { PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - if(!PyArg_ParseTuple(args, "(ffffff)", &cp1x, - &cp1y, - &cp2x, - &cp2y, - &x, - &y)) return NULL; + if(!PyArg_ParseTuple(args, "OOOO|O", + &marker_path, + &marker_transform, + &path, + &transform, + &rgbFace)) return NULL; - CGContextAddCurveToPoint(cr, cp1x, cp1y, cp2x, cp2y, x, y); + if(rgbFace==Py_None) rgbFace = NULL; + if (rgbFace) + { + ok = PyArg_ParseTuple(rgbFace, "fff", &r, &g, &b); + if (!ok) + { + return NULL; + } + if(self->pattern) + { + float components[4]; + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = 1.0; + CGContextSetFillPattern(cr, self->pattern, components); + CGPatternRelease(self->pattern); + self->pattern = nil; + } + else CGContextSetRGBFillColor(cr, r, g, b, 1.0); + } + + CGAffineTransform affine; + ok = _convert_affine_transform(transform, &affine); + if (!ok) return NULL; + + CGAffineTransform marker_affine; + ok = _convert_affine_transform(marker_transform, &marker_affine); + if (!ok) return NULL; + + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); + return NULL; + } + Py_DECREF(vertices); /* Don't keep a reference here */ + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return NULL; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return NULL; + } + + npy_intp i; + npy_intp n = PyArray_DIM(coordinates, 0); + CGPoint point; + CGAffineTransform t; + int m = 0; + for (i = 0; i < n; i++) + { + point.x = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + point.y = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + point = CGPointApplyAffineTransform(point, affine); + t = marker_affine; + t.tx += point.x; + t.ty += point.y; + m = _draw_path(cr, marker_path, t); + + if (m > 0) + { + if(rgbFace) CGContextDrawPath(cr, kCGPathFillStroke); + else CGContextStrokePath(cr); + } + } + + Py_DECREF(coordinates); + Py_INCREF(Py_None); return Py_None; } +static BOOL _clip(CGContextRef cr, PyObject* object) +{ + if (object == Py_None) return true; + + PyArrayObject* array = NULL; + array = (PyArrayObject*) PyArray_FromObject(object, PyArray_DOUBLE, 2, 2); + if (!array) + { + PyErr_SetString(PyExc_ValueError, "failed to read clipping bounding box"); + return false; + } + + if (PyArray_NDIM(array)!=2 || PyArray_DIM(array, 0)!=2 || PyArray_DIM(array, 1)!=2) + { + Py_DECREF(array); + PyErr_SetString(PyExc_ValueError, "clipping bounding box should be a 2x2 array"); + return false; + } + + const double l = *(double*)PyArray_GETPTR2(array, 0, 0); + const double b = *(double*)PyArray_GETPTR2(array, 0, 1); + const double r = *(double*)PyArray_GETPTR2(array, 1, 0); + const double t = *(double*)PyArray_GETPTR2(array, 1, 1); + + Py_DECREF(array); + + CGRect rect; + rect.origin.x = (CGFloat) l; + rect.origin.y = (CGFloat) b; + rect.size.width = (CGFloat) (r-l); + rect.size.height = (CGFloat) (t-b); + + CGContextClipToRect(cr, rect); + + return true; +} + + static PyObject* -GraphicsContext_closepoly (GraphicsContext* self) +GraphicsContext_draw_path_collection (GraphicsContext* self, PyObject* args) { + PyObject* master_transform_obj; + PyObject* cliprect; + PyObject* clippath; + PyObject* clippath_transform; + PyObject* paths; + PyObject* transforms_obj; + PyObject* offsets_obj; + PyObject* offset_transform_obj; + PyObject* facecolors_obj; + PyObject* edgecolors_obj; + PyObject* linewidths; + PyObject* linestyles; + PyObject* antialiaseds; + CGContextRef cr = self->cr; + if (!cr) { PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } - CGContextClosePath(cr); + if(!PyArg_ParseTuple(args, "OOOOOOOOOOOOO", &master_transform_obj, + &cliprect, + &clippath, + &clippath_transform, + &paths, + &transforms_obj, + &offsets_obj, + &offset_transform_obj, + &facecolors_obj, + &edgecolors_obj, + &linewidths, + &linestyles, + &antialiaseds)) + return NULL; + CGContextSaveGState(cr); + + CGAffineTransform transform; + CGAffineTransform master_transform; + CGAffineTransform offset_transform; + CGAffineTransform* transforms = NULL; + + if (!_convert_affine_transform(master_transform_obj, &master_transform)) return NULL; + if (!_convert_affine_transform(offset_transform_obj, &offset_transform)) return NULL; + + if (!_clip(cr, cliprect)) return NULL; + if (clippath!=Py_None) + { + if (!_convert_affine_transform(clippath_transform, &transform)) return NULL; + int n = _draw_path(cr, clippath, transform); + if (n==-1) return NULL; + else if (n > 0) CGContextClip(cr); + } + + PyArrayObject* offsets = NULL; + PyArrayObject* facecolors = NULL; + PyArrayObject* edgecolors = NULL; + + /* ------------------- Check offsets array ---------------------------- */ + + offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj, NPY_DOUBLE, 0, 2); + if (!offsets || + (PyArray_NDIM(offsets)==2 && PyArray_DIM(offsets, 1)!=2) || + (PyArray_NDIM(offsets)==1 && PyArray_DIM(offsets, 0)!=0)) + { + PyErr_SetString(PyExc_ValueError, "Offsets array must be Nx2"); + goto error; + } + + /* ------------------- Check facecolors array ------------------------- */ + + facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj, + NPY_DOUBLE, 1, 2); + if (!facecolors || + (PyArray_NDIM(facecolors)==1 && PyArray_DIM(facecolors, 0)!=0) || + (PyArray_NDIM(facecolors)==2 && PyArray_DIM(facecolors, 1)!=4)) + { + PyErr_SetString(PyExc_ValueError, "Facecolors must by a Nx4 numpy array or empty"); + goto error; + } + + /* ------------------- Check edgecolors array ------------------------- */ + + edgecolors = (PyArrayObject*)PyArray_FromObject(edgecolors_obj, + NPY_DOUBLE, 1, 2); + if (!edgecolors || + (PyArray_NDIM(edgecolors)==1 && PyArray_DIM(edgecolors, 0)!=0) || + (PyArray_NDIM(edgecolors)==2 && PyArray_DIM(edgecolors, 1)!=4)) + { + PyErr_SetString(PyExc_ValueError, "Edgecolors must by a Nx4 numpy array or empty"); + goto error; + } + + /* ------------------- Check the other arguments ---------------------- */ + + if (!PySequence_Check(paths)) + { + PyErr_SetString(PyExc_ValueError, "paths must be a sequence object"); + goto error; + } + if (!PySequence_Check(transforms_obj)) + { + PyErr_SetString(PyExc_ValueError, "transforms must be a sequence object"); + goto error; + } + if (!PySequence_Check(linewidths)) + { + PyErr_SetString(PyExc_ValueError, "linewidths must be a sequence object"); + goto error; + } + if (!PySequence_Check(linestyles)) + { + PyErr_SetString(PyExc_ValueError, "linestyles must be a sequence object"); + goto error; + } + if (!PySequence_Check(antialiaseds)) + { + PyErr_SetString(PyExc_ValueError, "antialiaseds must be a sequence object"); + goto error; + } + + size_t Npaths = (size_t) PySequence_Size(paths); + size_t Noffsets = (size_t) PyArray_DIM(offsets, 0); + size_t N = Npaths > Noffsets ? Npaths : Noffsets; + size_t Ntransforms = (size_t) PySequence_Size(transforms_obj); + size_t Nfacecolors = (size_t) PyArray_DIM(facecolors, 0); + size_t Nedgecolors = (size_t) PyArray_DIM(edgecolors, 0); + size_t Nlinewidths = (size_t) PySequence_Size(linewidths); + size_t Nlinestyles = (size_t) PySequence_Size(linestyles); + size_t Naa = (size_t) PySequence_Size(antialiaseds); + if (N < Ntransforms) Ntransforms = N; + if (N < Nlinestyles) Nlinestyles = N; + if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0) + { + goto success; + } + + size_t i = 0; + + /* Convert all of the transforms up front */ + if (Ntransforms > 0) + { + transforms = malloc(Ntransforms*sizeof(CGAffineTransform)); + if (!transforms) goto error; + for (i = 0; i < Ntransforms; i++) + { + PyObject* transform_obj = PySequence_ITEM(transforms_obj, i); + if(!_convert_affine_transform(transform_obj, &transforms[i])) goto error; + transforms[i] = CGAffineTransformConcat(transforms[i], master_transform); + } + } + + CGPoint offset; + PyObject* path; + + /* Preset graphics context properties if possible */ + if (Naa==1) + { + switch(PyObject_IsTrue(PySequence_ITEM(antialiaseds, 0))) + { + case 1: CGContextSetShouldAntialias(cr, true); break; + case 0: CGContextSetShouldAntialias(cr, false); break; + case -1: + { + PyErr_SetString(PyExc_ValueError, + "Failed to read antialiaseds array"); + goto error; + } + } + } + + if (Nlinewidths==1) + { + double linewidth = PyFloat_AsDouble(PySequence_ITEM(linewidths, 0)); + CGContextSetLineWidth(cr, (CGFloat)linewidth); + } + else if (Nlinewidths==0) + CGContextSetLineWidth(cr, 0.0); + + if (Nlinestyles==1) + { + PyObject* offset; + PyObject* dashes; + PyObject* linestyle = PySequence_ITEM(linestyles, 0); + if (!PyArg_ParseTuple(linestyle, "OO", &offset, &dashes)) goto error; + if (!_set_dashes(cr, offset, dashes)) goto error; + } + + if (Nedgecolors==1) + { + const double r = *(double*)PyArray_GETPTR2(edgecolors, 0, 0); + const double g = *(double*)PyArray_GETPTR2(edgecolors, 0, 1); + const double b = *(double*)PyArray_GETPTR2(edgecolors, 0, 2); + const double a = *(double*)PyArray_GETPTR2(edgecolors, 0, 3); + CGContextSetRGBStrokeColor(cr, r, g, b, a); + } + + if (Nfacecolors==1) + { + const double r = *(double*)PyArray_GETPTR2(facecolors, 0, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, 0, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, 0, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, 0, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + } + + for (i = 0; i < N; i++) + { + + if (Ntransforms) + { + transform = transforms[i % Ntransforms]; + } + else + { + transform = master_transform; + } + + if (Noffsets) + { + offset.x = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0)); + offset.y = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1)); + offset = CGPointApplyAffineTransform(offset, offset_transform); + transform.tx += offset.x; + transform.ty += offset.y; + } + + if (Naa > 1) + { + switch(PyObject_IsTrue(PySequence_ITEM(antialiaseds, i % Naa))) + { + case 1: CGContextSetShouldAntialias(cr, true); break; + case 0: CGContextSetShouldAntialias(cr, false); break; + case -1: + { + PyErr_SetString(PyExc_ValueError, + "Failed to read antialiaseds array"); + goto error; + } + } + } + + path = PySequence_ITEM(paths, i % Npaths); + int n = _draw_path(cr, path, transform); + if (n==-1) goto error; + else if (n==0) continue; + + if (Nlinewidths > 1) + { + double linewidth = PyFloat_AsDouble(PySequence_ITEM(linewidths, i % Nlinewidths)); + CGContextSetLineWidth(cr, (CGFloat)linewidth); + } + + if (Nlinestyles > 1) + { + PyObject* offset; + PyObject* dashes; + PyObject* linestyle = PySequence_ITEM(linestyles, i % Nlinestyles); + if (!PyArg_ParseTuple(linestyle, "OO", &offset, &dashes)) goto error; + if (!_set_dashes(cr, offset, dashes)) goto error; + } + + if (Nedgecolors > 1) + { + npy_intp fi = i % Nedgecolors; + const double r = *(double*)PyArray_GETPTR2(edgecolors, fi, 0); + const double g = *(double*)PyArray_GETPTR2(edgecolors, fi, 1); + const double b = *(double*)PyArray_GETPTR2(edgecolors, fi, 2); + const double a = *(double*)PyArray_GETPTR2(edgecolors, fi, 3); + CGContextSetRGBStrokeColor(cr, r, g, b, a); + } + + if (Nfacecolors > 1) + { + npy_intp fi = i % Nfacecolors; + const double r = *(double*)PyArray_GETPTR2(facecolors, fi, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, fi, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, fi, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, fi, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + CGContextDrawPath(cr, kCGPathFillStroke); + } + else if (Nfacecolors==1) + CGContextDrawPath(cr, kCGPathFillStroke); + else + CGContextStrokePath(cr); + } + +success: + CGContextRestoreGState(cr); + if (transforms) free(transforms); + Py_DECREF(offsets); + Py_DECREF(facecolors); + Py_DECREF(edgecolors); + Py_INCREF(Py_None); return Py_None; + +error: + CGContextRestoreGState(cr); + if (transforms) free(transforms); + Py_XDECREF(offsets); + Py_XDECREF(facecolors); + Py_XDECREF(edgecolors); + + return NULL; } static PyObject* -GraphicsContext_stroke (GraphicsContext* self, PyObject* args) +GraphicsContext_draw_quad_mesh (GraphicsContext* self, PyObject* args) { - PyObject* color; + PyObject* master_transform_obj; + PyObject* cliprect; + PyObject* clippath; + PyObject* clippath_transform; + int meshWidth; + int meshHeight; + PyObject* vertices; + PyObject* offsets_obj; + PyObject* offset_transform_obj; + PyObject* facecolors_obj; + int antialiased; + int showedges; + CGContextRef cr = self->cr; if (!cr) @@ -811,35 +1746,219 @@ return NULL; } - if(!PyArg_ParseTuple(args, "O", &color)) return NULL; + if(!PyArg_ParseTuple(args, "OOOOiiOOOOii", + &master_transform_obj, + &cliprect, + &clippath, + &clippath_transform, + &meshWidth, + &meshHeight, + &vertices, + &offsets_obj, + &offset_transform_obj, + &facecolors_obj, + &antialiased, + &showedges)) return NULL; - if(color!=Py_None) + PyArrayObject* offsets = NULL; + PyArrayObject* facecolors = NULL; + + CGAffineTransform transform; + CGAffineTransform master_transform; + CGAffineTransform offset_transform; + + if (!_convert_affine_transform(master_transform_obj, &master_transform)) + return NULL; + if (!_convert_affine_transform(offset_transform_obj, &offset_transform)) + return NULL; + + /* clipping */ + + if (!_clip(cr, cliprect)) return NULL; + if (clippath!=Py_None) { - float r, g, b; - if(!PyArg_ParseTuple(color, "fff", &r, &g, &b)) return NULL; - if(self->pattern) + if (!_convert_affine_transform(clippath_transform, &transform)) + return NULL; + int n = _draw_path(cr, clippath, transform); + if (n==-1) return NULL; + else if (n > 0) CGContextClip(cr); + } + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 3, 3); + if (!coordinates || + PyArray_NDIM(coordinates) != 3 || PyArray_DIM(coordinates, 2) != 2) + { + PyErr_SetString(PyExc_ValueError, "Invalid coordinates array"); + goto error; + } + + /* ------------------- Check offsets array ---------------------------- */ + + offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj, NPY_DOUBLE, 0, 2); + if (!offsets || + (PyArray_NDIM(offsets)==2 && PyArray_DIM(offsets, 1)!=2) || + (PyArray_NDIM(offsets)==1 && PyArray_DIM(offsets, 0)!=0)) + { + PyErr_SetString(PyExc_ValueError, "Offsets array must be Nx2"); + goto error; + } + + /* ------------------- Check facecolors array ------------------------- */ + + facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj, + NPY_DOUBLE, 1, 2); + if (!facecolors || + (PyArray_NDIM(facecolors)==1 && PyArray_DIM(facecolors, 0)!=0) || + (PyArray_NDIM(facecolors)==2 && PyArray_DIM(facecolors, 1)!=4)) + { + PyErr_SetString(PyExc_ValueError, "Facecolors must by a Nx4 numpy array or empty"); + goto error; + } + + /* ------------------- Check the other arguments ---------------------- */ + + size_t Noffsets = (size_t) PyArray_DIM(offsets, 0); + size_t Npaths = meshWidth * meshHeight; + size_t Nfacecolors = (size_t) PyArray_DIM(facecolors, 0); + if ((Nfacecolors == 0 && !showedges) || Npaths == 0) + { + /* Nothing to do here */ + goto success; + } + + size_t i = 0; + size_t iw = 0; + size_t ih = 0; + + CGPoint offset; + + /* Preset graphics context properties if possible */ + if (antialiased) CGContextSetShouldAntialias(cr, true); + else CGContextSetShouldAntialias(cr, false); + + CGContextSetLineWidth(cr, 0.0); + + if (Nfacecolors==1) + { + const double r = *(double*)PyArray_GETPTR2(facecolors, 0, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, 0, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, 0, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, 0, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + if (antialiased && !showedges) { - float components[4]; - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = 1.0; - CGContextSetFillPattern(cr, self->pattern, components); - CGPatternRelease (self->pattern); - self->pattern = nil; + CGContextSetRGBStrokeColor(cr, r, g, b, a); } - else CGContextSetRGBFillColor(cr, r, g, b, 1.0); - CGContextDrawPath(cr, kCGPathFillStroke); } - else CGContextStrokePath(cr); + if (showedges) + { + CGContextSetRGBStrokeColor(cr, 0, 0, 0, 1); + } + + for (ih = 0; ih < meshHeight; ih++) + { + for (iw = 0; iw < meshWidth; iw++, i++) + { + + transform = master_transform; + + if (Noffsets) + { + offset.x = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0)); + offset.y = (CGFloat) (*(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1)); + offset = CGPointApplyAffineTransform(offset, offset_transform); + transform.tx += offset.x; + transform.ty += offset.y; + } + + CGPoint p; + CGPoint points[4]; + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[0] = CGPointApplyAffineTransform(p, transform); + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw+1, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih, iw+1, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[1] = CGPointApplyAffineTransform(p, transform); + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw+1, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw+1, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[2] = CGPointApplyAffineTransform(p, transform); + + p.x = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw, 0)); + p.y = (CGFloat)(*(double*)PyArray_GETPTR3(coordinates, ih+1, iw, 1)); + if (isnan(p.x) || isnan(p.y)) continue; + points[3] = CGPointApplyAffineTransform(p, transform); + + CGContextMoveToPoint(cr, points[3].x, points[3].y); + CGContextAddLines(cr, points, 4); + + if (Nfacecolors > 1) + { + npy_intp fi = i % Nfacecolors; + const double r = *(double*)PyArray_GETPTR2(facecolors, fi, 0); + const double g = *(double*)PyArray_GETPTR2(facecolors, fi, 1); + const double b = *(double*)PyArray_GETPTR2(facecolors, fi, 2); + const double a = *(double*)PyArray_GETPTR2(facecolors, fi, 3); + CGContextSetRGBFillColor(cr, r, g, b, a); + if (showedges) + { + CGContextDrawPath(cr, kCGPathFillStroke); + } + else if (antialiased) + { + CGContextSetRGBStrokeColor(cr, r, g, b, a); + CGContextDrawPath(cr, kCGPathFillStroke); + } + else + { + CGContextFillPath(cr); + } + } + else if (Nfacecolors==1) + { + if (showedges || antialiased) + { + CGContextDrawPath(cr, kCGPathFillStroke); + } + else + { + CGContextFillPath(cr); + } + } + else if (showedges) + { + CGContextStrokePath(cr); + } + } + } + +success: + Py_DECREF(offsets); + Py_DECREF(facecolors); + Py_DECREF(coordinates); + Py_INCREF(Py_None); return Py_None; + +error: + Py_XDECREF(offsets); + Py_XDECREF(facecolors); + Py_XDECREF(coordinates); + return NULL; } + static ATSFontRef setfont(CGContextRef cr, PyObject* family, float size, const char weight[], - const char style[]) + const ch... [truncated message content] |
From: <lee...@us...> - 2008-12-18 09:02:15
|
Revision: 6659 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6659&view=rev Author: leejjoon Date: 2008-12-18 09:02:11 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Merged revisions 6658 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6658 | leejjoon | 2008-12-18 03:55:50 -0500 (Thu, 18 Dec 2008) | 1 line fix dpi-dependent behavior of text bbox & annotate arrow ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/text.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6653 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6658 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-18 08:55:50 UTC (rev 6658) +++ trunk/matplotlib/CHANGELOG 2008-12-18 09:02:11 UTC (rev 6659) @@ -1,3 +1,6 @@ +2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate + -JJL + 2008-12-17 Add group id support in artist. Two examples which demostrate svg filter are added. -JJL Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2008-12-18 08:55:50 UTC (rev 6658) +++ trunk/matplotlib/lib/matplotlib/text.py 2008-12-18 09:02:11 UTC (rev 6659) @@ -367,7 +367,8 @@ def update_bbox_position_size(self, renderer): - """ Update the location and the size of the bbox. This method + """ + Update the location and the size of the bbox. This method should be used when the position and size of the bbox needs to be updated before actually drawing the bbox. """ @@ -395,8 +396,8 @@ tr = mtransforms.Affine2D().rotate(theta) tr = tr.translate(posx+x_box, posy+y_box) self._bbox_patch.set_transform(tr) - fontsize = renderer.points_to_pixels(self.get_size()) - self._bbox_patch.set_mutation_scale(fontsize) + fontsize_in_pixel = renderer.points_to_pixels(self.get_size()) + self._bbox_patch.set_mutation_scale(fontsize_in_pixel) #self._bbox_patch.draw(renderer) else: @@ -434,7 +435,8 @@ tr = mtransforms.Affine2D().rotate(theta) tr = tr.translate(posx+x_box, posy+y_box) self._bbox_patch.set_transform(tr) - self._bbox_patch.set_mutation_scale(self.get_size()) + fontsize_in_pixel = renderer.points_to_pixels(self.get_size()) + self._bbox_patch.set_mutation_scale(fontsize_in_pixel) self._bbox_patch.draw(renderer) @@ -1563,6 +1565,7 @@ self.arrow_patch.set_positions((ox0, oy0), (ox1,oy1)) mutation_scale = d.pop("mutation_scale", self.get_size()) + mutation_scale = renderer.points_to_pixels(mutation_scale) self.arrow_patch.set_mutation_scale(mutation_scale) if self._bbox_patch: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-18 08:55:53
|
Revision: 6658 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6658&view=rev Author: leejjoon Date: 2008-12-18 08:55:50 +0000 (Thu, 18 Dec 2008) Log Message: ----------- fix dpi-dependent behavior of text bbox & annotate arrow Modified Paths: -------------- branches/v0_98_5_maint/CHANGELOG branches/v0_98_5_maint/lib/matplotlib/text.py Modified: branches/v0_98_5_maint/CHANGELOG =================================================================== --- branches/v0_98_5_maint/CHANGELOG 2008-12-17 23:37:18 UTC (rev 6657) +++ branches/v0_98_5_maint/CHANGELOG 2008-12-18 08:55:50 UTC (rev 6658) @@ -1,3 +1,6 @@ +2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate + -JJL + 2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL ====================================================================== Modified: branches/v0_98_5_maint/lib/matplotlib/text.py =================================================================== --- branches/v0_98_5_maint/lib/matplotlib/text.py 2008-12-17 23:37:18 UTC (rev 6657) +++ branches/v0_98_5_maint/lib/matplotlib/text.py 2008-12-18 08:55:50 UTC (rev 6658) @@ -367,7 +367,8 @@ def update_bbox_position_size(self, renderer): - """ Update the location and the size of the bbox. This method + """ + Update the location and the size of the bbox. This method should be used when the position and size of the bbox needs to be updated before actually drawing the bbox. """ @@ -395,8 +396,8 @@ tr = mtransforms.Affine2D().rotate(theta) tr = tr.translate(posx+x_box, posy+y_box) self._bbox_patch.set_transform(tr) - fontsize = renderer.points_to_pixels(self.get_size()) - self._bbox_patch.set_mutation_scale(fontsize) + fontsize_in_pixel = renderer.points_to_pixels(self.get_size()) + self._bbox_patch.set_mutation_scale(fontsize_in_pixel) #self._bbox_patch.draw(renderer) else: @@ -434,7 +435,8 @@ tr = mtransforms.Affine2D().rotate(theta) tr = tr.translate(posx+x_box, posy+y_box) self._bbox_patch.set_transform(tr) - self._bbox_patch.set_mutation_scale(self.get_size()) + fontsize_in_pixel = renderer.points_to_pixels(self.get_size()) + self._bbox_patch.set_mutation_scale(fontsize_in_pixel) self._bbox_patch.draw(renderer) @@ -1557,6 +1559,7 @@ self.arrow_patch.set_positions((ox0, oy0), (ox1,oy1)) mutation_scale = d.pop("mutation_scale", self.get_size()) + mutation_scale = renderer.points_to_pixels(mutation_scale) self.arrow_patch.set_mutation_scale(mutation_scale) if self._bbox_patch: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |