|
From: <md...@us...> - 2009-01-29 16:51:17
|
Revision: 6847
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6847&view=rev
Author: mdboom
Date: 2009-01-29 16:51:12 +0000 (Thu, 29 Jan 2009)
Log Message:
-----------
Rework the nan-handling/clipping/quantizing/simplification
framework so each is an independent part of a pipeline.
Expose the C++-implementation of all of this so it can be
used from all Python backends. Add rcParam
"path.simplify_threshold" to control the threshold of
similarity below which vertices will be removed.
Modified Paths:
--------------
trunk/matplotlib/CHANGELOG
trunk/matplotlib/examples/api/quad_bezier.py
trunk/matplotlib/examples/pylab_examples/simplification_clipping_test.py
trunk/matplotlib/lib/matplotlib/backend_bases.py
trunk/matplotlib/lib/matplotlib/backends/backend_cairo.py
trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
trunk/matplotlib/lib/matplotlib/backends/backend_ps.py
trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
trunk/matplotlib/lib/matplotlib/backends/backend_wx.py
trunk/matplotlib/lib/matplotlib/config/mplconfig.py
trunk/matplotlib/lib/matplotlib/config/rcsetup.py
trunk/matplotlib/lib/matplotlib/path.py
trunk/matplotlib/lib/matplotlib/rcsetup.py
trunk/matplotlib/matplotlibrc.template
trunk/matplotlib/src/_backend_agg.cpp
trunk/matplotlib/src/_backend_agg.h
trunk/matplotlib/src/_macosx.m
trunk/matplotlib/src/_path.cpp
trunk/matplotlib/src/agg_py_path_iterator.h
Added Paths:
-----------
trunk/matplotlib/src/path_converters.h
Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/CHANGELOG 2009-01-29 16:51:12 UTC (rev 6847)
@@ -1,3 +1,10 @@
+2009-01-29 Rework the nan-handling/clipping/quantizing/simplification
+ framework so each is an independent part of a pipeline.
+ Expose the C++-implementation of all of this so it can be
+ used from all Python backends. Add rcParam
+ "path.simplify_threshold" to control the threshold of
+ similarity below which vertices will be removed.
+
2009-01-26 Improved tight bbox option of the savefig. - JJL
2009-01-26 Make curves and NaNs play nice together - MGD
Modified: trunk/matplotlib/examples/api/quad_bezier.py
===================================================================
--- trunk/matplotlib/examples/api/quad_bezier.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/examples/api/quad_bezier.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -3,10 +3,13 @@
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
+Path = mpath.Path
+
fig = plt.figure()
ax = fig.add_subplot(111)
pp1 = mpatches.PathPatch(
- mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)], [1, 3, 3, 5]),
+ Path([(0, 0), (1, 0), (1, 1), (0, 0)],
+ [Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY]),
fc="none", transform=ax.transData)
ax.add_patch(pp1)
Modified: trunk/matplotlib/examples/pylab_examples/simplification_clipping_test.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/simplification_clipping_test.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/examples/pylab_examples/simplification_clipping_test.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -1,4 +1,5 @@
from pylab import *
+import numpy as np
t = arange(0.0, 2.0, 0.01)
s = sin(2*pi*t)
@@ -7,5 +8,51 @@
ylim((-0.20, -0.28))
title('Should see four lines extending from bottom to top')
-grid(True)
+
+figure()
+
+x = np.array([1.0,2.0,3.0,2.0e5])
+y = np.arange(len(x))
+plot(x,y)
+xlim(xmin=2,xmax=6)
+title("Should be monotonically increasing")
+
+figure()
+
+x = np.array([0.0, 1.0, 0.0, -1.0, 0.0])
+y = np.array([1.0, 0.0, -1.0, 0.0, 1.0])
+plot(x, y)
+xlim(xmin=-0.6, xmax=0.6)
+ylim(ymin=-0.6, ymax=0.6)
+title("Diamond shape, with segments visible in all four corners")
+
+figure()
+
+np.random.seed(0)
+x = np.random.uniform(size=(5000,)) * 50
+
+rcParams['path.simplify'] = True
+p1 = plot(x,solid_joinstyle='round',linewidth=2.0)
+
+path = p1[0].get_path()
+transform = p1[0].get_transform()
+path = transform.transform_path(path)
+simplified = list(path.iter_segments(simplify=(800, 600)))
+
+title("Original length: %d, simplified length: %d" % (len(path.vertices), len(simplified)))
+
+figure()
+
+x = np.sin(np.linspace(0, np.pi * 2.0, 1000)) + np.random.uniform(size=(1000,)) * 0.01
+
+rcParams['path.simplify'] = True
+p1 = plot(x,solid_joinstyle='round',linewidth=2.0)
+
+path = p1[0].get_path()
+transform = p1[0].get_transform()
+path = transform.transform_path(path)
+simplified = list(path.iter_segments(simplify=(800, 600)))
+
+title("Original length: %d, simplified length: %d" % (len(path.vertices), len(simplified)))
+
show()
Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -99,8 +99,7 @@
want to override this method in order to draw the marker only
once and reuse it multiple times.
"""
- tpath = trans.transform_path(path)
- for vertices, codes in tpath.iter_segments():
+ for vertices, codes in path.iter_segments(trans, simplify=False):
if len(vertices):
x,y = vertices[-2:]
self.draw_path(gc, marker_path,
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_cairo.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_cairo.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_cairo.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -122,8 +122,8 @@
@staticmethod
- def convert_path(ctx, tpath):
- for points, code in tpath.iter_segments():
+ def convert_path(ctx, path, transform):
+ for points, code in path.iter_segments(transform):
if code == Path.MOVETO:
ctx.move_to(*points)
elif code == Path.LINETO:
@@ -145,10 +145,9 @@
ctx = gc.ctx
transform = transform + \
Affine2D().scale(1.0, -1.0).translate(0, self.height)
- tpath = transform.transform_path(path)
ctx.new_path()
- self.convert_path(ctx, tpath)
+ self.convert_path(ctx, path, transform)
self._fill_and_stroke(ctx, rgbFace, gc.get_alpha())
@@ -343,8 +342,7 @@
ctx = self.ctx
ctx.new_path()
affine = affine + Affine2D().scale(1.0, -1.0).translate(0.0, self.renderer.height)
- tpath = affine.transform_path(tpath)
- RendererCairo.convert_path(ctx, tpath)
+ RendererCairo.convert_path(ctx, path, affine)
ctx.clip()
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -415,10 +415,6 @@
self.endStream()
self.width, self.height = width, height
- if rcParams['path.simplify']:
- self.simplify = (width * 72, height * 72)
- else:
- self.simplify = None
contentObject = self.reserveObject('page contents')
thePage = { 'Type': Name('Page'),
'Parent': self.pagesObject,
@@ -1140,12 +1136,10 @@
self.endStream()
@staticmethod
- def pathOperations(path, transform, simplify=None):
- tpath = transform.transform_path(path)
-
+ def pathOperations(path, transform, clip=None):
cmds = []
last_points = None
- for points, code in tpath.iter_segments(simplify):
+ for points, code in path.iter_segments(transform, clip=clip):
if code == Path.MOVETO:
cmds.extend(points)
cmds.append(Op.moveto)
@@ -1164,8 +1158,12 @@
last_points = points
return cmds
- def writePath(self, path, transform):
- cmds = self.pathOperations(path, transform, self.simplify)
+ def writePath(self, path, transform, clip=False):
+ if clip:
+ clip = (0.0, 0.0, self.width * 72, self.height * 72)
+ else:
+ clip = None
+ cmds = self.pathOperations(path, transform, clip)
self.output(*cmds)
def reserveObject(self, name=''):
@@ -1282,7 +1280,7 @@
def draw_path(self, gc, path, transform, rgbFace=None):
self.check_gc(gc, rgbFace)
- stream = self.file.writePath(path, transform)
+ stream = self.file.writePath(path, transform, rgbFace is None)
self.file.output(self.gc.paint())
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
@@ -1292,11 +1290,10 @@
output = self.file.output
marker = self.file.markerObject(
marker_path, marker_trans, fillp, self.gc._linewidth)
- tpath = trans.transform_path(path)
output(Op.gsave)
lastx, lasty = 0, 0
- for vertices, code in tpath.iter_segments():
+ for vertices, code in path.iter_segments(trans, simplify=False):
if len(vertices):
x, y = vertices[-2:]
dx, dy = x - lastx, y - lasty
@@ -1796,9 +1793,9 @@
if self._cliprect != cliprect:
cmds.extend([cliprect, Op.rectangle, Op.clip, Op.endpath])
if self._clippath != clippath:
+ path, affine = clippath.get_transformed_path_and_affine()
cmds.extend(
- PdfFile.pathOperations(
- *clippath.get_transformed_path_and_affine()) +
+ PdfFile.pathOperations(path, affine) +
[Op.clip, Op.endpath])
return cmds
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -150,10 +150,6 @@
self.textcnt = 0
self.psfrag = []
self.imagedpi = imagedpi
- if rcParams['path.simplify']:
- self.simplify = (width * imagedpi, height * imagedpi)
- else:
- self.simplify = None
# current renderer state (None=uninitialised)
self.color = None
@@ -428,12 +424,15 @@
# unflip
im.flipud_out()
- def _convert_path(self, path, transform, simplify=None):
- path = transform.transform_path(path)
-
+ def _convert_path(self, path, transform, clip=False):
ps = []
last_points = None
- for points, code in path.iter_segments(simplify):
+ if clip:
+ clip = (0.0, 0.0, self.width * self.imagedpi,
+ self.height * self.imagedpi)
+ else:
+ clip = None
+ for points, code in path.iter_segments(transform, clip=clip):
if code == Path.MOVETO:
ps.append("%g %g m" % tuple(points))
elif code == Path.LINETO:
@@ -466,7 +465,7 @@
"""
Draws a Path instance using the given affine transform.
"""
- ps = self._convert_path(path, transform, self.simplify)
+ ps = self._convert_path(path, transform, clip=(rgbFace is None))
self._draw_ps(ps, gc, rgbFace)
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
@@ -494,8 +493,7 @@
ps_cmd.extend(['stroke', 'grestore', '} bind def'])
- tpath = trans.transform_path(path)
- for vertices, code in tpath.iter_segments():
+ for vertices, code in path.iter_segments(trans, simplify=False):
if len(vertices):
x, y = vertices[-2:]
ps_cmd.append("%g %g o" % (x, y))
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -42,10 +42,6 @@
self.width=width
self.height=height
self._svgwriter = svgwriter
- if rcParams['path.simplify']:
- self.simplify = (width, height)
- else:
- self.simplify = None
self._groupd = {}
if not rcParams['svg.image_inline']:
@@ -209,14 +205,16 @@
.scale(1.0, -1.0)
.translate(0.0, self.height))
- def _convert_path(self, path, transform, simplify=None):
- tpath = transform.transform_path(path)
-
+ def _convert_path(self, path, transform, clip=False):
path_data = []
appender = path_data.append
path_commands = self._path_commands
currpos = 0
- for points, code in tpath.iter_segments(simplify):
+ if clip:
+ clip = (0.0, 0.0, self.width, self.height)
+ else:
+ clip = None
+ for points, code in path.iter_segments(transform, clip=clip):
if code == Path.CLOSEPOLY:
segment = 'z'
else:
@@ -231,7 +229,7 @@
def draw_path(self, gc, path, transform, rgbFace=None):
trans_and_flip = self._make_flip_transform(transform)
- path_data = self._convert_path(path, trans_and_flip, self.simplify)
+ path_data = self._convert_path(path, trans_and_flip, clip=(rgbFace is None))
self._draw_svg_element('path', 'd="%s"' % path_data, gc, rgbFace)
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
@@ -252,8 +250,7 @@
write('<g %s>' % clippath)
trans_and_flip = self._make_flip_transform(trans)
- tpath = trans_and_flip.transform_path(path)
- for vertices, code in tpath.iter_segments():
+ for vertices, code in path.iter_segments(trans_and_flip, simplify=False):
if len(vertices):
x, y = vertices[-2:]
details = 'xlink:href="#%s" x="%f" y="%f"' % (name, x, y)
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_wx.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_wx.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_wx.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -304,9 +304,9 @@
new_bounds[2], new_bounds[3])
@staticmethod
- def convert_path(gfx_ctx, tpath):
+ def convert_path(gfx_ctx, path, transform):
wxpath = gfx_ctx.CreatePath()
- for points, code in tpath.iter_segments():
+ for points, code in path.iter_segments(transform):
if code == Path.MOVETO:
wxpath.MoveToPoint(*points)
elif code == Path.LINETO:
@@ -324,8 +324,7 @@
self.handle_clip_rectangle(gc)
gfx_ctx = gc.gfx_ctx
transform = transform + Affine2D().scale(1.0, -1.0).translate(0.0, self.height)
- tpath = transform.transform_path(path)
- wxpath = self.convert_path(gfx_ctx, tpath)
+ wxpath = self.convert_path(gfx_ctx, path, transform)
if rgbFace is not None:
gfx_ctx.SetBrush(wx.Brush(gc.get_wxcolour(rgbFace)))
gfx_ctx.DrawPath(wxpath)
Modified: trunk/matplotlib/lib/matplotlib/config/mplconfig.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/config/mplconfig.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/config/mplconfig.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -118,6 +118,7 @@
class path(TConfig):
simplify = T.false
+ simplify_threshold = T.float(1.0 / 9.0)
class patch(TConfig):
linewidth = T.Float(1.0)
@@ -442,7 +443,8 @@
'svg.embed_char_paths' : (self.tconfig.backend.svg, 'embed_char_paths'),
# Path properties
- 'path.simplify' : (self.tconfig.path, 'simplify')
+ 'path.simplify' : (self.tconfig.path, 'simplify'),
+ 'path.simplify_threshold' : (self.tconfig.path, 'simplify_threshold')
}
def __setitem__(self, key, val):
Modified: trunk/matplotlib/lib/matplotlib/config/rcsetup.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/config/rcsetup.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/config/rcsetup.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -479,7 +479,8 @@
'svg.embed_char_paths' : [True, validate_bool], # True to save all characters as paths in the SVG
'plugins.directory' : ['.matplotlib_plugins', str], # where plugin directory is locate
- 'path.simplify' : [True, validate_bool]
+ 'path.simplify' : [True, validate_bool],
+ 'path.simplify_threshold' : [1.0 / 9.0, ValidateInterval(0.0, 1.0)]
}
if __name__ == '__main__':
Modified: trunk/matplotlib/lib/matplotlib/path.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/path.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/path.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -10,7 +10,8 @@
from matplotlib._path import point_in_path, get_path_extents, \
point_in_path_collection, get_path_collection_extents, \
- path_in_path, path_intersects_path, convert_path_to_polygons
+ path_in_path, path_intersects_path, convert_path_to_polygons, \
+ cleanup_path
from matplotlib.cbook import simple_linear_interpolation, maxdict
from matplotlib import rcParams
@@ -65,14 +66,17 @@
"""
# Path codes
- STOP = 0 # 1 vertex
- MOVETO = 1 # 1 vertex
- LINETO = 2 # 1 vertex
- CURVE3 = 3 # 2 vertices
- CURVE4 = 4 # 3 vertices
- CLOSEPOLY = 5 # 1 vertex
+ STOP = 0 # 1 vertex
+ MOVETO = 1 # 1 vertex
+ LINETO = 2 # 1 vertex
+ CURVE3 = 3 # 2 vertices
+ CURVE4 = 4 # 3 vertices
+ CLOSEPOLY = 0x4f # 1 vertex
- NUM_VERTICES = [1, 1, 1, 2, 3, 1]
+ NUM_VERTICES = [1, 1, 1, 2,
+ 3, 1, 1, 1,
+ 1, 1, 1, 1,
+ 1, 1, 1, 1]
code_type = np.uint8
@@ -113,6 +117,7 @@
self.should_simplify = (rcParams['path.simplify'] and
(len(vertices) >= 128 and
(codes is None or np.all(codes <= Path.LINETO))))
+ self.simplify_threshold = rcParams['path.simplify_threshold']
self.has_nonfinite = not np.isfinite(vertices).all()
self.codes = codes
self.vertices = vertices
@@ -146,31 +151,43 @@
def __len__(self):
return len(self.vertices)
- def iter_segments(self, simplify=None):
+ def iter_segments(self, transform=None, remove_nans=True, clip=None,
+ quantize=False, simplify=None, curves=True):
"""
Iterates over all of the curve segments in the path. Each
iteration returns a 2-tuple (*vertices*, *code*), where
*vertices* is a sequence of 1 - 3 coordinate pairs, and *code* is
one of the :class:`Path` codes.
- If *simplify* is provided, it must be a tuple (*width*,
- *height*) defining the size of the figure, in native units
- (e.g. pixels or points). Simplification implies both removing
- adjacent line segments that are very close to parallel, and
- removing line segments outside of the figure. The path will
- be simplified *only* if :attr:`should_simplify` is True, which
- is determined in the constructor by this criteria:
+ Additionally, this method can provide a number of standard
+ cleanups and conversions to the path.
- - No curves
- - More than 128 vertices
+ *transform*: if not None, the given affine transformation will
+ be applied to the path.
+
+ *remove_nans*: if True, will remove all NaNs from the path and
+ insert MOVETO commands to skip over them.
+
+ *clip*: if not None, must be a four-tuple (x1, y1, x2, y2)
+ defining a rectangle in which to clip the path.
+
+ *quantize*: if None, auto-quantize. If True, force quantize,
+ and if False, don't quantize.
+
+ *simplify*: if True, perform simplification, to remove
+ vertices that do not affect the appearance of the path. If
+ False, perform no simplification. If None, use the
+ should_simplify member variable.
+
+ *curves*: If True, curve segments will be returned as curve
+ segments. If False, all curves will be converted to line
+ segments.
"""
vertices = self.vertices
if not len(vertices):
return
codes = self.codes
- len_vertices = len(vertices)
- isfinite = np.isfinite
NUM_VERTICES = self.NUM_VERTICES
MOVETO = self.MOVETO
@@ -178,47 +195,20 @@
CLOSEPOLY = self.CLOSEPOLY
STOP = self.STOP
- if simplify is not None and self.should_simplify:
- polygons = self.to_polygons(None, *simplify)
- for vertices in polygons:
- yield vertices[0], MOVETO
- for v in vertices[1:]:
- yield v, LINETO
- elif codes is None:
- if self.has_nonfinite:
- next_code = MOVETO
- for v in vertices:
- if np.isfinite(v).all():
- yield v, next_code
- next_code = LINETO
- else:
- next_code = MOVETO
+ vertices, codes = cleanup_path(self, transform, remove_nans, clip,
+ quantize, simplify, curves)
+ len_vertices = len(vertices)
+
+ i = 0
+ while i < len_vertices:
+ code = codes[i]
+ if code == STOP:
+ return
else:
- yield vertices[0], MOVETO
- for v in vertices[1:]:
- yield v, LINETO
- else:
- i = 0
- was_nan = False
- while i < len_vertices:
- code = codes[i]
- if code == CLOSEPOLY:
- yield [], code
- i += 1
- elif code == STOP:
- return
- else:
- num_vertices = NUM_VERTICES[int(code)]
- curr_vertices = vertices[i:i+num_vertices].flatten()
- if not isfinite(curr_vertices).all():
- was_nan = True
- elif was_nan:
- yield curr_vertices[:2], MOVETO
- yield curr_vertices, code
- was_nan = False
- else:
- yield curr_vertices, code
- i += num_vertices
+ num_vertices = NUM_VERTICES[int(code) & 0xf]
+ curr_vertices = vertices[i:i+num_vertices].flatten()
+ yield curr_vertices, code
+ i += num_vertices
def transformed(self, transform):
"""
Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/rcsetup.py 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2009-01-29 16:51:12 UTC (rev 6847)
@@ -520,6 +520,7 @@
'plugins.directory' : ['.matplotlib_plugins', str], # where plugin directory is locate
'path.simplify' : [True, validate_bool],
+ 'path.simplify_threshold' : [1.0 / 9.0, ValidateInterval(0.0, 1.0)],
'agg.path.chunksize' : [0, validate_int] # 0 to disable chunking;
# recommend about 20000 to
# enable. Experimental.
Modified: trunk/matplotlib/matplotlibrc.template
===================================================================
--- trunk/matplotlib/matplotlibrc.template 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/matplotlibrc.template 2009-01-29 16:51:12 UTC (rev 6847)
@@ -290,7 +290,11 @@
# A value of 20000 is probably a good
# starting point.
### SAVING FIGURES
-#path.simplify : False # When True, simplify paths in vector backends, such as PDF, PS and SVG
+#path.simplify : False # When True, simplify paths in vector backends, such as
+ # PDF, PS and SVG
+#path.simplify_threshold : 0.1 # The threshold of similarity below which
+ # vertices will be removed in the simplification
+ # process
# the default savefig params can be different from the display params
# Eg, you may want a higher resolution, or to make the figure
Modified: trunk/matplotlib/src/_backend_agg.cpp
===================================================================
--- trunk/matplotlib/src/_backend_agg.cpp 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/src/_backend_agg.cpp 2009-01-29 16:51:12 UTC (rev 6847)
@@ -267,11 +267,11 @@
Py::Callable method(method_obj);
Py::Object py_snap = method.apply(Py::Tuple());
if (py_snap.isNone()) {
- snap = SNAP_AUTO;
+ quantize_mode = QUANTIZE_AUTO;
} else if (py_snap.isTrue()) {
- snap = SNAP_TRUE;
+ quantize_mode = QUANTIZE_TRUE;
} else {
- snap = SNAP_FALSE;
+ quantize_mode = QUANTIZE_FALSE;
}
}
@@ -366,55 +366,6 @@
return face;
}
-template<class Path>
-bool should_snap(GCAgg& gc, Path& path, const agg::trans_affine& trans) {
- // If this contains only straight horizontal or vertical lines, it should be
- // quantized to the nearest pixels
- double x0, y0, x1, y1;
- unsigned code;
-
- switch (gc.snap) {
- case GCAgg::SNAP_AUTO:
- if (path.total_vertices() > 15)
- return false;
-
- code = path.vertex(&x0, &y0);
- if (code == agg::path_cmd_stop) {
- path.rewind(0);
- return false;
- }
- trans.transform(&x0, &y0);
-
- while ((code = path.vertex(&x1, &y1)) != agg::path_cmd_stop) {
- trans.transform(&x1, &y1);
-
- switch (code) {
- case agg::path_cmd_curve3:
- case agg::path_cmd_curve4:
- path.rewind(0);
- return false;
- case agg::path_cmd_line_to:
- if (!(fabs(x0 - x1) < 1e-4 || fabs(y0 - y1) < 1e-4)) {
- path.rewind(0);
- return false;
- }
- }
-
- x0 = x1;
- y0 = y1;
- }
-
- path.rewind(0);
- gc.isaa = false;
- return true;
- case GCAgg::SNAP_FALSE:
- return false;
- case GCAgg::SNAP_TRUE:
- return true;
- }
- return false;
-}
-
Py::Object
RendererAgg::copy_from_bbox(const Py::Tuple& args) {
//copy region in bbox to buffer and return swig/agg buffer object
@@ -509,8 +460,8 @@
Py::Object
RendererAgg::draw_markers(const Py::Tuple& args) {
typedef agg::conv_transform<PathIterator> transformed_path_t;
- typedef SimplifyPath<transformed_path_t> simplify_t;
- typedef agg::conv_curve<simplify_t> curve_t;
+ typedef PathQuantizer<transformed_path_t> quantize_t;
+ typedef agg::conv_curve<quantize_t> curve_t;
typedef agg::conv_stroke<curve_t> stroke_t;
typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
@@ -534,15 +485,12 @@
trans *= agg::trans_affine_scaling(1.0, -1.0);
trans *= agg::trans_affine_translation(0.0, (double)height);
- PathIterator marker_path(marker_path_obj);
- // The built-in markers look better if snapping is turned on, but
- // unfortunately, it can cause really small things to disappear.
- // Disabling for now to revisit at a later date.
- // const bool marker_snap = true;
- bool marker_snap = should_snap(gc, marker_path, marker_trans);
+ PathIterator marker_path(marker_path_obj);
transformed_path_t marker_path_transformed(marker_path, marker_trans);
- simplify_t marker_path_simplified(marker_path_transformed, marker_snap, false, width, height);
- curve_t marker_path_curve(marker_path_simplified);
+ quantize_t marker_path_quantized(marker_path_transformed,
+ gc.quantize_mode,
+ marker_path.total_vertices());
+ curve_t marker_path_curve(marker_path_quantized);
PathIterator path(path_obj);
transformed_path_t path_transformed(path, trans);
@@ -900,8 +848,7 @@
// Create and transform the path
typedef agg::conv_transform<PathIterator> hatch_path_trans_t;
- typedef SimplifyPath<hatch_path_trans_t> hatch_path_simplify_t;
- typedef agg::conv_curve<hatch_path_simplify_t> hatch_path_curve_t;
+ typedef agg::conv_curve<hatch_path_trans_t> hatch_path_curve_t;
typedef agg::conv_stroke<hatch_path_curve_t> hatch_path_stroke_t;
PathIterator hatch_path(gc.hatchpath);
@@ -910,8 +857,7 @@
hatch_trans *= agg::trans_affine_translation(0.0, 1.0);
hatch_trans *= agg::trans_affine_scaling(HATCH_SIZE, HATCH_SIZE);
hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans);
- hatch_path_simplify_t hatch_path_simplify(hatch_path_trans, false, false, HATCH_SIZE, HATCH_SIZE);
- hatch_path_curve_t hatch_path_curve(hatch_path_simplify);
+ hatch_path_curve_t hatch_path_curve(hatch_path_trans);
hatch_path_stroke_t hatch_path_stroke(hatch_path_curve);
hatch_path_stroke.width(1.0);
hatch_path_stroke.line_cap(agg::square_cap);
@@ -1005,9 +951,12 @@
Py::Object
RendererAgg::draw_path(const Py::Tuple& args) {
- typedef agg::conv_transform<PathIterator> transformed_path_t;
- typedef SimplifyPath<transformed_path_t> simplify_t;
- typedef agg::conv_curve<simplify_t> curve_t;
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removed_t;
+ typedef PathClipper<nan_removed_t> clipped_t;
+ typedef PathQuantizer<clipped_t> quantized_t;
+ typedef PathSimplifier<quantized_t> simplify_t;
+ typedef agg::conv_curve<simplify_t> curve_t;
_VERBOSE("RendererAgg::draw_path");
args.verify_length(3, 4);
@@ -1030,12 +979,15 @@
trans *= agg::trans_affine_scaling(1.0, -1.0);
trans *= agg::trans_affine_translation(0.0, (double)height);
- bool snap = should_snap(gc, path, trans);
+ bool clip = !face.first;
bool simplify = path.should_simplify() && !face.first;
transformed_path_t tpath(path, trans);
- simplify_t simplified(tpath, snap, simplify, width, height);
- curve_t curve(simplified);
+ nan_removed_t nan_removed(tpath, true, path.has_curves());
+ clipped_t clipped(nan_removed, clip, width, height);
+ quantized_t quantized(clipped, gc.quantize_mode, path.total_vertices());
+ simplify_t simplified(quantized, simplify, path.simplify_threshold());
+ curve_t curve(simplified);
try {
_draw_path(curve, has_clippath, face, gc);
@@ -1063,9 +1015,11 @@
const Py::SeqBase<Py::Object>& linestyles_obj,
const Py::SeqBase<Py::Int>& antialiaseds) {
typedef agg::conv_transform<typename PathGenerator::path_iterator> transformed_path_t;
- typedef SimplifyPath<transformed_path_t> simplify_t;
- typedef agg::conv_curve<simplify_t> simplified_curve_t;
- typedef agg::conv_curve<transformed_path_t> curve_t;
+ typedef PathNanRemover<transformed_path_t> nan_removed_t;
+ typedef PathClipper<nan_removed_t> clipped_t;
+ typedef PathQuantizer<clipped_t> quantized_t;
+ typedef agg::conv_curve<quantized_t> quantized_curve_t;
+ typedef agg::conv_curve<clipped_t> curve_t;
GCAgg gc(dpi);
@@ -1144,7 +1098,6 @@
facepair_t face;
face.first = Nfacecolors != 0;
agg::trans_affine trans;
- bool snap = false;
for (i = 0; i < N; ++i) {
typename PathGenerator::path_iterator path = path_generator(i);
@@ -1192,26 +1145,29 @@
}
if (check_snap) {
- snap = should_snap(gc, path, trans);
gc.isaa = bool(Py::Int(antialiaseds[i % Naa]));
transformed_path_t tpath(path, trans);
- simplify_t simplified(tpath, snap, false, width, height);
+ nan_removed_t nan_removed(tpath, true, has_curves);
+ clipped_t clipped(nan_removed, !face.first, width, height);
+ quantized_t quantized(clipped, gc.quantize_mode, path.total_vertices());
if (has_curves) {
- simplified_curve_t curve(simplified);
+ quantized_curve_t curve(quantized);
_draw_path(curve, has_clippath, face, gc);
} else {
- _draw_path(simplified, has_clippath, face, gc);
+ _draw_path(quantized, has_clippath, face, gc);
}
} else {
gc.isaa = bool(Py::Int(antialiaseds[i % Naa]));
transformed_path_t tpath(path, trans);
+ nan_removed_t nan_removed(tpath, true, has_curves);
+ clipped_t clipped(nan_removed, !face.first, width, height);
if (has_curves) {
- curve_t curve(tpath);
+ curve_t curve(clipped);
_draw_path(curve, has_clippath, face, gc);
} else {
- _draw_path(tpath, has_clippath, face, gc);
+ _draw_path(clipped, has_clippath, face, gc);
}
}
}
Modified: trunk/matplotlib/src/_backend_agg.h
===================================================================
--- trunk/matplotlib/src/_backend_agg.h 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/src/_backend_agg.h 2009-01-29 16:51:12 UTC (rev 6847)
@@ -39,6 +39,7 @@
#include "agg_vcgen_markers_term.h"
#include "agg_py_path_iterator.h"
+#include "path_converters.h"
// These are copied directly from path.py, and must be kept in sync
#define STOP 0
@@ -121,13 +122,8 @@
typedef std::vector<std::pair<double, double> > dash_t;
double dashOffset;
dash_t dashes;
+ e_quantize_mode quantize_mode;
- enum {
- SNAP_AUTO,
- SNAP_FALSE,
- SNAP_TRUE
- } snap;
-
Py::Object hatchpath;
protected:
Modified: trunk/matplotlib/src/_macosx.m
===================================================================
--- trunk/matplotlib/src/_macosx.m 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/src/_macosx.m 2009-01-29 16:51:12 UTC (rev 6847)
@@ -1,4 +1,4 @@
-#include <Cocoa/Cocoa.h>
+#include <Cocoa/Cocoa.h>
#include <ApplicationServices/ApplicationServices.h>
#include <sys/socket.h>
#include <Python.h>
@@ -23,7 +23,7 @@
* [ a b 0]
* [ c d 0]
* [ tx ty 1]
- */
+ */
typedef struct
{
double a;
@@ -47,7 +47,7 @@
#define LINETO 2
#define CURVE3 3
#define CURVE4 4
-#define CLOSEPOLY 5
+#define CLOSEPOLY 0x4f
/* Hatching */
#define HATCH_SIZE 72
@@ -132,7 +132,7 @@
sigint_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
channel[0],
- kCFSocketReadCallBack,
+ kCFSocketReadCallBack,
_callback,
NULL);
if (sigint_socket)
@@ -470,8 +470,8 @@
}
else
{
- CGContextSetLineWidth(cr, 1.0);
- CGContextSetLineCap(cr, kCGLineCapSquare);
+ CGContextSetLineWidth(cr, 1.0);
+ CGContextSetLineCap(cr, kCGLineCapSquare);
CGContextDrawPath(cr, kCGPathFillStroke);
}
}
@@ -608,7 +608,7 @@
static PyObject*
GraphicsContext_set_alpha (GraphicsContext* self, PyObject* args)
-{
+{
float alpha;
if (!PyArg_ParseTuple(args, "f", &alpha)) return NULL;
CGContextRef cr = self->cr;
@@ -624,8 +624,8 @@
static PyObject*
GraphicsContext_set_antialiased (GraphicsContext* self, PyObject* args)
-{
- int shouldAntialias;
+{
+ int shouldAntialias;
if (!PyArg_ParseTuple(args, "i", &shouldAntialias)) return NULL;
CGContextRef cr = self->cr;
if (!cr)
@@ -640,7 +640,7 @@
static PyObject*
GraphicsContext_set_capstyle (GraphicsContext* self, PyObject* args)
-{
+{
char* string;
CGLineCap cap;
@@ -662,7 +662,7 @@
return NULL;
}
CGContextSetLineCap(cr, cap);
-
+
Py_INCREF(Py_None);
return Py_None;
}
@@ -964,7 +964,7 @@
static PyObject*
GraphicsContext_set_dashes (GraphicsContext* self, PyObject* args)
-{
+{
PyObject* offset;
PyObject* dashes;
@@ -1026,7 +1026,7 @@
static PyObject*
GraphicsContext_set_linewidth (GraphicsContext* self, PyObject* args)
-{
+{
float width;
if (!PyArg_ParseTuple(args, "f", &width)) return NULL;
@@ -1067,7 +1067,7 @@
return NULL;
}
CGContextSetLineJoin(cr, join);
-
+
Py_INCREF(Py_None);
return Py_None;
}
@@ -1128,7 +1128,7 @@
PyObject* rgbFace;
int ok;
-
+
CGContextRef cr = self->cr;
if (!cr)
@@ -1150,7 +1150,7 @@
int n = _draw_path(cr, path, affine);
if (n==-1) return NULL;
-
+
if (n > 0)
{
PyObject* hatchpath;
@@ -1235,7 +1235,7 @@
int ok;
float r, g, b;
double x, y;
-
+
CGContextRef cr = self->cr;
if (!cr)
@@ -1322,7 +1322,7 @@
}
static BOOL _clip(CGContextRef cr, PyObject* object)
-{
+{
if (object == Py_None) return true;
PyArrayObject* array = NULL;
@@ -1362,8 +1362,8 @@
static PyObject*
GraphicsContext_draw_path_collection (GraphicsContext* self, PyObject* args)
{
- PyObject* master_transform_obj;
- PyObject* cliprect;
+ PyObject* master_transform_obj;
+ PyObject* cliprect;
PyObject* clippath;
PyObject* clippath_transform;
PyObject* paths;
@@ -1538,10 +1538,10 @@
if (Nlinewidths==1)
{
double linewidth = PyFloat_AsDouble(PySequence_ITEM(linewidths, 0));
- CGContextSetLineWidth(cr, (CGFloat)linewidth);
+ CGContextSetLineWidth(cr, (CGFloat)linewidth);
}
else if (Nlinewidths==0)
- CGContextSetLineWidth(cr, 0.0);
+ CGContextSetLineWidth(cr, 0.0);
if (Nlinestyles==1)
{
@@ -1613,7 +1613,7 @@
if (Nlinewidths > 1)
{
double linewidth = PyFloat_AsDouble(PySequence_ITEM(linewidths, i % Nlinewidths));
- CGContextSetLineWidth(cr, (CGFloat)linewidth);
+ CGContextSetLineWidth(cr, (CGFloat)linewidth);
}
if (Nlinestyles > 1)
@@ -2057,15 +2057,15 @@
{"Chicago", /* 22 */
"",
"",
- ""},
+ ""},
{"Charcoal", /* 23 */
"",
"",
- ""},
+ ""},
{"Impact", /* 24 */
"",
"",
- ""},
+ ""},
{"Playbill", /* 25 */
"",
"",
@@ -2073,7 +2073,7 @@
{"AndaleMono", /* 26 */
"",
"",
- ""},
+ ""},
{"BitstreamVeraSansMono-Roman", /* 27 */
"BitstreamVeraSansMono-Bold",
"BitstreamVeraSansMono-Oblique",
@@ -2091,7 +2091,7 @@
"CourierNewPS-ItalicMT",
"CourierNewPS-Bold-ItalicMT"},
};
-
+
if(!PyList_Check(family)) return 0;
n = PyList_GET_SIZE(family);
@@ -2106,7 +2106,7 @@
break;
}
}
- /* If the font name is not found in mapping, we assume */
+ /* If the font name is not found in mapping, we assume */
/* that the user specified the Postscript name directly */
/* Check if this font can be found on the system */
@@ -2135,7 +2135,7 @@
static PyObject*
GraphicsContext_draw_text (GraphicsContext* self, PyObject* args)
-{
+{
float x;
float y;
const UniChar* text;
@@ -2335,7 +2335,7 @@
static PyObject*
GraphicsContext_get_text_width_height_descent(GraphicsContext* self, PyObject* args)
-{
+{
const UniChar* text;
int n;
PyObject* family;
@@ -2449,7 +2449,7 @@
if(!PyArg_ParseTuple(args, "ffiiOOOO", &x,
&y,
- &nrows,
+ &nrows,
&ncols,
&image,
&cliprect,
@@ -2661,7 +2661,7 @@
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- GraphicsContext_methods, /* tp_methods */
+ GraphicsContext_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
@@ -2695,7 +2695,7 @@
int height;
if(!self->view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return -1;
}
@@ -2736,7 +2736,7 @@
View* view = self->view;
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return NULL;
}
[view setNeedsDisplay: YES];
@@ -2752,7 +2752,7 @@
NSRect rubberband;
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return NULL;
}
if(!PyArg_ParseTuple(args, "iiii", &x0, &y0, &x1, &y1)) return NULL;
@@ -2787,7 +2787,7 @@
View* view = self->view;
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return NULL;
}
[view removeRubberband];
@@ -2845,7 +2845,7 @@
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return NULL;
}
if(!PyArg_ParseTuple(args, "u#ff",
@@ -2891,7 +2891,7 @@
else if ([extension isEqualToString: @"png"])
filetype = NSPNGFileType;
else
- { PyErr_SetString(PyExc_ValueError, "Unknown file type");
+ { PyErr_SetString(PyExc_ValueError, "Unknown file type");
return NULL;
}
@@ -2914,7 +2914,7 @@
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return NULL;
}
if(!PyArg_ParseTuple(args, "u#", &characters, &n)) return NULL;
@@ -2960,7 +2960,7 @@
context.info = &interrupted;
sigint_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
channel[0],
- kCFSocketReadCallBack,
+ kCFSocketReadCallBack,
_callback,
&context);
if (sigint_socket)
@@ -3100,7 +3100,7 @@
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- FigureCanvas_methods, /* tp_methods */
+ FigureCanvas_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
@@ -3147,7 +3147,7 @@
if(!self->window)
{
- PyErr_SetString(PyExc_RuntimeError, "NSWindow* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSWindow* is NULL");
return -1;
}
@@ -3157,7 +3157,7 @@
view = canvas->view;
if (!view) /* Something really weird going on */
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return -1;
}
@@ -3276,7 +3276,7 @@
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- FigureManager_methods, /* tp_methods */
+ FigureManager_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
@@ -3471,7 +3471,7 @@
view = canvas->view;
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return -1;
}
@@ -3748,7 +3748,7 @@
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- NavigationToolbar_methods, /* tp_methods */
+ NavigationToolbar_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
@@ -3902,7 +3902,7 @@
view = ((FigureCanvas*)canvas)->view;
if (!view) /* Something really weird going on */
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
PyErr_Print();
Py_DECREF(canvas);
Py_DECREF(master);
@@ -4003,7 +4003,7 @@
view = canvas->view;
if(!view)
{
- PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
+ PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return -1;
}
@@ -4173,7 +4173,7 @@
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- NavigationToolbar2_methods, /* tp_methods */
+ NavigationToolbar2_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
Modified: trunk/matplotlib/src/_path.cpp
===================================================================
--- trunk/matplotlib/src/_path.cpp 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/src/_path.cpp 2009-01-29 16:51:12 UTC (rev 6847)
@@ -1,5 +1,6 @@
#include "agg_py_path_iterator.h"
#include "agg_py_transforms.h"
+#include "path_converters.h"
#include <limits>
#include <math.h>
@@ -53,6 +54,8 @@
"path_intersects_path(p1, p2)");
add_varargs_method("convert_path_to_polygons", &_path_module::convert_path_to_polygons,
"convert_path_to_polygons(path, trans, width, height)");
+ add_varargs_method("cleanup_path", &_path_module::cleanup_path,
+ "cleanup_path(path, trans, remove_nans, clip, quantize, simplify, curves)");
initialize("Helper functions for paths");
}
@@ -72,6 +75,7 @@
Py::Object count_bboxes_overlapping_bbox(const Py::Tuple& args);
Py::Object path_intersects_path(const Py::Tuple& args);
Py::Object convert_path_to_polygons(const Py::Tuple& args);
+ Py::Object cleanup_path(const Py::Tuple& args);
};
//
@@ -1105,17 +1109,21 @@
PathIterator p1(args[0]);
PathIterator p2(args[1]);
bool filled = false;
- if (args.size() == 3) {
- filled = args[2].isTrue();
+ if (args.size() == 3)
+ {
+ filled = args[2].isTrue();
}
- if (!filled) {
- return Py::Int(::path_intersects_path(p1, p2));
- } else {
- return Py::Int(::path_intersects_path(p1, p2)
- || ::path_in_path(p1, agg::trans_affine(), p2, agg::trans_affine())
- || ::path_in_path(p2, agg::trans_affine(), p1, agg::trans_affine()));
+ if (!filled)
+ {
+ return Py::Int(::path_intersects_path(p1, p2));
}
+ else
+ {
+ return Py::Int(::path_intersects_path(p1, p2)
+ || ::path_in_path(p1, agg::trans_affine(), p2, agg::trans_affine())
+ || ::path_in_path(p2, agg::trans_affine(), p1, agg::trans_affine()));
+ }
}
void _add_polygon(Py::List& polygons, const std::vector<double>& polygon) {
@@ -1134,9 +1142,11 @@
Py::Object _path_module::convert_path_to_polygons(const Py::Tuple& args)
{
- typedef agg::conv_transform<PathIterator> transformed_path_t;
- typedef SimplifyPath<transformed_path_t> simplify_t;
- typedef agg::conv_curve<simplify_t> curve_t;
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removal_t;
+ typedef PathClipper<nan_removal_t> clipped_t;
+ typedef PathSimplifier<clipped_t> simplify_t;
+ typedef agg::conv_curve<simplify_t> curve_t;
typedef std::vector<double> vertices_t;
@@ -1147,11 +1157,15 @@
double width = Py::Float(args[2]);
double height = Py::Float(args[3]);
- bool simplify = path.should_simplify() && width != 0.0 && height != 0.0;
+ bool do_clip = width != 0.0 && height != 0.0;
+ bool simplify = path.should_simplify();
+
transformed_path_t tpath(path, trans);
- simplify_t simplified(tpath, false, simplify, width, height);
- curve_t curve(simplified);
+ nan_removal_t nan_removed(tpath, true, path.has_curves());
+ clipped_t clipped(nan_removed, do_clip, width, height);
+ simplify_t simplified(clipped, simplify, path.simplify_threshold());
+ curve_t curve(simplified);
Py::List polygons;
vertices_t polygon;
@@ -1162,7 +1176,8 @@
while ((code = curve.vertex(&x, &y)) != agg::path_cmd_stop)
{
- if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) {
+ if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly)
+ {
if (polygon.size() >= 2)
{
polygon.push_back(polygon[0]);
@@ -1170,8 +1185,11 @@
_add_polygon(polygons, polygon);
}
polygon.clear();
- } else {
- if (code == agg::path_cmd_move_to) {
+ }
+ else
+ {
+ if (code == agg::path_cmd_move_to)
+ {
_add_polygon(polygons, polygon);
polygon.clear();
}
@@ -1185,6 +1203,152 @@
return polygons;
}
+template<class VertexSource>
+void __cleanup_path(VertexSource& source,
+ std::vector<double>& vertices,
+ std::vector<uint8_t>& codes) {
+ unsigned code;
+ double x, y;
+ do
+ {
+ code = source.vertex(&x, &y);
+ vertices.push_back(x);
+ vertices.push_back(y);
+ codes.push_back((uint8_t)code);
+ } while (code != agg::path_cmd_stop);
+}
+
+void _cleanup_path(PathIterator& path, const agg::trans_affine& trans,
+ bool remove_nans, bool do_clip,
+ const agg::rect_base<double>& rect,
+ e_quantize_mode quantize_mode, bool do_simplify,
+ bool return_curves, std::vector<double>& vertices,
+ std::vector<uint8_t>& codes) {
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removal_t;
+ typedef PathClipper<nan_removal_t> clipped_t;
+ typedef PathQuantizer<clipped_t> quantized_t;
+ typedef PathSimplifier<quantized_t> simplify_t;
+ typedef agg::conv_curve<simplify_t> curve_t;
+
+ transformed_path_t tpath(path, trans);
+ nan_removal_t nan_removed(tpath, remove_nans, path.has_curves());
+ clipped_t clipped(nan_removed, do_clip, rect);
+ quantized_t quantized(clipped, quantize_mode, path.total_vertices());
+ simplify_t simplified(quantized, do_simplify, path.simplify_threshold());
+
+ vertices.reserve(path.total_vertices() * 2);
+ codes.reserve(path.total_vertices());
+
+ if (return_curves)
+ {
+ __cleanup_path(simplified, vertices, codes);
+ }
+ else
+ {
+ curve_t curve(simplified);
+ __cleanup_path(curve, vertices, codes);
+ }
+}
+
+Py::Object _path_module::cleanup_path(const Py::Tuple& args)
+{
+ args.verify_length(7);
+
+ PathIterator path(args[0]);
+ agg::trans_affine trans = py_to_agg_transformation_matrix(args[1], false);
+ bool remove_nans = args[2].isTrue();
+
+ Py::Object clip_obj = args[3];
+ bool do_clip;
+ agg::rect_base<double> clip_rect;
+ if (clip_obj.isNone())
+ {
+ do_clip = false;
+ }
+ else
+ {
+ double x1, y1, x2, y2;
+ Py::Tuple clip_tuple(clip_obj);
+ x1 = Py::Float(clip_tuple[0]);
+ y1 = Py::Float(clip_tuple[1]);
+ x2 = Py::Float(clip_tuple[2]);
+ y2 = Py::Float(clip_tuple[3]);
+ clip_rect.init(x1, y1, x2, y2);
+ do_clip = true;
+ }
+
+ Py::Object quantize_obj = args[4];
+ e_quantize_mode quantize_mode;
+ if (quantize_obj.isNone())
+ {
+ quantize_mode = QUANTIZE_AUTO;
+ }
+ else if (quantize_obj.isTrue())
+ {
+ quantize_mode = QUANTIZE_TRUE;
+ }
+ else
+ {
+ quantize_mode = QUANTIZE_FALSE;
+ }
+
+ bool simplify;
+ Py::Object simplify_obj = args[5];
+ if (simplify_obj.isNone())
+ {
+ simplify = path.should_simplify();
+ }
+ else
+ {
+ simplify = simplify_obj.isTrue();
+ }
+
+ bool return_curves = args[6].isTrue();
+
+ std::vector<double> vertices;
+ std::vector<uint8_t> codes;
+
+ _cleanup_path(path, trans, remove_nans, do_clip, clip_rect, quantize_mode,
+ simplify, return_curves, vertices, codes);
+
+ npy_intp length = codes.size();
+ npy_intp dims[] = { length, 2, 0 };
+
+ PyArrayObject* vertices_obj = NULL;
+ PyArrayObject* codes_obj = NULL;
+ Py::Tuple result(2);
+ try {
+ vertices_obj = (PyArrayObject*)PyArray_SimpleNew
+ (2, dims, PyArray_DOUBLE);
+ if (vertices_obj == NULL)
+ {
+ throw Py::MemoryError("Could not allocate result array");
+ }
+
+ codes_obj = (PyArrayObject*)PyArray_SimpleNew
+ (1, dims, PyArray_UINT8);
+ if (codes_obj == NULL)
+ {
+ throw Py::MemoryError("Could not allocate result array");
+ }
+
+ memcpy(PyArray_DATA(vertices_obj), &vertices[0], sizeof(double) * 2 * length);
+ memcpy(PyArray_DATA(codes_obj), &codes[0], sizeof(uint8_t) * length);
+
+ result[0] = Py::Object((PyObject*)vertices_obj, true);
+ result[1] = Py::Object((PyObject*)codes_obj, true);
+ }
+ catch (...)
+ {
+ Py_XDECREF(vertices_obj);
+ Py_XDECREF(codes_obj);
+ throw;
+ }
+
+ return result;
+}
+
extern "C"
DL_EXPORT(void)
init_path(void)
Modified: trunk/matplotlib/src/agg_py_path_iterator.h
===================================================================
--- trunk/matplotlib/src/agg_py_path_iterator.h 2009-01-29 16:16:14 UTC (rev 6846)
+++ trunk/matplotlib/src/agg_py_path_iterator.h 2009-01-29 16:51:12 UTC (rev 6847)
@@ -5,28 +5,48 @@
#define PY_ARRAY_TYPES_PREFIX NumPy
#include "numpy/arrayobject.h"
#include "agg_path_storage.h"
-#include "MPL_isnan.h"
-#include "mplutils.h"
-#include <queue>
+/*
+ This file contains a vertex source to adapt Python Numpy arrays to
+ Agg paths. It works as an iterator, and converts on-the-fly without
+ the need for a full copy of the data.
+ */
+
+/************************************************************
+ PathIterator acts as a bridge between Numpy and Agg. Given a pair of
+ Numpy arrays, vertices and codes, it iterates over those vertices and
+ codes, using the standard Agg vertex source interface:
+
+ unsigned vertex(double* x, double* y)
+ */
class PathIterator
{
+ /* We hold references to the Python objects, not just the
+ underlying data arrays, so that Python reference counting can
+ work.
+ */
PyArrayObject* m_vertices;
PyArrayObject* m_codes;
+
size_t m_iterator;
size_t m_total_vertices;
- size_t m_ok;
+
+ /* This class doesn't actually do any simplification, but we
+ store the value here, since it is obtained from the Python object.
+ */
bool m_should_simplify;
- static const unsigned char num_extra_points_map[16];
- static const unsigned code_map[];
+ double m_simplify_threshold;
public:
+ /* path_obj is an instance of the class Path as defined in path.py */
PathIterator(const Py::Object& path_obj) :
- m_vertices(NULL), m_codes(NULL), m_iterator(0), m_should_simplify(false)
+ m_vertices(NULL), m_codes(NULL), m_iterator(0), m_should_simplify(false),
+ m_simplify_threshold(1.0 / 9.0)
{
- Py::Object vertices_obj = path_obj.getAttr("vertices");
- Py::Object codes_obj = path_obj.getAttr("codes");
- Py::Object should_simplify_obj = path_obj.getAttr("should_simplify");
+ Py::Object vertices_obj = path_obj.getAttr("vertices");
+ Py::Object codes_obj = path_obj.getAttr("codes");
+ Py::Object should_simplify_obj = path_obj.getAttr("should_simplify");
+ Py::Object simplify_threshold_obj = path_obj.getAttr("simplify_threshold");
m_vertices = (PyArrayObject*)PyArray_FromObject
(vertices_obj.ptr(), PyArray_DOUBLE, 2, 2);
@@ -44,11 +64,11 @@
throw Py::ValueError("Invalid codes array.");
if (PyArray_DIM(m_codes, 0) != PyArray_DIM(m_vertices, 0))
throw Py::ValueError("Codes array is wrong length");
- m_ok = 0;
}
- m_should_simplify = should_simplify_obj.isTrue();
- m_total_vertices = m_vertices->dimensions[0];
+ m_should_simplify = should_simplify_obj.isTrue();
+ m_total_vertices = PyArray_DIM(m_vertices, 0);
+ m_simplify_threshold = Py::Float(simplify_threshold_obj);
}
~PathIterator()
@@ -57,20 +77,19 @@
Py_XDECREF(m_codes);
}
-private:
- inline void vertex(const unsigned idx, double* x, double* y)
+ inline unsigned vertex(double* x, double* y)
{
+ if (m_iterator >= m_total_vertices) return agg::path_cmd_stop;
+
+ const size_t idx = m_iterator++;
+
char* pair = (char*)PyArray_GETPTR2(m_vertices, idx, 0);
*x = *(double*)pair;
*y = *(double*)(pair + PyArray_STRIDE(m_vertices, 1));
- }
- inline unsigned vertex_with_code(const unsigned idx, double* x, double* y)
- {
- vertex(idx, x, y);
if (m_codes)
{
- return code_map[(int)*(char *)PyArray_GETPTR1(m_codes, idx)];
+ return (unsigned)(*(char *)PyArray_GETPTR1(m_codes, idx));
}
else
{
@@ -78,97 +97,6 @@
}
}
-public:
- inline unsigned vertex(double* x, double* y)
- {
- if (m_iterator >= m_total_vertices) return agg::path_cmd_stop;
- unsigned code = vertex_with_code(m_iterator++, x, y);
-
- if (!m_codes) {
- // This is the fast path for when we know we have no curves
- if (MPL_notisfinite64(*x) || MPL_notisfinite64(*y))
- {
- do
- {
- if (m_iterator < m_total_vertices)
- {
- vertex(m_iterator++, x, y);
- }
- else
- {
- return agg::path_cmd_stop;
- }
- } while (MPL_notisfinite64(*x) || MPL_notisfinite64(*y));
- return agg::path_cmd_move_to;
- }
- }
- else
- {
- // This is the slow method for when there might be curves.
-
- ...
[truncated message content] |