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
|
|
From: <md...@us...> - 2007-09-20 12:31:27
|
Revision: 3861
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3861&view=rev
Author: mdboom
Date: 2007-09-20 05:31:26 -0700 (Thu, 20 Sep 2007)
Log Message:
-----------
Fix font.size from being saved in the fontManager.cache
Modified Paths:
--------------
trunk/matplotlib/lib/matplotlib/font_manager.py
Modified: trunk/matplotlib/lib/matplotlib/font_manager.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/font_manager.py 2007-09-19 19:48:17 UTC (rev 3860)
+++ trunk/matplotlib/lib/matplotlib/font_manager.py 2007-09-20 12:31:26 UTC (rev 3861)
@@ -773,6 +773,7 @@
else:
if is_string_like(size):
parent_size = fontManager.get_default_size()
+ print "parent_size", parent_size, size
scaling = font_scalings.get(size)
if scaling is not None:
size = parent_size * scaling
@@ -843,10 +844,9 @@
"""
def __init__(self, size=None, weight='normal'):
- if not size : size = rcParams['font.size']
- self.__default_size = size
self.__default_weight = weight
-
+ self.default_size = size
+
paths = [os.path.join(rcParams['datapath'],'fonts','ttf'),
os.path.join(rcParams['datapath'],'fonts','afm')]
@@ -899,7 +899,9 @@
def get_default_size(self):
"Return the default font size."
- return self.__default_size
+ if self.default_size is None:
+ return rcParams['font.size']
+ return self.default_size
def set_default_weight(self, weight):
"Set the default font weight. The initial value is 'normal'."
@@ -1085,6 +1087,7 @@
try:
fontManager = pickle_load(_fmcache)
+ fontManager.default_size = None
verbose.report("Using fontManager instance from %s" % _fmcache)
except:
_rebuild()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-19 19:48:22
|
Revision: 3860
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3860&view=rev
Author: mdboom
Date: 2007-09-19 12:48:17 -0700 (Wed, 19 Sep 2007)
Log Message:
-----------
Use iterator rather than caching approach for paths
Modified Paths:
--------------
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/src/_backend_agg.cpp
branches/transforms/src/_backend_agg.h
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-19 19:46:34 UTC (rev 3859)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-19 19:48:17 UTC (rev 3860)
@@ -112,8 +112,7 @@
self.dpi = dpi
self.width = width
self.height = height
- if __debug__: verbose.report('RendererAgg.__init__ width=%s, \
- height=%s'%(width, height), 'debug-annoying')
+ if __debug__: verbose.report('RendererAgg.__init__ width=%s, height=%s'%(width, height), 'debug-annoying')
self._renderer = _RendererAgg(int(width), int(height), dpi,
debug=False)
if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done',
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-19 19:46:34 UTC (rev 3859)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-19 19:48:17 UTC (rev 3860)
@@ -87,15 +87,17 @@
inline void get_next_vertex(const char* & vertex_i, const char* vertex_end,
double& x, double& y,
size_t next_vertex_stride,
- size_t next_axis_stride) {
+ size_t next_axis_stride,
+ const char* & code_i, size_t code_stride) {
if (vertex_i + next_axis_stride >= vertex_end)
throw Py::ValueError("Error parsing path. Read past end of vertices");
x = *(double*)vertex_i;
y = *(double*)(vertex_i + next_axis_stride);
vertex_i += next_vertex_stride;
+ code_i += code_stride;
}
-#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride)
+#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride, code_i, code_stride)
Py::Object BufferRegion::to_string(const Py::Tuple &args) {
@@ -103,7 +105,71 @@
return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true);
}
+class PathIterator {
+ PyArrayObject* vertices;
+ PyArrayObject* codes;
+ size_t m_iterator;
+ size_t m_total_vertices;
+public:
+ PathIterator(const Py::Object& path_obj) :
+ vertices(NULL), codes(NULL), m_iterator(0) {
+ Py::Object vertices_obj = path_obj.getAttr("vertices");
+ Py::Object codes_obj = path_obj.getAttr("codes");
+
+ vertices = (PyArrayObject*)PyArray_ContiguousFromObject
+ (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2);
+ if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2)
+ throw Py::ValueError("Invalid vertices array.");
+ codes = (PyArrayObject*)PyArray_ContiguousFromObject
+ (codes_obj.ptr(), PyArray_UINT8, 1, 1);
+ if (!codes)
+ throw Py::ValueError("Invalid codes array.");
+
+ if (codes->dimensions[0] != vertices->dimensions[0])
+ throw Py::ValueError("Vertices and codes array are not the same length.");
+
+ m_total_vertices = codes->dimensions[0];
+ }
+
+ ~PathIterator() {
+ Py_XDECREF(vertices);
+ Py_XDECREF(codes);
+ }
+
+ static const char code_map[];
+
+ inline unsigned vertex(unsigned idx, double* x, double* y) {
+ if (idx > m_total_vertices)
+ throw Py::RuntimeError("Requested vertex past end");
+ double* pv = (double*)(vertices->data + (idx * vertices->strides[0]));
+ *x = *pv++;
+ *y = *pv;
+ // MGDTODO: Range check
+ return code_map[(unsigned int)*(codes->data + (idx * codes->strides[0]))];
+ }
+
+ inline unsigned vertex(double* x, double* y) {
+ if(m_iterator >= m_total_vertices) return agg::path_cmd_stop;
+ return vertex(m_iterator++, x, y);
+ }
+
+ inline void rewind(unsigned path_id) {
+ m_iterator = path_id;
+ }
+
+ inline unsigned total_vertices() {
+ return m_total_vertices;
+ }
+};
+
+const char PathIterator::code_map[] = {0,
+ agg::path_cmd_move_to,
+ agg::path_cmd_line_to,
+ agg::path_cmd_curve3,
+ agg::path_cmd_curve4,
+ agg::path_cmd_end_poly | agg::path_flags_close};
+
GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) :
dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0),
cliprect(NULL), clippath(NULL),
@@ -634,7 +700,7 @@
if (num_vertices) {
for (size_t j=0; j<num_vertices; ++j)
GET_NEXT_VERTEX(x, y);
- if (code_i == IGNORE)
+ if (*code_i == STOP || *code_i == CLOSEPOLY)
continue;
trans.transform(&x, &y);
@@ -863,9 +929,10 @@
for (size_t i = 0; i < N; ++i) {
switch (*(unsigned char*)(code_i)) {
- case IGNORE:
+ case STOP:
GET_NEXT_VERTEX(x0, y0);
- _VERBOSE("IGNORE");
+ _VERBOSE("STOP");
+ // MGDTODO: If this isn't the end, we should raise an error
break;
case MOVETO:
GET_NEXT_VERTEX(x0, y0);
@@ -894,10 +961,10 @@
break;
case CLOSEPOLY:
close_polygon();
+ GET_NEXT_VERTEX(x0, y0);
_VERBOSE("CLOSEPOLY");
break;
}
- code_i += code_stride;
}
} catch(...) {
Py_XDECREF(vertices);
@@ -911,7 +978,7 @@
Py::Object
RendererAgg::draw_path(const Py::Tuple& args) {
- typedef agg::conv_transform<agg::path_storage> transformed_path_t;
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
typedef agg::conv_curve<transformed_path_t> curve_t;
typedef agg::conv_stroke<curve_t> stroke_t;
typedef agg::conv_dash<curve_t> dash_t;
@@ -928,9 +995,11 @@
GCAgg gc = GCAgg(args[0], dpi);
Py::Object path_obj = args[1];
- if (!PathAgg::check(path_obj))
- throw Py::TypeError("Native path object is not of correct type");
- PathAgg* path = static_cast<PathAgg*>(path_obj.ptr());
+// if (!PathAgg::check(path_obj))
+// throw Py::TypeError("Native path object is not of correct type");
+ // PathAgg* path = static_cast<PathAgg*>(path_obj.ptr());
+ PathIterator path(path_obj);
+
agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]);
facepair_t face = _get_rgba_face(args[3], gc.alpha);
@@ -943,34 +1012,34 @@
bool has_clippath = (gc.clippath != NULL);
if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) {
- rendererBaseAlphaMask->clear(agg::gray8(0, 0));
- gc.clippath->rewind(0);
- transformed_path_t transformed_clippath(*(gc.clippath), trans);
- theRasterizer->add_path(transformed_clippath);
- rendererAlphaMask->color(agg::gray8(255, 255));
- agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask);
- lastclippath = gc.clippath;
- lastclippath_transform = trans;
+// rendererBaseAlphaMask->clear(agg::gray8(0, 0));
+// gc.clippath->rewind(0);
+// transformed_path_t transformed_clippath(*(gc.clippath), trans);
+// theRasterizer->add_path(transformed_clippath);
+// rendererAlphaMask->color(agg::gray8(255, 255));
+// agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask);
+// lastclippath = gc.clippath;
+// lastclippath_transform = trans;
}
try {
// If this is a straight horizontal or vertical line, quantize to nearest
// pixels
- if (path->total_vertices() == 2) {
- double x0, y0, x1, y1;
- path->vertex(0, &x0, &y0);
- trans.transform(&x0, &y0);
- path->vertex(1, &x1, &y1);
- trans.transform(&x1, &y1);
- if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) {
- new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5);
- new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5);
- tpath = new transformed_path_t(new_path, agg::trans_affine());
- }
- }
+// if (path.total_vertices() == 2) {
+// double x0, y0, x1, y1;
+// path.vertex(0, &x0, &y0);
+// trans.transform(&x0, &y0);
+// path.vertex(1, &x1, &y1);
+// trans.transform(&x1, &y1);
+// if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) {
+// new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5);
+// new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5);
+// tpath = new transformed_path_t(new_path, agg::trans_affine());
+// }
+// }
if (!tpath) {
- tpath = new transformed_path_t(*path, trans);
+ tpath = new transformed_path_t(path, trans);
}
// Benchmarking shows that there is no noticable slowdown to always
Modified: branches/transforms/src/_backend_agg.h
===================================================================
--- branches/transforms/src/_backend_agg.h 2007-09-19 19:46:34 UTC (rev 3859)
+++ branches/transforms/src/_backend_agg.h 2007-09-19 19:48:17 UTC (rev 3860)
@@ -40,14 +40,14 @@
#include "agg_vcgen_markers_term.h"
// These are copied directly from path.py, and must be kept in sync
-#define IGNORE 0
+#define STOP 0
#define MOVETO 1
#define LINETO 2
#define CURVE3 3
#define CURVE4 4
#define CLOSEPOLY 5
-const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 };
+const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 1 };
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-19 19:46:41
|
Revision: 3859
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3859&view=rev
Author: mdboom
Date: 2007-09-19 12:46:34 -0700 (Wed, 19 Sep 2007)
Log Message:
-----------
Lots of minor fixes
Modified Paths:
--------------
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/patches.py
branches/transforms/lib/matplotlib/path.py
branches/transforms/lib/matplotlib/pbox.py
branches/transforms/lib/matplotlib/text.py
branches/transforms/lib/matplotlib/transforms.py
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-19 16:18:51 UTC (rev 3858)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 19:46:34 UTC (rev 3859)
@@ -252,12 +252,9 @@
if not is_numlike(self.pickradius):
raise ValueError,"pick radius should be a distance"
- if self._newstyle:
- # transform in backend
- x = self._x
- y = self._y
- else:
- x, y = self._get_plottable()
+ # transform in backend
+ x = self._x
+ y = self._y
if len(x)==0: return False,{}
xt, yt = self.get_transform().numerix_x_y(x, y)
@@ -337,7 +334,6 @@
ACCEPTS: (npy.array xdata, npy.array ydata)
"""
-
if len(args)==1:
x, y = args[0]
else:
@@ -347,8 +343,9 @@
self._yorig = y
self.recache()
+ # MGDTODO: Masked data arrays are broken
_masked_array_to_path_code_mapping = npy.array(
- [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type)
+ [Path.LINETO, Path.MOVETO, Path.MOVETO], Path.code_type)
def recache(self):
#if self.axes is None: print 'recache no axes'
#else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units
@@ -387,18 +384,18 @@
# MGDTODO: If _draw_steps is removed, remove the following line also
self._step_path = None
-
def _is_sorted(self, x):
"return true if x is sorted"
if len(x)<2: return 1
return npy.alltrue(x[1:]-x[0:-1]>=0)
+ # MGDTODO: Remove me (seems to be used for old-style interface only)
def _get_plottable(self):
# If log scale is set, only pos data will be returned
x, y = self._x, self._y
- # MGDTODO: Deal with the log scale here
+ # MGDTODO: (log-scaling)
# try: logx = self.get_transform().get_funcx().get_type()==LOG10
# except RuntimeError: logx = False # non-separable
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-09-19 16:18:51 UTC (rev 3858)
+++ branches/transforms/lib/matplotlib/patches.py 2007-09-19 19:46:34 UTC (rev 3859)
@@ -319,8 +319,6 @@
return str(self.__class__).split('.')[-1] \
+ "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height)
- # MGDTODO: Perhaps pass in a Bbox here instead, then the updates will
- # happen automatically (without needing to call set_x etc.
def __init__(self, xy, width, height, **kwargs):
"""
xy is an x,y tuple lower, left
@@ -459,17 +457,14 @@
def __init__(self, xy, **kwargs):
"""
- xy is a sequence of (x,y) 2 tuples
+ xy is a numpy array with shape Nx2
Valid kwargs are:
%(Patch)s
See Patch documentation for additional kwargs
"""
- # MGDTODO: This should encourage the use of numpy arrays of shape Nx2
Patch.__init__(self, **kwargs)
- if not isinstance(xy, list):
- xy = list(xy)
- self._path = Path(xy, closed=False)
+ self._path = Path(xy, closed=True)
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
def get_verts(self):
Modified: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py 2007-09-19 16:18:51 UTC (rev 3858)
+++ branches/transforms/lib/matplotlib/path.py 2007-09-19 19:46:34 UTC (rev 3859)
@@ -1,15 +1,13 @@
import numpy as npy
-DEBUG = True
-
class Path(object):
# Path codes
- IGNORE = 0 # 1 vertex
+ STOP = 0 # 1 vertex
MOVETO = 1 # 1 vertex
LINETO = 2 # 1 vertex
CURVE3 = 3 # 2 vertices
CURVE4 = 4 # 3 vertices
- CLOSEPOLY = 5
+ CLOSEPOLY = 5 # 1 vertex
###
# MGDTODO: I'm not sure these are supported by PS/PDF/SVG,
# so if they don't, we probably shouldn't
@@ -18,38 +16,36 @@
UBSPLINE = 8
####
- NUM_VERTICES = [1, 1, 1, 2, 3, 0]
+ NUM_VERTICES = [1, 1, 1, 2, 3, 1]
code_type = npy.uint8
def __init__(self, vertices, codes=None, closed=True):
- self._vertices = npy.asarray(vertices, npy.float_)
- assert self._vertices.ndim == 2
- assert self._vertices.shape[1] == 2
-
+ vertices = npy.asarray(vertices, npy.float_)
+ assert vertices.ndim == 2
+ assert vertices.shape[1] == 2
+
if codes is None:
if closed:
codes = self.LINETO * npy.ones(
- self._vertices.shape[0] + 1, self.code_type)
+ vertices.shape[0] + 1, self.code_type)
codes[0] = self.MOVETO
- codes[-1] = self.CLOSEPOLY
+ codes[-1] = self.CLOSEPOLY
+ vertices = npy.concatenate((vertices, [[0.0, 0.0]]))
else:
codes = self.LINETO * npy.ones(
- self._vertices.shape[0], self.code_type)
+ vertices.shape[0], self.code_type)
codes[0] = self.MOVETO
else:
codes = npy.asarray(codes, self.code_type)
- self._codes = codes
-
+ assert codes.ndim == 1
+ assert len(codes) == len(vertices)
+
+ self._codes = codes
+ self._vertices = vertices
+
assert self._codes.ndim == 1
- if DEBUG:
- i = 0
- NUM_VERTICES = self.NUM_VERTICES
- for code in codes:
- i += NUM_VERTICES[code]
- assert i == len(self.vertices)
-
def __repr__(self):
return "Path(%s, %s)" % (self.vertices, self.codes)
@@ -66,11 +62,13 @@
NUM_VERTICES = self.NUM_VERTICES
vertices = self.vertices
for code in self.codes:
- num_vertices = NUM_VERTICES[code]
- if num_vertices >= 1:
- i += num_vertices - 1
- yield vertices[i]
- i += 1
+ if code == self.CLOSEPOLY:
+ i += 1
+ else:
+ num_vertices = NUM_VERTICES[code]
+ i += num_vertices - 1
+ yield vertices[i]
+ i += 1
_unit_rectangle = None
#@classmethod
@@ -118,16 +116,18 @@
[-offset, -1.0],
[-1.0, -offset],
- [-1.0, 0.0]],
- npy.float_)
- codes = npy.array(
- [cls.MOVETO,
- cls.CURVE4,
- cls.CURVE4,
- cls.CURVE4,
- cls.CURVE4,
- cls.CLOSEPOLY],
- cls.code_type)
+ [-1.0, 0.0],
+
+ [-1.0, 0.0]],
+ npy.float_)
+
+ codes = cls.CURVE4 + npy.ones((len(vertices)))
+ codes[0] = cls.MOVETO
+ codes[-1] = cls.CLOSEPOLY
+
cls._unit_circle = Path(vertices, codes)
return cls._unit_circle
unit_circle = classmethod(unit_circle)
+
+# MGDTODO: Add a transformed path that would automatically invalidate
+# itself when its transform changes
Modified: branches/transforms/lib/matplotlib/pbox.py
===================================================================
--- branches/transforms/lib/matplotlib/pbox.py 2007-09-19 16:18:51 UTC (rev 3858)
+++ branches/transforms/lib/matplotlib/pbox.py 2007-09-19 19:46:34 UTC (rev 3859)
@@ -1,5 +1,3 @@
-# MGDTODO: Just included verbatim for now
-
class PBox(list):
'''
A left-bottom-width-height (lbwh) specification of a bounding box,
Modified: branches/transforms/lib/matplotlib/text.py
===================================================================
--- branches/transforms/lib/matplotlib/text.py 2007-09-19 16:18:51 UTC (rev 3858)
+++ branches/transforms/lib/matplotlib/text.py 2007-09-19 19:46:34 UTC (rev 3859)
@@ -231,7 +231,7 @@
# now rotate the bbox
- cornersRotated = M(cornersHoriz)
+ cornersRotated = M.transform(cornersHoriz)
txs = cornersRotated[:, 0]
tys = cornersRotated[:, 1]
@@ -269,7 +269,7 @@
# now rotate the positions around the first x,y position
- xys = M(offsetLayout)
+ xys = M.transform(offsetLayout)
tx = xys[:, 0]
ty = xys[:, 1]
tx += offsetx
@@ -277,7 +277,7 @@
# now inverse transform back to data coords
inverse_transform = self.get_transform().inverted()
- xys = inverse_transform(xys)
+ xys = inverse_transform.transform(xys)
xs, ys = zip(*xys)
@@ -407,7 +407,7 @@
return (x, y, self._text, self._color,
self._verticalalignment, self._horizontalalignment,
hash(self._fontproperties), self._rotation,
- self.get_transform().to_values(),
+ self.get_transform(),
)
def get_text(self):
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-19 16:18:51 UTC (rev 3858)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 19:46:34 UTC (rev 3859)
@@ -32,7 +32,8 @@
for child in children:
getattr(self, child)._parents.add(self)
self._children = children
-
+
+
class BboxBase(TransformNode):
'''
This is the read-only part of a bounding-box
@@ -293,6 +294,7 @@
return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
union = staticmethod(union)
+
class TransformedBbox(BboxBase):
def __init__(self, bbox, transform):
assert isinstance(bbox, Bbox)
@@ -313,16 +315,20 @@
def get_points(self):
if self._points is None:
- self._points = self.transform(self.bbox.get_points())
+ self._points = self.transform.transform(self.bbox.get_points())
return self._points
+
class Transform(TransformNode):
def __init__(self):
TransformNode.__init__(self)
- def __call__(self, points):
+ def transform(self, points):
raise NotImplementedError()
+ def transform_without_affine(self, points):
+ return self.transform(points), IDENTITY
+
def __add__(self, other):
if isinstance(other, Transform):
return composite_transform_factory(self, other)
@@ -336,7 +342,7 @@
"Can not add Transform to object of type '%s'" % type(other))
def transform_point(self, point):
- return self.__call__(npy.asarray([point]))[0]
+ return self.transform(npy.asarray([point]))[0]
def has_inverse(self):
raise NotImplementedError()
@@ -350,6 +356,7 @@
def is_affine(self):
return False
+
class Affine2DBase(Transform):
input_dims = 2
output_dims = 2
@@ -390,7 +397,7 @@
def get_matrix(self):
raise NotImplementedError()
- def __call__(self, points):
+ def transform(self, points):
"""
Applies the transformation to an array of 2D points and
returns the result.
@@ -414,6 +421,11 @@
points = npy.dot(mtx[0:2, 0:2], points)
points = points + mtx[0:2, 2:]
return points.transpose()
+
+ def transform_without_affine(self, points):
+ # MGDTODO: Should we copy the points here? I'd like to avoid it,
+ # if possible
+ return points, self
def inverted(self):
if self._inverted is None:
@@ -430,9 +442,6 @@
class Affine2D(Affine2DBase):
- input_dims = 2
- output_dims = 2
-
def __init__(self, matrix = None):
"""
Initialize an Affine transform from a 3x3 numpy float array.
@@ -535,40 +544,82 @@
def is_affine(self):
return True
+
+IDENTITY = Affine2D()
class BlendedGenericTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+
def __init__(self, x_transform, y_transform):
# Here we ask: "Does it blend?"
assert x_transform.is_separable()
assert y_transform.is_separable()
-
+ assert x_transform.input_dims == x_transform.output_dims == 2
+ assert y_transform.input_dims == y_transform.output_dims == 2
+
Transform.__init__(self)
self._x = x_transform
self._y = y_transform
self.set_children(['_x', '_y'])
- def __call__(self, points):
- if self._x == self._y:
+ def transform(self, points):
+ # MGDTODO: Optimize the case where one of these is
+ # an affine
+ x = self._x
+ y = self._y
+ if x == y and x.input_dims == 2:
return self._x(points)
-
- x_points = self._x(points)
- y_points = self._y(points)
- # This works because we already know the transforms are
- # separable
- return npy.hstack((x_points[:, 0:1], y_points[:, 1:2]))
-# def set_x_transform(self, x_transform):
-# self.replace_child(0, x_transform)
+ if x.input_dims == 2:
+ x_points = x.transform(points)[:, 0]
+ else:
+ x_points = x.transform(points[:, 0])
-# def set_y_transform(self, y_transform):
-# self.replace_child(1, y_transform)
+ if y.input_dims == 2:
+ y_points = y.transform(points)[:, 1]
+ else:
+ y_points = y.transform(points[:, 1])
+ return npy.vstack((x_points, y_points)).transpose()
+
+ def inverted(self):
+ return BlendedGenericTransform(self._x.inverted(), self._y.inverted())
-class BlendedAffine2D(Affine2DBase, BlendedGenericTransform):
+ def is_separable(self):
+ return True
+
+
+class BlendedSeparableTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+
def __init__(self, x_transform, y_transform):
+ # Here we ask: "Does it blend?"
+ assert x_transform.is_separable()
+ assert y_transform.is_separable()
+ assert x_transform.input_dims == x.transform.output_dims == 1
+ assert y_transform.input_dims == y.transform.output_dims == 1
+
+ Transform.__init__(self)
+ self._x = x_transform
+ self._y = y_transform
+ self.set_children(['_x', '_y'])
+
+ def transform(self, points):
+ x_points = self._x(points[:, 0])
+ y_points = self._y(points[:, 1])
+ return npy.vstack((x_points[:, 0:1], y_points[:, 1:2])).transpose()
+
+
+class BlendedAffine2D(Affine2DBase, Transform):
+ def __init__(self, x_transform, y_transform):
assert x_transform.is_affine()
assert y_transform.is_affine()
- BlendedGenericTransform.__init__(self, x_transform, y_transform)
+ Transform.__init__(self)
+ self._x = x_transform
+ self._y = y_transform
+ self.set_children(['_x', '_y'])
Affine2DBase.__init__(self)
self._mtx = None
@@ -597,12 +648,14 @@
# c to zero.
self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
return self._mtx
-
+
+
def blended_transform_factory(x_transform, y_transform):
if x_transform.is_affine() and y_transform.is_affine():
return BlendedAffine2D(x_transform, y_transform)
return BlendedGenericTransform(x_transform, y_transform)
+
class CompositeGenericTransform(Transform):
def __init__(self, a, b):
assert a.output_dims == b.input_dims
@@ -614,9 +667,17 @@
self._b = b
self.set_children(['_a', '_b'])
- def __call__(self, points):
- return self._b(self._a(points))
+ def transform(self, points):
+ return self._b.transform(self._a.transform(points))
+
+ def inverted(self):
+ return CompositeGenericTransform(self._b.inverted(), self._a.inverted())
+ def is_separable(self):
+ return True
+ return self._a.is_separable() and self._b.is_separable()
+
+
class CompositeAffine2D(Affine2DBase):
def __init__(self, a, b):
assert a.is_affine()
@@ -643,11 +704,32 @@
self._b.get_matrix())
return self._mtx
+
def composite_transform_factory(a, b):
if a.is_affine() and b.is_affine():
return CompositeAffine2D(a, b)
return CompositeGenericTransform(a, b)
+
+
+class LogTransform(Transform):
+ input_dims = 1
+ output_dims = 1
+ def transform(self, a):
+ m = npy.ma.masked_where(a < 0, a)
+ return npy.log10(m)
+
+
+class TestLogTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+ def transform(self, xy):
+ return xy * 2
+
+ def inverted(self):
+ return self
+
+
class BboxTransform(Affine2DBase):
def __init__(self, boxin, boxout):
assert isinstance(boxin, BboxBase)
@@ -688,6 +770,7 @@
self._mtx = affine._mtx
return self._mtx
+
def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True):
'''
Ensure the endpoints of a range are not too close together.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-19 16:18:56
|
Revision: 3858
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3858&view=rev
Author: mdboom
Date: 2007-09-19 09:18:51 -0700 (Wed, 19 Sep 2007)
Log Message:
-----------
Got steps_demo.py working
Modified Paths:
--------------
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/path.py
branches/transforms/lib/matplotlib/transforms.py
branches/transforms/src/_backend_agg.cpp
branches/transforms/src/_backend_agg.h
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-19 13:28:11 UTC (rev 3857)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-19 16:18:51 UTC (rev 3858)
@@ -40,7 +40,7 @@
def _get_cached_native_path(self, path):
native_path = self._native_paths.get(path)
if native_path is None:
- # print "CACHE MISS", path
+ print "CACHE MISS", path
native_path = self.convert_to_native_path(path)
self._native_paths[path] = native_path
return native_path
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-19 13:28:11 UTC (rev 3857)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 16:18:51 UTC (rev 3858)
@@ -25,53 +25,6 @@
(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN,
CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN) = range(8)
-def unmasked_index_ranges(mask, compressed = True):
- '''
- Calculate the good data ranges in a masked 1-D npy.array, based on mask.
-
- Returns Nx2 npy.array with each row the start and stop indices
- for slices of the compressed npy.array corresponding to each of N
- uninterrupted runs of unmasked values.
- If optional argument compressed is False, it returns the
- start and stop indices into the original npy.array, not the
- compressed npy.array.
- Returns None if there are no unmasked values.
-
- Example:
-
- y = ma.array(npy.arange(5), mask = [0,0,1,0,0])
- #ii = unmasked_index_ranges(y.mask())
- ii = unmasked_index_ranges(ma.getmask(y))
- # returns [[0,2,] [2,4,]]
-
- y.compressed().filled()[ii[1,0]:ii[1,1]]
- # returns npy.array [3,4,]
- # (The 'filled()' method converts the masked npy.array to a numerix npy.array.)
-
- #i0, i1 = unmasked_index_ranges(y.mask(), compressed=False)
- i0, i1 = unmasked_index_ranges(ma.getmask(y), compressed=False)
- # returns [[0,3,] [2,5,]]
-
- y.filled()[ii[1,0]:ii[1,1]]
- # returns npy.array [3,4,]
-
- '''
- m = npy.concatenate(((1,), mask, (1,)))
- indices = npy.arange(len(mask) + 1)
- mdif = m[1:] - m[:-1]
- i0 = npy.compress(mdif == -1, indices)
- i1 = npy.compress(mdif == 1, indices)
- assert len(i0) == len(i1)
- if len(i1) == 0:
- return None
- if not compressed:
- return npy.concatenate((i0[:, npy.newaxis], i1[:, npy.newaxis]), axis=1)
- seglengths = i1 - i0
- breakpoints = npy.cumsum(seglengths)
- ic0 = npy.concatenate(((0,), breakpoints[:-1]))
- ic1 = breakpoints
- return npy.concatenate((ic0[:, npy.newaxis], ic1[:, npy.newaxis]), axis=1)
-
def segment_hits(cx,cy,x,y,radius):
"""Determine if any line segments are within radius of a point. Returns
the list of line segments that are within that radius.
@@ -117,7 +70,7 @@
'--' : '_draw_dashed',
'-.' : '_draw_dash_dot',
':' : '_draw_dotted',
- 'steps': '_draw_solid',
+ 'steps': '_draw_steps',
'None' : '_draw_nothing',
' ' : '_draw_nothing',
'' : '_draw_nothing',
@@ -394,6 +347,8 @@
self._yorig = y
self.recache()
+ _masked_array_to_path_code_mapping = npy.array(
+ [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type)
def recache(self):
#if self.axes is None: print 'recache no axes'
#else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units
@@ -411,36 +366,28 @@
if len(x) != len(y):
raise RuntimeError('xdata and ydata must be the same length')
- # MGDTODO: Deal with segments
+ self._xy = npy.vstack((npy.asarray(x, npy.float_),
+ npy.asarray(y, npy.float_))).transpose()
+ self._x = self._xy[:, 0] # just a view
+ self._y = self._xy[:, 1] # just a view
+ self._logcache = None
+
mx = ma.getmask(x)
my = ma.getmask(y)
mask = ma.mask_or(mx, my)
+ codes = None
if mask is not ma.nomask:
- x = ma.masked_array(x, mask=mask).compressed()
- y = ma.masked_array(y, mask=mask).compressed()
- self._segments = unmasked_index_ranges(mask)
- else:
- self._segments = None
-
- self._xy = npy.vstack((npy.asarray(x, npy.float_),
- npy.asarray(y, npy.float_))).transpose()
- self._x = self._xy[:, 0]
- self._y = self._xy[:, 1]
- self._logcache = None
+ m = npy.concatenate(((1,), mask, (1,)))
+ mdif = m[1:] - m[:-1]
+ mdif = npy.maximum((mdif[:-1] * -2), mask)
+ codes = npy.take(
+ self._masked_array_to_path_code_mapping,
+ mdif)
+ self._path = Path(self._xy, codes, closed=False)
+ # MGDTODO: If _draw_steps is removed, remove the following line also
+ self._step_path = None
- if self._linestyle == 'steps':
- siz=len(xt)
- if siz<2: return
- xt, yt = self._x, self._y
- xt2=npy.ones((2*siz,), xt.dtype)
- xt2[0:-1:2], xt2[1:-1:2], xt2[-1] = xt, xt[1:], xt[-1]
- yt2=npy.ones((2*siz,), yt.dtype)
- yt2[0:-1:2], yt2[1::2] = yt, yt
- self._path = Path(npy.vstack((xt2, yt2)).transpose(), closed=False)
- else:
- self._path = Path(self._xy, closed=False)
-
def _is_sorted(self, x):
"return true if x is sorted"
if len(x)<2: return 1
@@ -507,14 +454,7 @@
funcname = self._lineStyles.get(self._linestyle, '_draw_nothing')
lineFunc = getattr(self, funcname)
-
- # MGDTODO: Deal with self._segments
- if self._segments is not None:
- for ii in self._segments:
- lineFunc(renderer, gc, xt[ii[0]:ii[1]], yt[ii[0]:ii[1]])
-
- else:
- lineFunc(renderer, gc, self._path)
+ lineFunc(renderer, gc, self._path)
# MGDTODO: Deal with markers
if self._marker is not None:
@@ -709,7 +649,29 @@
def _draw_nothing(self, renderer, gc, path):
pass
-
+
+ def _draw_steps(self, renderer, gc, path):
+ # We generate the step function path on-the-fly, and then cache it.
+ # The cache may be later invalidated when the data changes
+ # (in self.recache())
+
+ # MGDTODO: Untested -- using pylab.step doesn't actually trigger
+ # this code -- the path is "stepped" before even getting to this
+ # class. Perhaps this should be removed here, since it is not as
+ # powerful as what is in axes.step() anyway.
+ if self._step_path is None:
+ vertices = self._path.vertices
+ codes = self._path.codes
+ siz = len(vertices)
+ if siz<2: return
+ new_vertices = npy.zeros((2*siz, 2), vertices.dtype)
+ new_vertices[0:-1:2, 0], new_vertices[1:-1:2, 0], newvertices[-1, 0] = vertices[:, 0], vertices[1:, 0], vertices[-1, 0]
+ new_vertices[0:-1:2, 1], new_vertices[1::2, 1] = vertices[:, 1], vertices[:, 1]
+ self._step_path = Path(new_vertices, closed=False)
+ gc.set_linestyle('solid')
+ renderer.draw_path(gc, self._step_path, self.get_transform())
+
+
def _draw_solid(self, renderer, gc, path):
gc.set_linestyle('solid')
renderer.draw_path(gc, path, self.get_transform())
Modified: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py 2007-09-19 13:28:11 UTC (rev 3857)
+++ branches/transforms/lib/matplotlib/path.py 2007-09-19 16:18:51 UTC (rev 3858)
@@ -1,10 +1,10 @@
import numpy as npy
-VALIDATE_PATHS = True
+DEBUG = True
class Path(object):
# Path codes
- STOP = 0
+ IGNORE = 0 # 1 vertex
MOVETO = 1 # 1 vertex
LINETO = 2 # 1 vertex
CURVE3 = 3 # 2 vertices
@@ -18,7 +18,7 @@
UBSPLINE = 8
####
- NUM_VERTICES = [0, 1, 1, 2, 3, 0]
+ NUM_VERTICES = [1, 1, 1, 2, 3, 0]
code_type = npy.uint8
@@ -43,7 +43,7 @@
assert self._codes.ndim == 1
- if VALIDATE_PATHS:
+ if DEBUG:
i = 0
NUM_VERTICES = self.NUM_VERTICES
for code in codes:
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-19 13:28:11 UTC (rev 3857)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 16:18:51 UTC (rev 3858)
@@ -32,18 +32,6 @@
for child in children:
getattr(self, child)._parents.add(self)
self._children = children
-
- # MGDTODO: decide whether we need this in-place updating and
- # remove if not
-# def replace_child(self, index, child):
-# children = self._children
-# getattr(self, children[index])._parents.remove(self)
-# setattr(self, children[index], child)
-# # We have to reset children in case two or more
-# # of the children are the same
-# for child in children:
-# getattr(self, child)._parents.add(self)
-# self.invalidate()
class BboxBase(TransformNode):
'''
@@ -327,53 +315,7 @@
if self._points is None:
self._points = self.transform(self.bbox.get_points())
return self._points
-
-# MGDTODO: This code probably works, but I don't think it's a good idea
-# (from a code clarity perspective)
-# class BlendedBbox(BboxBase):
-# def __init__(self, bbox_x, bbox_y):
-# assert isinstance(bbox_x, BboxBase)
-# assert isinstance(bbox_y, BboxBase)
-
-# BboxBase.__init__(self)
-# self._x = bbox_x
-# self._y = bbox_y
-# self.set_children(['_x', '_y'])
-# self._points = None
-
-# def __repr__(self):
-# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
-# __str__ = __repr__
-# def _do_invalidation(self):
-# self._points = None
-
-# def get_points(self):
-# if self._points is None:
-# # MGDTODO: Optimize
-# if self._x == self._y:
-# self._points = self._x.get_points()
-# else:
-# x_points = self._x.get_points()
-# y_points = self._y.get_points()
-# self._points = npy.array(
-# [[x_points[0,0], y_points[0,1]],
-# [x_points[1,0], y_points[1,1]]],
-# npy.float_)
-# return self._points
-
-# def _set_intervalx(self, pair):
-# # MGDTODO: Optimize
-# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]])
-# self.replace_child(0, bbox)
-# intervalx = property(BboxBase._get_intervalx, _set_intervalx)
-
-# def _set_intervaly(self, pair):
-# # MGDTODO: Optimize
-# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]])
-# self.replace_child(1, bbox)
-# intervaly = property(BboxBase._get_intervaly, _set_intervaly)
-
class Transform(TransformNode):
def __init__(self):
TransformNode.__init__(self)
@@ -746,7 +688,6 @@
self._mtx = affine._mtx
return self._mtx
-# MGDTODO: There's probably a better place for this
def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True):
'''
Ensure the endpoints of a range are not too close together.
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-19 13:28:11 UTC (rev 3857)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-19 16:18:51 UTC (rev 3858)
@@ -634,6 +634,9 @@
if (num_vertices) {
for (size_t j=0; j<num_vertices; ++j)
GET_NEXT_VERTEX(x, y);
+ if (code_i == IGNORE)
+ continue;
+
trans.transform(&x, &y);
if (face.first) {
@@ -860,6 +863,10 @@
for (size_t i = 0; i < N; ++i) {
switch (*(unsigned char*)(code_i)) {
+ case IGNORE:
+ GET_NEXT_VERTEX(x0, y0);
+ _VERBOSE("IGNORE");
+ break;
case MOVETO:
GET_NEXT_VERTEX(x0, y0);
move_to(x0, y0);
Modified: branches/transforms/src/_backend_agg.h
===================================================================
--- branches/transforms/src/_backend_agg.h 2007-09-19 13:28:11 UTC (rev 3857)
+++ branches/transforms/src/_backend_agg.h 2007-09-19 16:18:51 UTC (rev 3858)
@@ -40,14 +40,14 @@
#include "agg_vcgen_markers_term.h"
// These are copied directly from path.py, and must be kept in sync
-#define STOP 0
+#define IGNORE 0
#define MOVETO 1
#define LINETO 2
#define CURVE3 3
#define CURVE4 4
#define CLOSEPOLY 5
-const size_t NUM_VERTICES[] = { 0, 1, 1, 2, 3, 0 };
+const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 };
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-19 13:28:21
|
Revision: 3857
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3857&view=rev
Author: mdboom
Date: 2007-09-19 06:28:11 -0700 (Wed, 19 Sep 2007)
Log Message:
-----------
Got legend working with new transforms
Modified Paths:
--------------
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/legend.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/patches.py
branches/transforms/lib/matplotlib/transforms.py
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-18 19:29:21 UTC (rev 3856)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-19 13:28:11 UTC (rev 3857)
@@ -632,6 +632,7 @@
self.transAxes = mtransforms.BboxTransform(
mtransforms.Bbox.unit(), self.bbox)
+ # self.set_transform(self.transAxes)
self.transData = mtransforms.BboxTransform(
self.viewLim, self.bbox)
@@ -724,6 +725,7 @@
self.axesPatch.set_figure(self.figure)
self.axesPatch.set_transform(self.transAxes)
self.axesPatch.set_linewidth(rcParams['axes.linewidth'])
+ # MGDTODO: What is axesFrame for? We already have axesPatch
self.axesFrame = mlines.Line2D((0,1,1,0,0), (0,0,1,1,0),
linewidth=rcParams['axes.linewidth'],
color=rcParams['axes.edgecolor'],
@@ -5201,7 +5203,7 @@
Subplot(211) # 2 rows, 1 column, first (upper) plot
"""
def __str__(self):
- return "Subplot(%g,%g)"%(self.bottom.get(),self.left.get())
+ return "Subplot(%f,%f,%f,%f)" % (self.bbox.bounds)
def __init__(self, fig, *args, **kwargs):
"""
Modified: branches/transforms/lib/matplotlib/legend.py
===================================================================
--- branches/transforms/lib/matplotlib/legend.py 2007-09-18 19:29:21 UTC (rev 3856)
+++ branches/transforms/lib/matplotlib/legend.py 2007-09-19 13:28:11 UTC (rev 3857)
@@ -164,7 +164,7 @@
else:
raise TypeError("Legend needs either Axes or Figure as parent")
self.parent = parent
- self.set_transform( BboxTransform( Bbox.unit(), parent.bbox) )
+ self.set_transform( BboxTransform(Bbox.unit(), parent.bbox) )
if loc is None:
loc = rcParams["legend.loc"]
@@ -223,7 +223,7 @@
a.set_transform(self.get_transform())
def _approx_text_height(self):
- return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height()
+ return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height
def draw(self, renderer):
@@ -531,7 +531,7 @@
def get_tbounds(text): #get text bounds in axes coords
bbox = text.get_window_extent(renderer)
bboxa = bbox.inverse_transformed(self.get_transform())
- return bboxa.get_bounds()
+ return bboxa.bounds
hpos = []
for t, tabove in zip(self.texts[1:], self.texts[:-1]):
@@ -560,10 +560,10 @@
# Set the data for the legend patch
bbox = copy.copy(self._get_handle_text_bbox(renderer))
- bbox = bbox.scaled(1 + self.pad, 1 + self.pad)
- l,b,w,h = bbox.get_bounds()
- self.legendPatch.set_bounds(l,b,w,h)
-
+ bbox = bbox.expanded(1 + self.pad, 1 + self.pad)
+ l, b, w, h = bbox.bounds
+ self.legendPatch.set_bounds(l, b, w, h)
+
ox, oy = 0, 0 # center
if iterable(self._loc) and len(self._loc)==2:
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-18 19:29:21 UTC (rev 3856)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 13:28:11 UTC (rev 3857)
@@ -117,7 +117,7 @@
'--' : '_draw_dashed',
'-.' : '_draw_dash_dot',
':' : '_draw_dotted',
- 'steps': '_draw_steps',
+ 'steps': '_draw_solid',
'None' : '_draw_nothing',
' ' : '_draw_nothing',
'' : '_draw_nothing',
@@ -352,10 +352,10 @@
self._picker = p
def get_window_extent(self, renderer):
- xys = self.get_transform()(self._xys)
+ xy = self.get_transform()(self._xy)
- x = xys[:, 0]
- y = xys[:, 1]
+ x = xy[:, 0]
+ y = xy[:, 1]
left = x.min()
bottom = y.min()
width = x.max() - left
@@ -426,9 +426,19 @@
npy.asarray(y, npy.float_))).transpose()
self._x = self._xy[:, 0]
self._y = self._xy[:, 1]
- self._path = Path(self._xy, closed=False)
-
self._logcache = None
+
+ if self._linestyle == 'steps':
+ siz=len(xt)
+ if siz<2: return
+ xt, yt = self._x, self._y
+ xt2=npy.ones((2*siz,), xt.dtype)
+ xt2[0:-1:2], xt2[1:-1:2], xt2[-1] = xt, xt[1:], xt[-1]
+ yt2=npy.ones((2*siz,), yt.dtype)
+ yt2[0:-1:2], yt2[1::2] = yt, yt
+ self._path = Path(npy.vstack((xt2, yt2)).transpose(), closed=False)
+ else:
+ self._path = Path(self._xy, closed=False)
def _is_sorted(self, x):
@@ -700,24 +710,6 @@
pass
- def _draw_steps(self, renderer, gc, xt, yt):
- # MGDTODO: This is a quirky one. The step-plotting part
- # should probably be moved to where the path is generated
- # in recache, and then just drawn with _draw_solid
- siz=len(xt)
- if siz<2: return
- xt2=npy.ones((2*siz,), xt.dtype)
- xt2[0:-1:2], xt2[1:-1:2], xt2[-1]=xt, xt[1:], xt[-1]
- yt2=npy.ones((2*siz,), yt.dtype)
- yt2[0:-1:2], yt2[1::2]=yt, yt
- gc.set_linestyle('solid')
-
- if self._newstyle:
- renderer.draw_lines(gc, xt2, yt2, self.get_transform())
- else:
- renderer.draw_lines(gc, xt2, yt2)
-
-
def _draw_solid(self, renderer, gc, path):
gc.set_linestyle('solid')
renderer.draw_path(gc, path, self.get_transform())
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-09-18 19:29:21 UTC (rev 3856)
+++ branches/transforms/lib/matplotlib/patches.py 2007-09-19 13:28:11 UTC (rev 3857)
@@ -212,8 +212,8 @@
gc.set_hatch(self._hatch )
path = self.get_path()
- transform = self.get_transform()
-
+ transform = self.get_patch_transform() + self.get_transform()
+
renderer.draw_path(gc, path, transform, rgbFace)
#renderer.close_group('patch')
@@ -284,7 +284,7 @@
self.patch = patch
self.props = props
self.ox, self.oy = ox, oy
- self._shadow_transform = transforms.Affine2D.translate(self.ox, self.oy)
+ self._shadow_transform = transforms.Affine2D().translate(self.ox, self.oy)
self._update()
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
@@ -305,8 +305,8 @@
def get_path(self):
return self.patch.get_path()
- def get_transform(self):
- return self._transform + self._shadow_transform
+ def get_patch_transform(self):
+ return self._shadow_transform
class Rectangle(Patch):
"""
@@ -315,8 +315,6 @@
"""
- _path = Path.unit_rectangle()
-
def __str__(self):
return str(self.__class__).split('.')[-1] \
+ "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height)
@@ -346,16 +344,14 @@
"""
Return the vertices of the rectangle
"""
- # This is a "class-static" variable, so all rectangles in the plot
- # will be shared (and merely have different transforms)
- return self._path
+ return Path.unit_rectangle()
# MGDTODO: Convert units
# left, right = self.convert_xunits((x, x + self.width))
# bottom, top = self.convert_yunits((y, y + self.height))
- def get_transform(self):
- return self._rect_transform + self._transform
+ def get_patch_transform(self):
+ return self._rect_transform
def get_x(self):
"Return the left coord of the rectangle"
@@ -379,7 +375,8 @@
ACCEPTS: float
"""
- self._bbox.xmin = x
+ w = self._bbox.width
+ self._bbox.intervalx = (x, x + w)
def set_y(self, y):
"""
@@ -387,7 +384,8 @@
ACCEPTS: float
"""
- self._bbox.ymin = y
+ h = self._bbox.height
+ self._bbox.intervaly = (y, y + h)
def set_width(self, w):
"""
@@ -395,7 +393,7 @@
ACCEPTS: float
"""
- self._bbox.width = w
+ self._bbox.xmax = self._bbox.xmin + w
def set_height(self, h):
"""
@@ -403,7 +401,7 @@
ACCEPTS: float
"""
- self._bbox.height = h
+ self._bbox.ymax = self._bbox.ymin + h
def set_bounds(self, *args):
"""
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-18 19:29:21 UTC (rev 3856)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 13:28:11 UTC (rev 3857)
@@ -8,6 +8,8 @@
from numpy.linalg import inv
from sets import Set
+DEBUG = True
+
# MGDTODO: This creates a ton of cyclical references. We may want to
# consider using weak references
@@ -53,6 +55,13 @@
def __array__(self):
return self.get_points()
+
+ if DEBUG:
+ def invalidate(self):
+ points = self.get_points()
+ assert points[0, 0] <= points[1, 0]
+ assert points[0, 1] <= points[1, 1]
+ TransformNode.invalidate(self)
# MGDTODO: Probably a more efficient ways to do this...
def _get_xmin(self):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-18 19:29:24
|
Revision: 3856
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3856&view=rev
Author: mdboom
Date: 2007-09-18 12:29:21 -0700 (Tue, 18 Sep 2007)
Log Message:
-----------
Optimize shared axes (to prevent calling set_xlim/set_ylim more than
once per axes per update).
Save figure at correct dpi.
General cleanup and optimizations.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/backends/backend_gtkagg.py
branches/transforms/lib/matplotlib/backends/backend_tkagg.py
branches/transforms/lib/matplotlib/cbook.py
branches/transforms/lib/matplotlib/figure.py
branches/transforms/lib/matplotlib/legend.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/transforms.py
branches/transforms/src/_backend_agg.cpp
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -435,6 +435,9 @@
1 : 'log',
}
+ _shared_x_axes = cbook.Grouper()
+ _shared_y_axes = cbook.Grouper()
+
def __str__(self):
return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
def __init__(self, fig, rect,
@@ -491,6 +494,10 @@
# must be set before set_figure
self._sharex = sharex
self._sharey = sharey
+ if sharex is not None:
+ self._shared_x_axes.join(self, sharex)
+ if sharey is not None:
+ self._shared_y_axes.join(self, sharey)
# Flag: True if some other Axes instance is sharing our x or y axis
self._masterx = False
self._mastery = False
@@ -502,7 +509,6 @@
# this call may differ for non-sep axes, eg polar
self._init_axis()
-
if axisbg is None: axisbg = rcParams['axes.facecolor']
self._axisbg = axisbg
self._frameon = frameon
@@ -616,55 +622,28 @@
#these will be updated later as data is added
self._set_lim_and_transforms()
- def _shared_xlim_callback(self, ax):
- xmin, xmax = ax.get_xlim()
- self.set_xlim(xmin, xmax, emit=False)
- self.figure.canvas.draw_idle()
-
- def _shared_ylim_callback(self, ax):
- ymin, ymax = ax.get_ylim()
- self.set_ylim(ymin, ymax, emit=False)
- self.figure.canvas.draw_idle()
-
def _set_lim_and_transforms(self):
"""
set the dataLim and viewLim BBox attributes and the
transData and transAxes Transformation attributes
"""
- Bbox = mtransforms.Bbox
- self.viewLim = Bbox.unit()
+ self.viewLim = mtransforms.Bbox.unit()
+ self.dataLim = mtransforms.Bbox.unit()
- if self._sharex is not None:
- # MGDTODO: This may be doing at least one too many updates
- # than necessary
- self._sharex.callbacks.connect(
- 'xlim_changed', self._shared_xlim_callback)
- self.viewLim.intervalx = self._sharex.viewLim.intervalx
- if self._sharey is not None:
- self._sharey.callbacks.connect(
- 'ylim_changed', self._shared_ylim_callback)
- self.viewLim.intervaly = self._sharex.viewLim.intervaly
-
- self.dataLim = Bbox.unit()
-
self.transAxes = mtransforms.BboxTransform(
- Bbox.unit(), self.bbox)
-
+ mtransforms.Bbox.unit(), self.bbox)
self.transData = mtransforms.BboxTransform(
self.viewLim, self.bbox)
def get_position(self, original=False):
'Return the axes rectangle left, bottom, width, height'
- # MGDTODO: This changed from returning a list to returning a Bbox
- # If you get any errors with the result of this function, please
- # update the calling code
if original:
- return copy.copy(self._originalPosition)
+ return self._originalPosition
else:
- return copy.copy(self._position)
- # return [val.get() for val in self._position]
+ return self._position
+
def set_position(self, pos, which='both'):
"""
Set the axes position with pos = [left, bottom, width, height]
@@ -699,8 +678,7 @@
self.xaxis.cla()
self.yaxis.cla()
- # MGDTODO
- # self.dataLim.ignore(1)
+ self.ignore_existing_data_limits = True
self.callbacks = cbook.CallbackRegistry(('xlim_changed', 'ylim_changed'))
if self._sharex is not None:
@@ -886,7 +864,7 @@
return
- l,b,w,h = self.get_position(original=True)
+ l,b,w,h = self.get_position(original=True).bounds
box_aspect = fig_aspect * (h/w)
data_ratio = box_aspect / A
@@ -1152,7 +1130,7 @@
# Otherwise, it will compute the bounds of it's current data
# and the data in xydata
xys = npy.asarray(xys)
- self.dataLim.update_numerix_xy(xys, -1)
+ self.update_datalim_numerix(xys[:, 0], xys[:, 1])
def update_datalim_numerix(self, x, y):
@@ -1161,10 +1139,9 @@
# limits and set the bound to be the bounds of the xydata.
# Otherwise, it will compute the bounds of it's current data
# and the data in xydata
- #print type(x), type(y)
- # MGDTODO
## self.dataLim.update_numerix(x, y, -1)
- self.dataLim.update_from_data(x, y)
+ self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits)
+ self.ignore_existing_data_limits = False
def _get_verts_in_data_coords(self, trans, xys):
if trans == self.transData:
@@ -1264,9 +1241,7 @@
if not self.get_visible(): return
renderer.open_group('axes')
self.apply_aspect()
- # MGDTODO -- this is where we can finalize all of the transforms
- # self.transData.freeze() # eval the lazy objects
- # self.transAxes.freeze()
+
if self.axison and self._frameon: self.axesPatch.draw(renderer)
artists = []
@@ -1314,17 +1289,13 @@
if self.axison and self._frameon:
artists.append(self.axesFrame)
- # keep track of i to guarantee stable sort for python 2.2
- dsu = [ (a.zorder, i, a) for i, a in enumerate(artists)
- if not a.get_animated()]
+ dsu = [ (a.zorder, a) for a in artists
+ if not a.get_animated() ]
dsu.sort()
- for zorder, i, a in dsu:
+ for zorder, a in dsu:
a.draw(renderer)
- # MGDTODO
- # self.transData.thaw() # release the lazy objects
- # self.transAxes.thaw() # release the lazy objects
renderer.close_group('axes')
self._cachedRenderer = renderer
@@ -1509,7 +1480,6 @@
ACCEPTS: len(2) sequence of floats
"""
-
if xmax is None and iterable(xmin):
xmin,xmax = xmin
@@ -1534,11 +1504,10 @@
self.viewLim.intervalx = (xmin, xmax)
if emit:
self.callbacks.process('xlim_changed', self)
- # MGDTODO: It would be nice to do this is in the above callback list,
- # but it's difficult to tell how to initialize this at the
- # right time
- if self._sharex:
- self._sharex.set_xlim(*self.viewLim.intervalx)
+ # Call all of the other x-axes that are shared with this one
+ for other in self._shared_x_axes.get_siblings(self):
+ if other is not self:
+ other.set_xlim(self.viewLim.xmin, self.viewLim.xmax, emit=False)
return xmin, xmax
@@ -1665,11 +1634,10 @@
self.viewLim.intervaly = (ymin, ymax)
if emit:
self.callbacks.process('ylim_changed', self)
- # MGDTODO: It would be nice to do this is in the above callback list,
- # but it's difficult to tell how to initialize this at the
- # right time
- if self._sharey:
- self._sharey.set_ylim(*self.viewLim.intervaly)
+ # Call all of the other y-axes that are shared with this one
+ for other in self._shared_y_axes.get_siblings(self):
+ if other is not self:
+ other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False)
return ymin, ymax
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -1033,8 +1033,7 @@
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
- self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi/72.0))
-# self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0)) MGDTODO
+ self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0))
else:
if not len(bboxes2):
@@ -1057,7 +1056,6 @@
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0))
-# self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO
def set_ticks_position(self, position):
"""
@@ -1225,7 +1223,6 @@
left = bbox.xmin
self.label.set_position( (left-self.LABELPAD*self.figure.dpi/72.0, y))
- # self.label.set_position( (left-self.LABELPAD*self.figure.dpi.get()/72.0, y)) MGDTODO
else:
if not len(bboxes2):
@@ -1245,7 +1242,6 @@
x,y = self.offsetText.get_position()
top = self.axes.bbox.ymax
self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi/72.0))
-# self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO
def set_offset_position(self, position):
assert position == 'left' or position == 'right'
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -18,8 +18,9 @@
"""An abstract base class to handle drawing/rendering operations
"""
# This will cache paths across rendering instances
- # Each subclass of RenderBase should define this -->
- # _paths = weakref.WeakKeyDictionary()
+ # Each subclass of RenderBase should define this a weak-keyed
+ # dictionary to hold native paths
+ # _native_paths = weakref.WeakKeyDictionary()
def __init__(self):
self._texmanager = None
@@ -44,7 +45,7 @@
self._native_paths[path] = native_path
return native_path
- def draw_path(self, gc, path, transform, rgbFace = None):
+ def draw_path(self, gc, path, transform, rgbFace=None):
"""
Handles the caching of the native path associated with the
given path and calls the underlying backend's _draw_path to
@@ -67,17 +68,31 @@
passed to them in draw_path.
"""
return path
+
+ def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
+ native_marker_path = self._get_cached_native_path(marker_path)
+ self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
- def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
- rotation):
+ def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
"""
- Draw an arc using GraphicsContext instance gcEdge, centered at x,y,
- with width and height and angles from 0.0 to 360.0
- 0 degrees is at 3-o'clock
- positive angles are anti-clockwise
- draw rotated 'rotation' degrees anti-clockwise about x,y
+ This method is currently underscore hidden because the
+ draw_markers method is being used as a sentinel for newstyle
+ backend drawing
- If the color rgbFace is not None, fill the arc with it.
+ path - a matplotlib.agg.path_storage instance
+
+ Draw the marker specified in path with graphics context gc at
+ each of the locations in arrays x and y. trans is a
+ matplotlib.transforms.Transformation instance used to
+ transform x and y to display coords. It consists of an
+ optional nonlinear component and an affine. You can access
+ these two components as
+
+ if transform.need_nonlinear():
+ x,y = transform.nonlinear_only_numerix(x, y)
+ # the a,b,c,d,tx,ty affine which transforms x and y
+ vec6 = transform.as_vec6_val()
+ ...backend dependent affine...
"""
raise NotImplementedError
@@ -109,30 +124,21 @@
"""
return False
- def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
- native_marker_path = self._get_cached_native_path(marker_path)
- self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
-
- def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
+ ######################################################################
+ ## OLD API IS BELOW
+ ## These functions no longer need to be implemented in the backends --
+ ## they now perform all of their functions in terms of the new API.
+
+ def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
+ rotation):
"""
- This method is currently underscore hidden because the
- draw_markers method is being used as a sentinel for newstyle
- backend drawing
+ Draw an arc using GraphicsContext instance gcEdge, centered at x,y,
+ with width and height and angles from 0.0 to 360.0
+ 0 degrees is at 3-o'clock
+ positive angles are anti-clockwise
+ draw rotated 'rotation' degrees anti-clockwise about x,y
- path - a matplotlib.agg.path_storage instance
-
- Draw the marker specified in path with graphics context gc at
- each of the locations in arrays x and y. trans is a
- matplotlib.transforms.Transformation instance used to
- transform x and y to display coords. It consists of an
- optional nonlinear component and an affine. You can access
- these two components as
-
- if transform.need_nonlinear():
- x,y = transform.nonlinear_only_numerix(x, y)
- # the a,b,c,d,tx,ty affine which transforms x and y
- vec6 = transform.as_vec6_val()
- ...backend dependent affine...
+ If the color rgbFace is not None, fill the arc with it.
"""
raise NotImplementedError
@@ -346,8 +352,10 @@
If rgbFace is not None, fill the rectangle with it.
"""
- raise NotImplementedError
-
+ warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning)
+ transform = transforms.Affine2D().scale(width, height).translate(x, y)
+ self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace)
+
def draw_regpoly_collection(
self, clipbox, offsets, transOffset, verts, sizes,
facecolors, edgecolors, linewidths, antialiaseds):
@@ -1221,8 +1229,6 @@
origfacecolor = self.figure.get_facecolor()
origedgecolor = self.figure.get_edgecolor()
- # MGDTODO
- # self.figure.dpi.set(dpi)
self.figure.dpi = dpi
self.figure.set_facecolor(facecolor)
self.figure.set_edgecolor(edgecolor)
@@ -1236,12 +1242,12 @@
orientation=orientation,
**kwargs)
finally:
- # MGDTODO
- # self.figure.dpi.set(origDPI)
self.figure.dpi = origDPI
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)
self.figure.set_canvas(self)
+
+ self.draw()
return result
@@ -1623,8 +1629,8 @@
lims.append( (xmin, xmax, ymin, ymax) )
# Store both the original and modified positions
pos.append( (
- a.get_position(True),
- a.get_position() ) )
+ copy.copy(a.get_position(True)),
+ copy.copy(a.get_position() )) )
self._views.push(lims)
self._positions.push(pos)
self.set_history_buttons()
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -114,9 +114,6 @@
self.height = height
if __debug__: verbose.report('RendererAgg.__init__ width=%s, \
height=%s'%(width, height), 'debug-annoying')
- # MGDTODO
-# self._renderer = _RendererAgg(int(width), int(height), dpi.get(),
-# debug=False)
self._renderer = _RendererAgg(int(width), int(height), dpi,
debug=False)
if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done',
@@ -136,35 +133,6 @@
def _draw_native_path(self, gc, path, transform, rgbFace):
return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace)
- def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation):
- """
- Draw an arc centered at x,y with width and height and angles
- from 0.0 to 360.0
-
- If rgbFace is not None, fill the rectangle with that color. gcEdge
- is a GraphicsContext instance
-
- Currently, I'm only supporting ellipses, ie angle args are
- ignored
- """
- if __debug__: verbose.report('RendererAgg.draw_arc', 'debug-annoying')
- self._renderer.draw_ellipse(
- gcEdge, rgbFace, x, y, width/2, height/2, rotation) # ellipse takes radius
-
-
- def draw_line(self, gc, x1, y1, x2, y2):
- """
- x and y are equal length arrays, draw lines connecting each
- point in x, y
- """
- if __debug__: verbose.report('RendererAgg.draw_line', 'debug-annoying')
- x = npy.array([x1,x2], float)
- y = npy.array([y1,y2], float)
- self._renderer.draw_lines(gc, x, y)
-
- def draw_lines(self, gc, x, y, transform):
- return self._renderer.draw_lines(gc, x, y, transform.to_values())
-
def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
return self._renderer.draw_markers(
gc,
@@ -172,18 +140,6 @@
path.vertices, path.codes, trans.get_matrix(),
rgbFace)
- def draw_polygon(self, *args):
- return self._renderer.draw_polygon(*args)
-
- def draw_point(self, gc, x, y):
- """
- Draw a single point at x,y
- """
- if __debug__: verbose.report('RendererAgg.draw_point', 'debug-annoying')
- rgbFace = gc.get_rgb()
- self._renderer.draw_ellipse(
- gc, rgbFace, x, y, 0.5, 0.5, 0.0)
-
def draw_mathtext(self, gc, x, y, s, prop, angle):
"""
Draw the math text using matplotlib.mathtext
@@ -192,8 +148,6 @@
'debug-annoying')
ox, oy, width, height, descent, font_image, used_characters = \
self.mathtext_parser.parse(s, self.dpi, prop)
-# ox, oy, width, height, descent, font_image, used_characters = \
-# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO
x = int(x) + ox
y = int(y) - oy
@@ -227,7 +181,6 @@
# (in vector space) in the above call to font.set_text.
self._renderer.draw_text_image(font.get_image(), int(x), int(y) + 1, angle, gc)
-
def get_text_width_height_descent(self, s, prop, ismath, rgb=(0,0,0)):
"""
get the width and height in display coords of the string s
@@ -241,7 +194,7 @@
# todo: handle props
size = prop.get_size_in_points()
texmanager = self.get_texmanager()
- Z = texmanager.get_rgba(s, size, self.dpi.get(), rgb)
+ Z = texmanager.get_rgba(s, size, self.dpi, rgb)
m,n,tmp = Z.shape
# TODO: descent of TeX text (I am imitating backend_ps here -JKS)
return n, m, 0
@@ -249,8 +202,6 @@
if ismath:
ox, oy, width, height, descent, fonts, used_characters = \
self.mathtext_parser.parse(s, self.dpi, prop)
-# ox, oy, width, height, descent, fonts, used_characters = \
-# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO
return width, height, descent
font = self._get_agg_font(prop)
font.set_text(s, 0.0, flags=LOAD_DEFAULT) # the width and height of unrotated string
@@ -265,7 +216,7 @@
# todo, handle props, angle, origins
rgb = gc.get_rgb()
size = prop.get_size_in_points()
- dpi = self.dpi.get()
+ dpi = self.dpi
flip = angle==90
w,h,d = self.get_text_width_height_descent(s, prop, 'TeX', rgb)
@@ -306,7 +257,6 @@
'return the canvas width and height in display coords'
return self.width, self.height
-
def _get_agg_font(self, prop):
"""
Get the font for text instance t, cacheing for efficiency
@@ -325,11 +275,9 @@
font.clear()
size = prop.get_size_in_points()
font.set_size(size, self.dpi)
- # font.set_size(size, self.dpi.get()) MGDTODO
return font
-
def points_to_pixels(self, points):
"""
convert point measures to pixes using dpi and the pixels per
@@ -337,8 +285,6 @@
"""
if __debug__: verbose.report('RendererAgg.points_to_pixels',
'debug-annoying')
- # MGDTODO
- # return points*self.dpi.get()/72.0
return points*self.dpi/72.0
def tostring_rgb(self):
@@ -404,9 +350,7 @@
self.figure.draw(self.renderer)
def get_renderer(self):
- l,b,w,h = self.figure.bbox.bounds
- # MGDTODO
- # key = w, h, self.figure.dpi.get()
+ l, b, w, h = self.figure.bbox.bounds
key = w, h, self.figure.dpi
try: self._lastKey, self.renderer
except AttributeError: need_new_renderer = True
Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -60,7 +60,6 @@
w,h = widget.window.get_size()
if w==1 or h==1: return # empty fig
- # dpival = self.figure.dpi.get() MGDTODO
# compute desired figure size in inches
dpival = self.figure.dpi
winch = w/dpival
Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -175,7 +175,6 @@
self._resize_callback(event)
# compute desired figure size in inches
- # dpival = self.figure.dpi.get() MGDTODO
dpival = self.figure.dpi
winch = width/dpival
hinch = height/dpival
Modified: branches/transforms/lib/matplotlib/cbook.py
===================================================================
--- branches/transforms/lib/matplotlib/cbook.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/cbook.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -955,6 +955,84 @@
outstream.write("Examining: %r\n" % (obj,))
recurse(obj, obj, { }, [])
+class Grouper(object):
+ """
+ This class provides a lightweight way to group arbitrary objects
+ together into disjoint sets when a full-blown graph data structure
+ would be overkill.
+
+ Objects can be joined using .join(), tested for connectedness
+ using .joined(), and all disjoint sets can be retreived using
+ .get().
+
+ The objects being joined must be hashable.
+
+ For example:
+
+ >>> g = grouper.Grouper()
+ >>> g.join('a', 'b')
+ >>> g.join('b', 'c')
+ >>> g.join('d', 'e')
+ >>> list(g.get())
+ [['a', 'b', 'c'], ['d', 'e']]
+ >>> g.joined('a', 'b')
+ True
+ >>> g.joined('a', 'c')
+ True
+ >>> g.joined('a', 'd')
+ False"""
+ def __init__(self, init=[]):
+ mapping = self._mapping = {}
+ for x in init:
+ mapping[x] = [x]
+
+ def join(self, a, *args):
+ """
+ Join given arguments into the same set.
+ Accepts one or more arguments.
+ """
+ mapping = self._mapping
+ set_a = mapping.setdefault(a, [a])
+
+ for arg in args:
+ set_b = mapping.get(arg)
+ if set_b is None:
+ set_a.append(arg)
+ mapping[arg] = set_a
+ elif set_b is not set_a:
+ if len(set_b) > len(set_a):
+ set_a, set_b = set_b, set_a
+ set_a.extend(set_b)
+ for elem in set_b:
+ mapping[elem] = set_a
+
+ def joined(self, a, b):
+ """
+ Returns True if a and b are members of the same set.
+ """
+ mapping = self._mapping
+ try:
+ return mapping[a] is mapping[b]
+ except KeyError:
+ return False
+
+ def __iter__(self):
+ """
+ Returns an iterator returning each of the disjoint sets as a list.
+ """
+ seen = set()
+ for elem, group in self._mapping.iteritems():
+ if elem not in seen:
+ yield group
+ seen.update(group)
+
+ def get_siblings(self, a):
+ """
+ Returns all of the items joined with the given item, including
+ itself.
+ """
+ return self._mapping.get(a, [a])
+
if __name__=='__main__':
assert( allequal([1,1,1]) )
assert(not allequal([1,1,0]) )
Modified: branches/transforms/lib/matplotlib/figure.py
===================================================================
--- branches/transforms/lib/matplotlib/figure.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/figure.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -161,10 +161,8 @@
def _get_dpi(self):
return self._dpi
def _set_dpi(self, dpi):
- print "setting dpi"
self._dpi = dpi
self._dpi_scale_trans.clear().scale(dpi, dpi)
- print self._dpi_scale_trans
dpi = property(_get_dpi, _set_dpi)
def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'):
@@ -178,10 +176,7 @@
bottom : the bottom of the subplots for subplots_adjust
rotation: the rotation of the xtick labels
ha : the horizontal alignment of the xticklabels
-
-
"""
-
for ax in self.get_axes():
if not hasattr(ax, 'is_last_row'):
raise RuntimeError('Axes must be subplot instances; found %s'%type(ax))
@@ -333,11 +328,8 @@
dpival = self.dpi
self.bbox_inches.max = w, h
- # self.figwidth.set(w) MGDTODO
- # self.figheight.set(h)
if forward:
- # dpival = self.dpi.get()
dpival = self.dpi
canvasw = w*dpival
canvash = h*dpival
@@ -347,7 +339,6 @@
def get_size_inches(self):
return self.bbox_inches.max
- # return self.figwidth.get(), self.figheight.get() MGDTODO
def get_edgecolor(self):
'Get the edge color of the Figure rectangle'
@@ -360,7 +351,6 @@
def get_figwidth(self):
'Return the figwidth as a float'
return self.bbox_inches.xmax
- # return self.figwidth.get() MGDTODO
def get_figheight(self):
'Return the figheight as a float'
@@ -369,7 +359,6 @@
def get_dpi(self):
'Return the dpi as a float'
return self.dpi
- # return self.dpi.get() MGDTODO
def get_frameon(self):
'get the boolean indicating frameon'
@@ -397,7 +386,6 @@
ACCEPTS: float
"""
- # self.dpi.set(val) MGDTODO
self.dpi = val
def set_figwidth(self, val):
@@ -406,7 +394,6 @@
ACCEPTS: float
"""
- # self.figwidth.set(val) MGDTODO
self.bbox_inches.xmax = val
def set_figheight(self, val):
@@ -415,7 +402,6 @@
ACCEPTS: float
"""
- # MGDTODO (set())
self.bbox_inches.ymax = val
def set_frameon(self, b):
@@ -598,8 +584,6 @@
#print 'figure draw'
if not self.get_visible(): return
renderer.open_group('figure')
- # MGDTODO
- # self.transFigure.freeze() # eval the lazy objects
if self.frameon: self.figurePatch.draw(renderer)
@@ -633,8 +617,6 @@
for legend in self.legends:
legend.draw(renderer)
- # MGDTODO
- # self.transFigure.thaw() # release the lazy objects
renderer.close_group('figure')
self._cachedRenderer = renderer
Modified: branches/transforms/lib/matplotlib/legend.py
===================================================================
--- branches/transforms/lib/matplotlib/legend.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/legend.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -21,7 +21,7 @@
up the legend
"""
from __future__ import division
-import sys, warnings
+import copy, sys, warnings
import numpy as npy
@@ -558,9 +558,7 @@
handle.set_height(h/2)
# Set the data for the legend patch
- # MGDTODO: This copy may no longer be needed now that Bboxes are
- # essentially immutable
- bbox = self._get_handle_text_bbox(renderer).copy()
+ bbox = copy.copy(self._get_handle_text_bbox(renderer))
bbox = bbox.scaled(1 + self.pad, 1 + self.pad)
l,b,w,h = bbox.get_bounds()
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -352,32 +352,18 @@
self._picker = p
def get_window_extent(self, renderer):
- self._newstyle = hasattr(renderer, 'draw_markers')
- if self._newstyle:
- x = self._x
- y = self._y
- else:
- x, y = self._get_plottable()
+ xys = self.get_transform()(self._xys)
- # MGDTODO: Put this in a single Nx2 array, rather than these
- # separate ones
- #### Conversion code
- a = npy.vstack((x, y)).swapaxes(0, 1)
- ####
- x, y = self.get_transform()(a)
- print "get_window_extent", self.get_transform()
-
- #x, y = self.get_transform().seq_x_y(x, y)
+ x = xys[:, 0]
+ y = xys[:, 1]
+ left = x.min()
+ bottom = y.min()
+ width = x.max() - left
+ height = y.max() - bottom
- left = min(x)
- bottom = min(y)
- width = max(x) - left
- height = max(y) - bottom
-
# correct for marker size, if any
if self._marker is not None:
- ms = self._markersize/72.0*self.figure.dpi
- # ms = self._markersize/72.0*self.figure.dpi.get() MGDTODO
+ ms = self._markersize / 72.0 * self.figure.dpi
left -= ms/2
bottom -= ms/2
width += ms
@@ -411,6 +397,7 @@
def recache(self):
#if self.axes is None: print 'recache no axes'
#else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units
+ # MGDTODO: Deal with units
x = ma.asarray(self.convert_xunits(self._xorig), float)
y = ma.asarray(self.convert_yunits(self._yorig), float)
@@ -435,15 +422,15 @@
else:
self._segments = None
- self._x = npy.asarray(x, float)
- self._y = npy.asarray(y, float)
- self._path = Path(npy.vstack((self._x, self._y)).transpose(),
- closed=False)
+ self._xy = npy.vstack((npy.asarray(x, npy.float_),
+ npy.asarray(y, npy.float_))).transpose()
+ self._x = self._xy[:, 0]
+ self._y = self._xy[:, 1]
+ self._path = Path(self._xy, closed=False)
self._logcache = None
-
def _is_sorted(self, x):
"return true if x is sorted"
if len(x)<2: return 1
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -19,9 +19,9 @@
self._parents = Set()
def invalidate(self):
- if not self._do_invalidation():
- for parent in self._parents:
- parent.invalidate()
+ self._do_invalidation()
+ for parent in self._parents:
+ parent.invalidate()
def _do_invalidation(self):
return False
@@ -187,14 +187,16 @@
return 'Bbox(%s)' % repr(self._points)
__str__ = __repr__
- # JDH: the update method will update the box limits from the
- # existing limits and the new data; it appears here you are just
- # using the new data. We use an "ignore" flag to specify whether
- # you want to include the existing data or not in the update
def update_from_data(self, x, y, ignore=True):
- self._points = npy.array(
- [[x.min(), y.min()], [x.max(), y.max()]],
- npy.float_)
+ if ignore:
+ self._points = npy.array(
+ [[x.min(), y.min()], [x.max(), y.max()]],
+ npy.float_)
+ else:
+ self._points = npy.array(
+ [[min(x.min(), self.xmin), min(y.min(), self.ymin)],
+ [max(x.max(), self.xmax), max(y.max(), self.ymax)]],
+ npy.float_)
self.invalidate()
# MGDTODO: Probably a more efficient ways to do this...
@@ -409,9 +411,7 @@
return self.get_matrix()
def _do_invalidation(self):
- result = self._inverted is None
self._inverted = None
- return result
#@staticmethod
def _concat(a, b):
@@ -494,6 +494,7 @@
if matrix is None:
matrix = npy.identity(3)
else:
+ matrix = npy.asarray(matrix, npy.float_)
assert matrix.shape == (3, 3)
self._mtx = matrix
self._inverted = None
@@ -629,8 +630,6 @@
if self._mtx is not None:
self._mtx = None
Affine2DBase._do_invalidation(self)
- return False
- return True
def is_separable(self):
return True
@@ -684,7 +683,7 @@
def _do_invalidation(self):
self._mtx = None
- Affine2DBase._do_invalidation(self)
+ return Affine2DBase._do_invalidation(self)
def get_matrix(self):
if self._mtx is None:
@@ -718,8 +717,6 @@
if self._mtx is not None:
self._mtx = None
Affine2DBase._do_invalidation(self)
- return False
- return True
def is_separable(self):
return True
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-18 19:29:21 UTC (rev 3856)
@@ -456,7 +456,7 @@
//return the agg::rect for bbox, flipping y
PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2);
- if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2)
+ if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2)
throw Py::TypeError
("Expected a Bbox object.");
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-18 16:21:40
|
Revision: 3855
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3855&view=rev
Author: mdboom
Date: 2007-09-18 09:21:37 -0700 (Tue, 18 Sep 2007)
Log Message:
-----------
More code using new transformation framework. Lots of dead code
removed from backend_agg.cpp/h
Modified Paths:
--------------
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/path.py
branches/transforms/lib/matplotlib/transforms.py
branches/transforms/lib/matplotlib/type1font.py
branches/transforms/src/_backend_agg.cpp
branches/transforms/src/_backend_agg.h
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 16:21:37 UTC (rev 3855)
@@ -39,8 +39,7 @@
def _get_cached_native_path(self, path):
native_path = self._native_paths.get(path)
if native_path is None:
- import matplotlib.patches
- print "CACHE MISS", path
+ # print "CACHE MISS", path
native_path = self.convert_to_native_path(path)
self._native_paths[path] = native_path
return native_path
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 16:21:37 UTC (rev 3855)
@@ -121,13 +121,9 @@
debug=False)
if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done',
'debug-annoying')
- # self.draw_polygon = self._renderer.draw_polygon
- self.draw_rectangle = self._renderer.draw_rectangle
- # MGDTODO -- remove these lines
- # self.draw_lines = self._renderer.draw_lines
- # self.draw_markers = self._renderer.draw_markers
- self.draw_image = self._renderer.draw_image
+ self.convert_to_native_path = self._renderer.convert_to_native_path
+ self.draw_image = self._renderer.draw_image
self.copy_from_bbox = self._renderer.copy_from_bbox
self.restore_region = self._renderer.restore_region
self.mathtext_parser = MathTextParser('Agg')
@@ -137,12 +133,9 @@
if __debug__: verbose.report('RendererAgg.__init__ done',
'debug-annoying')
- def convert_to_native_path(self, path):
- return self._renderer.convert_to_native_path(path.vertices, path.codes)
-
- def _draw_native_path(self, gc, native_path, transform, rgbFace):
- return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace)
-
+ def _draw_native_path(self, gc, path, transform, rgbFace):
+ return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace)
+
def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation):
"""
Draw an arc centered at x,y with width and height and angles
@@ -175,8 +168,8 @@
def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
return self._renderer.draw_markers(
gc,
- native_marker_path, marker_trans.to_values(),
- path.vertices, path.codes, trans.to_values(),
+ native_marker_path, marker_trans.get_matrix(),
+ path.vertices, path.codes, trans.get_matrix(),
rgbFace)
def draw_polygon(self, *args):
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-18 16:21:37 UTC (rev 3855)
@@ -712,6 +712,7 @@
def _draw_nothing(self, renderer, gc, path):
pass
+
def _draw_steps(self, renderer, gc, xt, yt):
# MGDTODO: This is a quirky one. The step-plotting part
# should probably be moved to where the path is generated
@@ -729,6 +730,7 @@
else:
renderer.draw_lines(gc, xt2, yt2)
+
def _draw_solid(self, renderer, gc, path):
gc.set_linestyle('solid')
renderer.draw_path(gc, path, self.get_transform())
@@ -753,8 +755,15 @@
def _draw_point(self, renderer, gc, path):
- self._draw_circle(renderer, gc, path, point = True)
+ w = renderer.points_to_pixels(self._markersize) * \
+ self._point_size_reduction * 0.5
+ rgbFace = self._get_rgb_face()
+ transform = Affine2D().scale(w)
+ renderer.draw_markers(
+ gc, Path.unit_circle(), transform, path, self.get_transform(),
+ rgbFace)
+
def _draw_pixel(self, renderer, gc, path):
rgbFace = self._get_rgb_face()
transform = Affine2D().translate(-0.5, -0.5)
@@ -762,12 +771,8 @@
path, self.get_transform(), rgbFace)
- def _draw_circle(self, renderer, gc, path, point=False):
- w = renderer.points_to_pixels(self._markersize)
- if point:
- w *= self._point_size_reduction
- w *= 0.5
-
+ def _draw_circle(self, renderer, gc, path):
+ w = renderer.points_to_pixels(self._markersize) * 0.5
rgbFace = self._get_rgb_face()
transform = Affine2D().scale(w, w)
renderer.draw_markers(
@@ -826,7 +831,8 @@
def _draw_thin_diamond(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
- transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(offset * 0.8, offset)
+ transform = Affine2D().translate(0.5, 0.5) \
+ .rotate_deg(45).scale(offset * 0.8, offset)
rgbFace = self._get_rgb_face()
renderer.draw_markers(gc, Path.unit_rectangle(), transform,
path, self.get_transform(), rgbFace)
@@ -840,7 +846,7 @@
path, self.get_transform(), rgbFace)
- def _draw_hexagon1(self, renderer, gc, path, point=False):
+ def _draw_hexagon1(self, renderer, gc, path):
offset = 0.5 * renderer.points_to_pixels(self._markersize)
transform = Affine2D().scale(offset)
rgbFace = self._get_rgb_face()
@@ -848,50 +854,44 @@
path, self.get_transform(), rgbFace)
- def _draw_hexagon2(self, renderer, gc, xt, yt):
+ def _draw_hexagon2(self, renderer, gc, path):
offset = 0.5 * renderer.points_to_pixels(self._markersize)
transform = Affine2D().scale(offset).rotate_deg(30)
rgbFace = self._get_rgb_face()
renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform,
path, self.get_transform(), rgbFace)
-
- def _draw_vline(self, renderer, gc, xt, yt):
+
+ _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]], closed=False)
+ def _draw_vline(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, -offset)
- path.line_to(0, offset)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y-offset, x, y+offset)
+ transform = Affine2D().scale(offset)
+ renderer.draw_markers(gc, self._line_marker_path, transform,
+ path, self.get_transform())
- def _draw_hline(self, renderer, gc, xt, yt):
+ def _draw_hline(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset, 0)
- path.line_to(offset, 0)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset, y, x+offset, y)
+ transform = Affine2D().scale(offset).rotate_deg(90)
+ renderer.draw_markers(gc, self._line_marker_path, transform,
+ path, self.get_transform())
+
_tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]])
def _draw_tickleft(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
- marker_transform = Affine2D().scale(offset, 1.0)
+ marker_transform = Affine2D().scale(-offset, 1.0)
renderer.draw_markers(gc, self._tickhoriz_path, marker_transform,
path, self.get_transform())
+
def _draw_tickright(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
- marker_transform = Affine2D().scale(-offset, 1.0)
+ marker_transform = Affine2D().scale(offset, 1.0)
renderer.draw_markers(gc, self._tickhoriz_path, marker_transform,
path, self.get_transform())
+
_tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]])
def _draw_tickup(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
@@ -899,174 +899,99 @@
renderer.draw_markers(gc, self._tickvert_path, marker_transform,
path, self.get_transform())
+
def _draw_tickdown(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
marker_transform = Affine2D().scale(1.0, -offset)
renderer.draw_markers(gc, self._tickvert_path, marker_transform,
path, self.get_transform())
- def _draw_plus(self, renderer, gc, xt, yt):
+
+ _plus_path = Path([[-1.0, 0.0], [1.0, 0.0],
+ [0.0, -1.0], [0.0, 1.0]],
+ [Path.MOVETO, Path.LINETO,
+ Path.MOVETO, Path.LINETO])
+ def _draw_plus(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- if self._newstyle:
+ transform = Affine2D().scale(offset)
+ renderer.draw_markers(gc, self._plus_path, transform,
+ path, self.get_transform())
- path = agg.path_storage()
- path.move_to(-offset, 0)
- path.line_to( offset, 0)
- path.move_to( 0, -offset)
- path.line_to( 0, offset)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset, y, x+offset, y)
- renderer.draw_line(gc, x, y-offset, x, y+offset)
- def _draw_tri_down(self, renderer, gc, xt, yt):
+ _tri_path = Path([[0.0, 0.0], [0.0, -1.0],
+ [0.0, 0.0], [0.8, 0.5],
+ [0.0, 0.0], [-0.8, 0.5]],
+ [Path.MOVETO, Path.LINETO,
+ Path.MOVETO, Path.LINETO,
+ Path.MOVETO, Path.LINETO])
+ def _draw_tri_down(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = offset*0.8
- offset2 = offset*0.5
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, 0)
- path.line_to(0, -offset)
- path.move_to(0, 0)
- path.line_to(offset1, offset2)
- path.move_to(0, 0)
- path.line_to(-offset1, offset2)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y, x, y-offset)
- renderer.draw_line(gc, x, y, x+offset1, y+offset2)
- renderer.draw_line(gc, x, y, x-offset1, y+offset2)
+ transform = Affine2D().scale(offset)
+ renderer.draw_markers(gc, self._tri_path, transform,
+ path, self.get_transform())
- def _draw_tri_up(self, renderer, gc, xt, yt):
+
+ def _draw_tri_up(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = offset*0.8
- offset2 = offset*0.5
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, 0)
- path.line_to(0, offset)
- path.move_to(0, 0)
- path.line_to(offset1, -offset2)
- path.move_to(0, 0)
- path.line_to(-offset1, -offset2)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y, x, y+offset)
- renderer.draw_line(gc, x, y, x+offset1, y-offset2)
- renderer.draw_line(gc, x, y, x-offset1, y-offset2)
+ transform = Affine2D().scale(offset).rotate_deg(180)
+ renderer.draw_markers(gc, self._tri_path, transform,
+ path, self.get_transform())
- def _draw_tri_left(self, renderer, gc, xt, yt):
- offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = offset*0.8
- offset2 = offset*0.5
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, 0)
- path.line_to(-offset, 0)
- path.move_to(0, 0)
- path.line_to(offset2, offset1)
- path.move_to(0, 0)
- path.line_to(offset2, -offset1)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y, x-offset, y)
- renderer.draw_line(gc, x, y, x+offset2, y+offset1)
- renderer.draw_line(gc, x, y, x+offset2, y-offset1)
+
+ def _draw_tri_left(self, renderer, gc, path):
+ offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset).rotate_deg(90)
+ renderer.draw_markers(gc, self._tri_path, transform,
+ path, self.get_transform())
- def _draw_tri_right(self, renderer, gc, xt, yt):
- offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = offset*0.8
- offset2 = offset*0.5
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, 0)
- path.line_to(offset, 0)
- path.move_to(0, 0)
- path.line_to(-offset2, offset1)
- path.move_to(0, 0)
- path.line_to(-offset2, -offset1)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y, x+offset, y)
- renderer.draw_line(gc, x, y, x-offset2, y+offset1)
- renderer.draw_line(gc, x, y, x-offset2, y-offset1)
+
+ def _draw_tri_right(self, renderer, gc, path):
+ offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset).rotate_deg(270)
+ renderer.draw_markers(gc, self._tri_path, transform,
+ path, self.get_transform())
- def _draw_caretdown(self, renderer, gc, xt, yt):
+
+ _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]], closed=False)
+ def _draw_caretdown(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = 1.5*offset
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset, offset1)
- path.line_to(0, 0)
- path.line_to(+offset, offset1)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset, y+offset1, x, y)
- renderer.draw_line(gc, x, y, x+offset, y+offset1)
+ transform = Affine2D().scale(offset)
+ renderer.draw_markers(gc, self._caret_path, transform,
+ path, self.get_transform())
- def _draw_caretup(self, renderer, gc, xt, yt):
+
+ def _draw_caretup(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = 1.5*offset
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset, -offset1)
- path.line_to(0, 0)
- path.line_to(+offset, -offset1)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset, y-offset1, x, y)
- renderer.draw_line(gc, x, y, x+offset, y-offset1)
+ transform = Affine2D().scale(offset).rotate_deg(180)
+ renderer.draw_markers(gc, self._caret_path, transform,
+ path, self.get_transform())
- def _draw_caretleft(self, renderer, gc, xt, yt):
+
+ def _draw_caretleft(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = 1.5*offset
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(offset1, -offset)
- path.line_to(0, 0)
- path.line_to(offset1, offset)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x+offset1, y-offset, x, y)
- renderer.draw_line(gc, x, y, x+offset1, y+offset)
+ transform = Affine2D().scale(offset).rotate_deg(90)
+ renderer.draw_markers(gc, self._caret_path, transform,
+ path, self.get_transform())
- def _draw_caretright(self, renderer, gc, xt, yt):
+
+ def _draw_caretright(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
- offset1 = 1.5*offset
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset1, -offset)
- path.line_to(0, 0)
- path.line_to(-offset1, offset)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset1, y-offset, x, y)
- renderer.draw_line(gc, x, y, x-offset1, y+offset)
+ transform = Affine2D().scale(offset).rotate_deg(270)
+ renderer.draw_markers(gc, self._caret_path, transform,
+ path, self.get_transform())
+
+ _x_path = Path([[-1.0, -1.0], [1.0, 1.0],
+ [-1.0, 1.0], [1.0, -1.0]],
+ [Path.MOVETO, Path.LINETO,
+ Path.MOVETO, Path.LINETO])
def _draw_x(self, renderer, gc, xt, yt):
offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset)
+ renderer.draw_markers(gc, self._x_path, transform,
+ path, self.get_transform())
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset, -offset)
- path.line_to(offset, offset)
- path.move_to(-offset, offset)
- path.line_to(offset, -offset)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset, y-offset, x+offset, y+offset)
- renderer.draw_line(gc, x-offset, y+offset, x+offset, y-offset)
-
+
def update_from(self, other):
'copy properties from other to self'
Artist.update_from(self, other)
Modified: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/lib/matplotlib/path.py 2007-09-18 16:21:37 UTC (rev 3855)
@@ -50,6 +50,9 @@
i += NUM_VERTICES[code]
assert i == len(self.vertices)
+ def __repr__(self):
+ return "Path(%s, %s)" % (self.vertices, self.codes)
+
def _get_codes(self):
return self._codes
codes = property(_get_codes)
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-18 16:21:37 UTC (rev 3855)
@@ -88,11 +88,13 @@
intervaly = property(_get_intervaly)
def _get_width(self):
- return self.xmax - self.xmin
+ points = self.get_points()
+ return points[1, 0] - points[0, 0]
width = property(_get_width)
def _get_height(self):
- return self.ymax - self.ymin
+ points = self.get_points()
+ return points[1, 1] - points[0, 1]
height = property(_get_height)
def _get_bounds(self):
Modified: branches/transforms/lib/matplotlib/type1font.py
===================================================================
--- branches/transforms/lib/matplotlib/type1font.py 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/lib/matplotlib/type1font.py 2007-09-18 16:21:37 UTC (rev 3855)
@@ -1,15 +1,15 @@
"""
A class representing a Type 1 font.
-This version merely allows reading in pfa and pfb files, stores the
-data in pfa format, and allows reading the parts of the data in a
-format suitable for embedding in pdf files. A more complete class
-might support subsetting.
+This version merely reads pfa and pfb files and splits them for
+embedding in pdf files. There is no support yet for subsetting or
+anything like that.
-Usage: font = Type1Font(filename)
- somefile.write(font.data) # writes out font in pfa format
- len1, len2, len3 = font.lengths() # needed for pdf embedding
+Usage (subject to change):
+ font = Type1Font(filename)
+ clear_part, encrypted_part, finale = font.parts
+
Source: Adobe Technical Note #5040, Supporting Downloadable PostScript
Language Fonts.
@@ -32,8 +32,7 @@
def _read(self, file):
rawdata = file.read()
if not rawdata.startswith(chr(128)):
- self.data = rawdata
- return
+ return rawdata
data = ''
while len(rawdata) > 0:
@@ -101,4 +100,6 @@
if __name__ == '__main__':
import sys
font = Type1Font(sys.argv[1])
- sys.stdout.write(font.data)
+ parts = font.parts
+ print len(parts[0]), len(parts[1]), len(parts[2])
+
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-17 13:41:38 UTC (rev 3854)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-18 16:21:37 UTC (rev 3855)
@@ -10,7 +10,6 @@
#include <time.h>
#include <algorithm>
-
#include "agg_conv_transform.h"
#include "agg_conv_curve.h"
#include "agg_scanline_storage_aa.h"
@@ -43,28 +42,48 @@
#define M_PI_2 1.57079632679489661923
#endif
-agg::trans_affine py_sequence_to_agg_transformation_matrix(const Py::Object& obj) {
- Py::SeqBase<Py::Float> seq;
+/** A helper function to convert from a Numpy affine transformation matrix
+ * to an agg::trans_affine.
+ */
+agg::trans_affine py_to_agg_transformation_matrix(const Py::Object& obj) {
+ PyArrayObject* matrix = NULL;
+
+ double a = 1.0, b = 0.0, c = 0.0, d = 1.0, e = 0.0, f = 0.0;
+
try {
- seq = obj;
- } catch(...) {
- throw Py::ValueError("Transformation matrix must be given as a 6-element list.");
- }
+ matrix = (PyArrayObject*) PyArray_ContiguousFromObject(obj.ptr(), PyArray_DOUBLE, 2, 2);
+ if (!matrix || matrix->nd != 2 || matrix->dimensions[0] != 3 || matrix->dimensions[1] != 3) {
+ throw Py::ValueError("Invalid affine transformation matrix.");
+ }
- if (seq.size() != 6) {
- throw Py::ValueError("Transformation matrix must be given as a 6-element list.");
+ size_t stride0 = matrix->strides[0];
+ size_t stride1 = matrix->strides[1];
+ char* row0 = matrix->data;
+ char* row1 = row0 + stride0;
+
+ a = *(double*)(row0);
+ row0 += stride1;
+ c = *(double*)(row0);
+ row0 += stride1;
+ e = *(double*)(row0);
+
+ b = *(double*)(row1);
+ row1 += stride1;
+ d = *(double*)(row1);
+ row1 += stride1;
+ f = *(double*)(row1);
+ } catch (...) {
+ Py_XDECREF(matrix);
}
- return agg::trans_affine
- (Py::Float(seq[0]),
- Py::Float(seq[1]),
- Py::Float(seq[2]),
- Py::Float(seq[3]),
- Py::Float(seq[4]),
- Py::Float(seq[5]));
+ Py_XDECREF(matrix);
+
+ return agg::trans_affine(a, b, c, d, e, f);
}
-// MGDTODO: Implement this as a nice iterator
+/** Helper function to get the next vertex in a Numpy array of vertices.
+ * Will generally be used through the GET_NEXT_VERTEX macro.
+ */
inline void get_next_vertex(const char* & vertex_i, const char* vertex_end,
double& x, double& y,
size_t next_vertex_stride,
@@ -78,12 +97,18 @@
#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride)
+Py::Object BufferRegion::to_string(const Py::Tuple &args) {
+
+ // owned=true to prevent memory leak
+ return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true);
+}
+
+
GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) :
dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0),
cliprect(NULL), clippath(NULL),
Ndash(0), dashOffset(0.0), dasha(NULL)
{
-
_VERBOSE("GCAgg::GCAgg");
linewidth = points_to_pixels ( gc.getAttr("_linewidth") ) ;
alpha = Py::Float( gc.getAttr("_alpha") );
@@ -100,7 +125,6 @@
GCAgg::_set_antialiased(const Py::Object& gc) {
_VERBOSE("GCAgg::antialiased");
isaa = Py::Int( gc.getAttr( "_antialiased") );
-
}
agg::rgba
@@ -123,13 +147,12 @@
return p * dpi/72.0;
}
-
void
GCAgg::_set_linecap(const Py::Object& gc) {
_VERBOSE("GCAgg::_set_linecap");
std::string capstyle = Py::String( gc.getAttr( "_capstyle" ) );
-
+
if (capstyle=="butt")
cap = agg::butt_cap;
else if (capstyle=="round")
@@ -138,7 +161,6 @@
cap = agg::square_cap;
else
throw Py::ValueError(Printf("GC _capstyle attribute must be one of butt, round, projecting; found %s", capstyle.c_str()).str());
-
}
void
@@ -155,7 +177,6 @@
join = agg::bevel_join;
else
throw Py::ValueError(Printf("GC _joinstyle attribute must be one of butt, round, projecting; found %s", joinstyle.c_str()).str());
-
}
void
@@ -193,7 +214,7 @@
}
}
-
+// MGDTODO: Convert directly from Bbox object (numpy)
void
GCAgg::_set_clip_rectangle( const Py::Object& gc) {
//set the clip rectangle from the gc
@@ -204,7 +225,7 @@
cliprect = NULL;
Py::Object o ( gc.getAttr( "_cliprect" ) );
- if (o.ptr()==Py_None) {
+ if (o.ptr() == Py_None) {
return;
}
@@ -229,38 +250,18 @@
_VERBOSE("GCAgg::_set_clip_path");
- delete clippath;
+ Py_XINCREF(clippath);
clippath = NULL;
- Py::Object o = gc.getAttr( "_clippath" );
+ Py::Object o = gc.getAttr("_clippath");
if (o.ptr()==Py_None) {
return;
}
- agg::path_storage *tmppath;
- swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *");
- assert(descr);
- if (SWIG_ConvertPtr(o.ptr(),(void **)(&tmppath), descr, 0) == -1) {
- throw Py::TypeError("Could not convert gc path_storage");
- }
-
- tmppath->rewind(0);
- clippath = new agg::path_storage();
- clippath->copy_from(*tmppath);
- clippath->rewind(0);
- tmppath->rewind(0);
+ clippath = new PathAgg(o);
}
-Py::Object BufferRegion::to_string(const Py::Tuple &args) {
-
- // owned=true to prevent memory leak
- return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true);
-}
-
-
-
-
const size_t
RendererAgg::PIXELS_PER_INCH(96);
@@ -311,19 +312,14 @@
void
-RendererAgg::set_clipbox_rasterizer( double *cliprect) {
+RendererAgg::set_clipbox_rasterizer(double *cliprect) {
//set the clip rectangle from the gc
_VERBOSE("RendererAgg::set_clipbox_rasterizer");
-
theRasterizer->reset_clipping();
rendererBase->reset_clipping(true);
- //if (cliprect==NULL) {
- // theRasterizer->reset_clipping();
- // rendererBase->reset_clipping(true);
- //}
if (cliprect!=NULL) {
double l = cliprect[0] ;
@@ -355,273 +351,10 @@
}
-// MGDTODO: Remove this method (it has been conglomerated into draw_path
-template <class VS>
-void
-RendererAgg::_fill_and_stroke(VS& path,
- const GCAgg& gc,
- const facepair_t& face,
- bool curvy) {
- typedef agg::conv_curve<VS> curve_t;
-
- //bool isclippath(gc.clippath!=NULL);
- //if (isclippath) _process_alpha_mask(gc);
-
- if (face.first) {
- rendererAA->color(face.second);
- if (curvy) {
- curve_t curve(path);
- theRasterizer->add_path(curve);
- }
- else
- theRasterizer->add_path(path);
-
- /*
- if (isclippath) {
- typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
- typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
- pixfmt_amask_type pfa(*pixFmt, *alphaMask);
- amask_ren_type r(pfa);
- typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type;
- renderer_type ren(r);
- ren.color(gc.color);
- //std::cout << "render clippath" << std::endl;
-
- agg::render_scanlines(*theRasterizer, *slineP8, ren);
- }
- else {
- rendererAA->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
- }
- */
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
- }
-
- //now stroke the edge
- if (gc.linewidth) {
- if (curvy) {
- curve_t curve(path);
- agg::conv_stroke<curve_t> stroke(curve);
- stroke.width(gc.linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- theRasterizer->add_path(stroke);
- }
- else {
- agg::conv_stroke<VS> stroke(path);
- stroke.width(gc.linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- theRasterizer->add_path(stroke);
- }
-
-
- /*
- if ( gc.isaa ) {
- if (isclippath) {
- typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
- typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
- pixfmt_amask_type pfa(*pixFmt, *alphaMask);
- amask_ren_type r(pfa);
- typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type;
- renderer_type ren(r);
- ren.color(gc.color);
- //std::cout << "render clippath" << std::endl;
-
- agg::render_scanlines(*theRasterizer, *slineP8, ren);
- }
- else {
- rendererAA->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
- }
- }
- else {
- if (isclippath) {
- typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
- typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
- pixfmt_amask_type pfa(*pixFmt, *alphaMask);
- amask_ren_type r(pfa);
- typedef agg::renderer_scanline_bin_solid<amask_ren_type> renderer_type;
- renderer_type ren(r);
- ren.color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, ren);
- }
- else{
- rendererBin->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin);
- }
- }
-
- */
-
- if ( gc.isaa ) {
- rendererAA->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
- }
- else {
- rendererBin->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin);
- }
- }
-
-
-}
-
-Py::Object
-RendererAgg::draw_rectangle(const Py::Tuple & args) {
- _VERBOSE("RendererAgg::draw_rectangle");
- args.verify_length(6);
-
-
- GCAgg gc = GCAgg(args[0], dpi);
- facepair_t face = _get_rgba_face(args[1], gc.alpha);
-
-
- double l = Py::Float( args[2] );
- double b = Py::Float( args[3] );
- double w = Py::Float( args[4] );
- double h = Py::Float( args[5] );
-
- b = height - (b+h);
- double r = l + w;
- double t = b + h;
-
- //snapto pixel centers
- l = (int)l + 0.5;
- b = (int)b + 0.5;
- r = (int)r + 0.5;
- t = (int)t + 0.5;
-
-
- set_clipbox_rasterizer(gc.cliprect);
-
- agg::path_storage path;
-
-
- path.move_to(l, t);
- path.line_to(r, t);
- path.line_to(r, b);
- path.line_to(l, b);
- path.close_polygon();
-
- _fill_and_stroke(path, gc, face, false);
-
- return Py::Object();
-
-}
-
-Py::Object
-RendererAgg::draw_ellipse(const Py::Tuple& args) {
- _VERBOSE("RendererAgg::draw_ellipse");
- args.verify_length(7);
-
- GCAgg gc = GCAgg(args[0], dpi);
- facepair_t face = _get_rgba_face(args[1], gc.alpha);
-
- double x = Py::Float( args[2] );
- double y = Py::Float( args[3] );
- double w = Py::Float( args[4] );
- double h = Py::Float( args[5] );
- double rot = Py::Float( args[6] );
-
- double r; // rot in radians
-
- set_clipbox_rasterizer(gc.cliprect);
-
- // Approximate the ellipse with 4 bezier paths
- agg::path_storage path;
- if (rot == 0.0) // simple case
- {
- path.move_to(x, height-(y+h));
- path.arc_to(w, h, 0.0, false, true, x+w, height-y);
- path.arc_to(w, h, 0.0, false, true, x, height-(y-h));
- path.arc_to(w, h, 0.0, false, true, x-w, height-y);
- path.arc_to(w, h, 0.0, false, true, x, height-(y+h));
- path.close_polygon();
- }
- else // rotate by hand :(
- {
- // deg to rad
- r = rot * (M_PI/180.0);
- path.move_to( x+(cos(r)*w), height-(y+(sin(r)*w)));
- path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI_2*3)*h), height-(y+(sin(r+M_PI_2*3)*h)));
- path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI)*w), height-(y+(sin(r+M_PI)*w)));
- path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI_2)*h), height-(y+(sin(r+M_PI_2)*h)));
- path.arc_to(w, h, -r, false, true, x+(cos(r)*w), height-(y+(sin(r)*w)));
- path.close_polygon();
- }
-
- _fill_and_stroke(path, gc, face);
- return Py::Object();
-
-}
-
-Py::Object
-RendererAgg::draw_polygon(const Py::Tuple& args) {
- _VERBOSE("RendererAgg::draw_polygon");
-
- args.verify_length(3);
-
- GCAgg gc = GCAgg(args[0], dpi);
- facepair_t face = _get_rgba_face(args[1], gc.alpha);
-
- Py::SeqBase<Py::Object> points( args[2] );
-
- set_clipbox_rasterizer(gc.cliprect);
-
- size_t Npoints = points.length();
- if (Npoints<=0)
- return Py::Object();
-
-
- // dump the x.y vertices into a double array for faster look ahead
- // and behind access
- double *xs = new double[Npoints];
- double *ys = new double[Npoints];
-
- for (size_t i=0; i<Npoints; i++) {
- Py::SeqBase<Py::Object> xy(points[i]);
- xy = Py::Tuple(points[i]);
- xs[i] = Py::Float(xy[0]);
- ys[i] = Py::Float(xy[1]);
- ys[i] = height - ys[i];
- }
-
-
-
- agg::path_storage path;
- for (size_t j=0; j<Npoints; j++) {
-
- double x = xs[j];
- double y = ys[j];
-
- //snapto pixel centers
- x = (int)x + 0.5;
- y = (int)y + 0.5;
-
- if (j==0) path.move_to(x,y);
- else path.line_to(x,y);
- }
- path.close_polygon();
-
- _fill_and_stroke(path, gc, face, false);
-
- delete [] xs;
- delete [] ys;
-
- _VERBOSE("RendererAgg::draw_polygon DONE");
- return Py::Object();
-
-}
-
-
-
SnapData
SafeSnap::snap (const float& x, const float& y) {
xsnap = (int)x + 0.5;
ysnap = (int)y + 0.5;
-
-
if ( first || ( (xsnap!=lastxsnap) || (ysnap!=lastysnap) ) ) {
lastxsnap = xsnap;
@@ -690,9 +423,6 @@
rb.copy_from(*renderingBuffer, &r, -r.x1, -r.y1);
BufferRegion* reg = new BufferRegion(buf, r, true);
return Py::asObject(reg);
-
-
-
}
Py::Object
@@ -715,25 +445,20 @@
rendererBase->copy_from(rbuf, 0, region->rect.x1, region->rect.y1);
return Py::Object();
-
-
-
}
-
+/**
+ * Helper function to convert a Python Bbox object to an agg rectangle
+ */
template<class T>
agg::rect_base<T>
RendererAgg::bbox_to_rect(const Py::Object& o) {
//return the agg::rect for bbox, flipping y
PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2);
- if (!bbox)
+ if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2)
throw Py::TypeError
("Expected a Bbox object.");
-
- if (bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2)
- throw Py::TypeError
- ("Expected a Bbox object.");
double l = bbox->data[0];
double b = bbox->data[1];
@@ -805,469 +530,8 @@
return numIntersect;
}
-void RendererAgg::DrawQuadMesh(int meshWidth, int meshHeight, const agg::rgba8 colorArray[], const double xCoords[], const double yCoords[])
-{
- /* draw each quadrilateral */
- // agg::renderer_primitives<agg::renderer_base<agg::pixfmt_rgba32> > lineRen(*rendererBase);
- int i = 0;
- int j = 0;
- int k = 0;
- double xs[4];
- double ys[4];
- int col[4];
- int numCol;
- double ymin;
- int firstRow;
- double ymax;
- int lastRow;
- for(i=0; i < meshHeight; i++)
- {
- for(j=0; j < meshWidth; j++)
- {
- //currTime = clock();
- xs[0] = xCoords[(i * (meshWidth + 1)) + j];
- ys[0] = yCoords[(i * (meshWidth + 1)) + j];
- xs[1] = xCoords[(i * (meshWidth + 1)) + j+1];
- ys[1] = yCoords[(i * (meshWidth + 1)) + j+1];
- xs[3] = xCoords[((i+1) * (meshWidth + 1)) + j];
- ys[3] = yCoords[((i+1) * (meshWidth + 1)) + j];
- xs[2] = xCoords[((i+1) * (meshWidth + 1)) + j+1];
- ys[2] = yCoords[((i+1) * (meshWidth + 1)) + j+1];
- ymin = std::min(std::min(std::min(ys[0], ys[1]), ys[2]), ys[3]);
- ymax = std::max(std::max(std::max(ys[0], ys[1]), ys[2]), ys[3]);
- firstRow = (int)(ymin);
- lastRow = (int)(ymax);
- //timer1 += (clock() - currTime);
- //currTime = clock();
- //timer2 += (clock() - currTime);
- //currTime = clock();
- for(k = firstRow; k <= lastRow; k++)
- {
- numCol = inPolygon(k, xs, ys, col);
- if (numCol >= 2) rendererBase->copy_hline(col[0], k, col[1] - 1, colorArray[(i * meshWidth) + j]);
- if (numCol == 4) rendererBase->copy_hline(col[2], k, col[3] - 1, colorArray[(i * meshWidth) + j]);
- }
- }
- }
- return;
-}
-void RendererAgg::DrawQuadMeshEdges(int meshWidth, int meshHeight, const agg::rgba8 colorArray[], const double xCoords[], const double yCoords[])
-{
- int i, j;
- agg::renderer_primitives<agg::renderer_base<agg::pixfmt_rgba32> > lineRen(*rendererBase);
- agg::rgba8 lc(0, 0, 0, 32);
- lineRen.line_color(lc);
- /* show the vertical edges */
- for(i=0; i <= meshWidth; i++)
- {
- lineRen.move_to((int)(256.0 * (xCoords[i])), (int)(256.0 * (yCoords[i])));
- for(j=1; j <= meshHeight; j++)
- lineRen.line_to((int)(256.0 *(xCoords[(j * (meshWidth + 1))+i])), (int)(256.0 * (yCoords[(j * (meshWidth + 1))+i])));
- }
- /* show the horizontal edges */
- for(i=0; i <= meshHeight; i++)
- {
- lineRen.move_to((int)(256.0 * (xCoords[i * (meshWidth + 1)])), (int)(256.0 * (yCoords[i * (meshWidth + 1)])));
- for(j=1; j <= meshWidth; j++)
- lineRen.line_to((int)(256.0 * (xCoords[(i * (meshWidth + 1))+j])), (int)(256.0 * (yCoords[(i * (meshWidth + 1))+j])));
- }
-}
-
-
-
Py::Object
-RendererAgg::draw_lines(const Py::Tuple& args) {
-
- _VERBOSE("RendererAgg::draw_lines");
- args.verify_length(4);
-
- Py::Object xo = args[1];
- Py::Object yo = args[2];
-
- PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1);
-
- if (xa==NULL)
- throw Py::TypeError("RendererAgg::draw_lines expected numerix array");
-
-
- PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1);
-
- if (ya==NULL)
- throw Py::TypeError("RendererAgg::draw_lines expected numerix array");
-
-
- size_t Nx = xa->dimensions[0];
- size_t Ny = ya->dimensions[0];
-
- if (Nx!=Ny)
- throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str());
-
- // call gc with snapto==True if line len is 2 to fix grid line
- // problem
- bool snapto = false;
- if (Nx==2) {
- // disable subpiel rendering for len(2) horizontal or vertical
- // lines
- double x0 = *(double *)(xa->data + 0*xa->strides[0]);
- double x1 = *(double *)(xa->data + 1*xa->strides[0]);
- double y0 = *(double *)(ya->data + 0*ya->strides[0]);
- double y1 = *(double *)(ya->data + 1*ya->strides[0]);
- snapto = (x0==x1) || (y0==y1);
-
- }
- GCAgg gc = GCAgg(args[0], dpi, snapto);
-
- set_clipbox_rasterizer(gc.cliprect);
- //path_t transpath(path, xytrans);
- _process_alpha_mask(gc);
-
- agg::trans_affine xytrans = py_sequence_to_agg_transformation_matrix(args[3]);
-
- agg::path_storage path;
-
- // MGDTODO
- bool needNonlinear = false;
- // mpltransform->need_nonlinear_api();
-
- double thisx(0.0), thisy(0.0);
- double origdx(0.0), origdy(0.0), origdNorm2(0);
- bool moveto = true;
- double heightd = height;
-
- double lastx(0), lasty(0);
- double lastWrittenx(0), lastWritteny(0);
- bool clipped = false;
-
- bool haveMin = false, lastMax = true;
- double dnorm2Min(0), dnorm2Max(0);
- double maxX(0), maxY(0), minX(0), minY(0);
-
- double totdx, totdy, totdot;
- double paradx, parady, paradNorm2;
- double perpdx, perpdy, perpdNorm2;
-
- int counter = 0;
- //idea: we can skip drawing many lines: lines < 1 pixel in length, lines
- //outside of the drawing area, and we can combine sequential parallel lines
- //into a single line instead of redrawing lines over the same points.
- //The loop below works a bit like a state machine, where what it does depends
- //on what it did in the last looping. To test whether sequential lines
- //are close to parallel, I calculate the distance moved perpendicular to the
- //last line. Once it gets too big, the lines cannot be combined.
- for (size_t i=0; i<Nx; i++) {
-
- thisx = *(double *)(xa->data + i*xa->strides[0]);
- thisy = *(double *)(ya->data + i*ya->strides[0]);
-
- if (needNonlinear)
- try {
- // MGDTODO
- // mpltransform->nonlinear_only_api(&thisx, &thisy);
- }
- catch (...) {
- moveto = true;
- continue;
- }
- if (MPL_isnan64(thisx) || MPL_isnan64(thisy)) {
- moveto = true;
- continue;
- }
-
- //use agg's transformer?
- xytrans.transform(&thisx, &thisy);
- thisy = heightd - thisy; //flipy
-
- if (snapto) {
- //disable subpixel rendering for horizontal or vertical lines of len=2
- //because it causes irregular line widths for grids and ticks
- thisx = (int)thisx + 0.5;
- thisy = (int)thisy + 0.5;
- }
-
- //if we are starting a new path segment, move to the first point + init
- if(moveto){
- path.move_to(thisx, thisy);
- lastx = thisx;
- lasty = thisy;
- origdNorm2 = 0; //resets the orig-vector variables (see if-statement below)
- moveto = false;
- continue;
- }
-
- //don't render line segments less that on pixel long!
- if (fabs(thisx-lastx) < 1.0 && fabs(thisy-lasty) < 1.0 ){
- continue; //don't update lastx this time!
- }
-
- //skip any lines that are outside the drawing area. Note: More lines
- //could be clipped, but a more involved calculation would be needed
- if( (thisx < 0 && lastx < 0 ) ||
- (thisx > width && lastx > width ) ||
- (thisy < 0 && lasty < 0 ) ||
- (thisy > height && lasty > height) ){
- lastx = thisx;
- lasty = thisy;
- clipped = true;
- continue;
- }
-
- //if we have no orig vector, set it to this vector and continue.
- //this orig vector is the reference vector we will build up the line to
- if(origdNorm2 == 0){
- //if we clipped after the moveto but before we got here, redo the moveto
- if(clipped){
- path.move_to(lastx, lasty);
- clipped = false;
- }
-
- origdx = thisx - lastx;
- origdy = thisy - lasty;
- origdNorm2 = origdx*origdx + origdy*origdy;
-
- //set all the variables to reflect this new orig vecor
- dnorm2Max = origdNorm2;
- dnorm2Min = 0;
- haveMin = false;
- lastMax = true;
- maxX = thisx;
- maxY = thisy;
- minX = lastx;
- minY = lasty;
-
- lastWrittenx = lastx;
- lastWritteny = lasty;
-
- //set the last point seen
- lastx = thisx;
- lasty = thisy;
- continue;
- }
-
- //if got to here, then we have an orig vector and we just got
- //a vector in the sequence.
-
- //check that the perpendicular distance we have moved from the
- //last written point compared to the line we are building is not too
- //much. If o is the orig vector (we are building on), and v is the vector
- //from the last written point to the current point, then the perpendicular
- //vector is p = v - (o.v)o, and we normalize o (by dividing the
- //second term by o.o).
-
- //get the v vector
- totdx = thisx - lastWrittenx;
- totdy = thisy - lastWritteny;
- totdot = origdx*totdx + origdy*totdy;
-
- //get the para vector ( = (o.v)o/(o.o) )
- paradx = totdot*origdx/origdNorm2;
- parady = totdot*origdy/origdNorm2;
- paradNorm2 = paradx*paradx + parady*parady;
-
- //get the perp vector ( = v - para )
- perpdx = totdx - paradx;
- perpdy = totdy - parady;
- perpdNorm2 = perpdx*perpdx + perpdy*perpdy;
-
- //if the perp vector is less than some number of (squared) pixels in size,
- //then merge the current vector
- if(perpdNorm2 < 0.25 ){
- //check if the current vector is parallel or
- //anti-parallel to the orig vector. If it is parallel, test
- //if it is the longest of the vectors we are merging in that direction.
- //If anti-p, test if it is the longest in the opposite direction (the
- //min of our final line)
-
- lastMax = false;
- if(totdot >= 0){
- if(paradNorm2 > dnorm2Max){
- lastMax = true;
- dnorm2Max = paradNorm2;
- maxX = lastWrittenx + paradx;
- maxY = lastWritteny + parady;
- }
- }
- else{
-
- haveMin = true;
- if(paradNorm2 > dnorm2Min){
- dnorm2Min = paradNorm2;
- minX = lastWrittenx + paradx;
- minY = lastWritteny + parady;
- }
- }
-
- lastx = thisx;
- lasty = thisy;
- continue;
- }
-
- //if we get here, then this vector was not similar enough to the line
- //we are building, so we need to draw that line and start the next one.
-
- //if the line needs to extend in the opposite direction from the direction
- //we are drawing in, move back to we start drawing from back there.
- if(haveMin){
- path.line_to(minX, minY); //would be move_to if not for artifacts
- }
-
- path.line_to(maxX, maxY);
-
- //if we clipped some segments between this line and the next line
- //we are starting, we also need to move to the last point.
- if(clipped){
- path.move_to(lastx, lasty);
- }
- else if(!lastMax){
- //if the last line was not the longest line, then move back to the end
- //point of the last line in the sequence. Only do this if not clipped,
- //since in that case lastx,lasty is not part of the line just drawn.
- path.line_to(lastx, lasty); //would be move_to if not for artifacts
- }
-
- //std::cout << "draw lines (" << lastx << ", " << lasty << ")" << std::endl;
-
- //now reset all the variables to get ready for the next line
-
- origdx = thisx - lastx;
- origdy = thisy - lasty;
- origdNorm2 = origdx*origdx + origdy*origdy;
-
- dnorm2Max = origdNorm2;
- dnorm2Min = 0;
- haveMin = false;
- lastMax = true;
- maxX = thisx;
- maxY = thisy;
- minX = lastx;
- minY = lasty;
-
- lastWrittenx = lastx;
- lastWritteny = lasty;
-
- clipped = false;
-
- lastx = thisx;
- lasty = thisy;
-
- counter++;
- }
-
- //draw the last line, which is usually not drawn in the loop
- if(origdNorm2 != 0){
- if(haveMin){
- path.line_to(minX, minY); //would be move_to if not for artifacts
- }
-
- path.line_to(maxX, maxY);
- }
-
- //std::cout << "drew " << counter+1 << " lines" << std::endl;
-
- Py_XDECREF(xa);
- Py_XDECREF(ya);
-
- //typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t;
- //path_t transpath(path, xytrans);
- _VERBOSE("RendererAgg::draw_lines rendering lines path");
- _render_lines_path(path, gc);
-
- _VERBOSE("RendererAgg::draw_lines DONE");
- return Py::Object();
-
-}
-
-bool
-RendererAgg::_process_alpha_mask(const GCAgg& gc)
- //if gc has a clippath set, process the alpha mask and return True,
- //else return False
-{
- if (gc.clippath==NULL) {
- return false;
- }
- if (0 &(gc.clippath==lastclippath)) {
- //std::cout << "seen it" << std::endl;
- return true;
- }
- rendererBaseAlphaMask->clear(agg::gray8(0, 0));
- gc.clippath->rewind(0);
- theRasterizer->add_path(*(gc.clippath));
- rendererAlphaMask->color(agg::gray8(255,255));
- agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask);
- lastclippath = gc.clippath;
- return true;
-}
-
-template<class PathSource>
-void
-RendererAgg::_render_lines_path(PathSource &path, const GCAgg& gc) {
- _VERBOSE("RendererAgg::_render_lines_path");
- typedef PathSource path_t;
- //typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t;
- typedef agg::conv_stroke<path_t> stroke_t;
- typedef agg::conv_dash<path_t> dash_t;
-
- bool isclippath(gc.clippath!=NULL);
-
- if (gc.dasha==NULL ) { //no dashes
- stroke_t stroke(path);
- stroke.width(gc.linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- theRasterizer->add_path(stroke);
- }
- else {
- dash_t dash(path);
-
- //todo: dash.dash_start(gc.dashOffset);
- for (size_t i=0; i<gc.Ndash/2; i+=1)
- dash.add_dash(gc.dasha[2*i], gc.dasha[2*i+1]);
-
- agg::conv_stroke<dash_t> stroke(dash);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- stroke.width(gc.linewidth);
- theRasterizer->add_path(stroke); //boyle freeze is herre
- }
-
-
- if ( gc.isaa ) {
- if (isclippath) {
- typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
- typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
- pixfmt_amask_type pfa(*pixFmt, *alphaMask);
- amask_ren_type r(pfa);
- typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type;
- renderer_type ren(r);
- ren.color(gc.color);
- //std::cout << "render clippath" << std::endl;
-
- agg::render_scanlines(*theRasterizer, *slineP8, ren);
- }
- else {
- rendererAA->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
- }
- }
- else {
- if (isclippath) {
- typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
- typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
- pixfmt_amask_type pfa(*pixFmt, *alphaMask);
- amask_ren_type r(pfa);
- typedef agg::renderer_scanline_bin_solid<amask_ren_type> renderer_type;
- renderer_type ren(r);
- ren.color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, ren);
- }
- else{
- rendererBin->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin);
- }
- }
-}
-
-Py::Object
RendererAgg::draw_markers(const Py::Tuple& args) {
typedef agg::conv_transform<agg::path_storage> transformed_path_t;
typedef agg::conv_curve<transformed_path_t> curve_t;
@@ -1284,10 +548,10 @@
if (!PathAgg::check(marker_path_obj))
throw Py::TypeError("Native path object is not of correct type");
PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr());
- agg::trans_affine marker_trans = py_sequence_to_agg_transformation_matrix(args[2]);
+ agg::trans_affine marker_trans = py_to_agg_transformation_matrix(args[2]);
Py::Object vertices_obj = args[3];
Py::Object codes_obj = args[4];
- agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[5]);
+ agg::trans_affine trans = py_to_agg_transformation_matrix(args[5]);
facepair_t face = _get_rgba_face(args[6], gc.alpha);
// Deal with the difference in y-axis direction
@@ -1337,7 +601,8 @@
unsigned strokeSize = scanlines.byte_size();
strokeCache = new agg::int8u[strokeSize]; // or any container
scanlines.serialize(strokeCache);
-
+
+ // MGDTODO: Clean this up and support clippaths as well
theRasterizer->reset_clipping();
if (gc.cliprect==NULL) {
rendererBase->reset_clipping(true);
@@ -1557,14 +822,20 @@
Py::Object
RendererAgg::convert_to_native_path(const Py::Tuple& args) {
_VERBOSE("RendererAgg::draw_image");
- args.verify_length(2);
+ args.verify_length(1);
- Py::Object vertices_obj = args[0];
- Py::Object codes_obj = args[1];
+ Py::Object path = args[0];
+
+ return Py::asObject(new PathAgg(path));
+}
+
+PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) {
+ Py::Object vertices_obj = path_obj.getAttr("vertices");
+ Py::Object codes_obj = path_obj.getAttr("codes");
+
PyArrayObject* vertices = NULL;
PyArrayObject* codes = NULL;
- PathAgg* path = NULL;
try {
vertices = (PyArrayObject*)PyArray_ContiguousFromObject
@@ -1576,8 +847,6 @@
if (!codes)
throw Py::ValueError("Invalid codes array.");
- path = new PathAgg();
-
size_t next_vertex_stride = vertices->strides[0];
size_t next_axis_stride = vertices->strides[1];
size_t code_stride = codes->strides[0];
@@ -1593,31 +862,31 @@
switch (*(unsigned char*)(code_i)) {
case MOVETO:
GET_NEXT_VERTEX(x0, y0);
- path->move_to(x0, y0);
+ move_to(x0, y0);
_VERBOSE("MOVETO");
break;
case LINETO:
GET_NEXT_VERTEX(x0, y0);
- path->line_to(x0, y0);
+ line_to(x0, y0);
_VERBOSE("LINETO");
break;
case CURVE3:
GET_NEXT_VERTEX(x0, y0);
GET_NEXT_VERTEX(x1, y1);
- path->curve3(x0, y0, x1, y1);
- path->curvy = true;
+ curve3(x0, y0, x1, y1);
+ curvy = true;
_VERBOSE("CURVE3");
break;
case CURVE4:
GET_NEXT_VERTEX(x0, y0);
GET_NEXT_VERTEX(x1, y1);
GET_NEXT_VERTEX(x2, y2);
- path->curve4(x0, y0, x1, y1, x2, y2);
- path->curvy = true;
+ curve4(x0, y0, x1, y1, x2, y2);
+ curvy = true;
_VERBOSE("CURVE4");
break;
case CLOSEPOLY:
- path->close_polygon();
+ close_polygon();
_VERBOSE("CLOSEPOLY");
break;
}
@@ -1626,14 +895,11 @@
} catch(...) {
Py_XDECREF(vertices);
Py_XDECREF(codes);
- delete path;
throw;
}
Py_XDECREF(vertices);
Py_XDECREF(codes);
-
- return Py::asObject(path);
}
Py::Object
@@ -1643,7 +909,11 @@
typedef agg::conv_stroke<curve_t> stroke_t;
typedef agg::conv_dash<curve_t> dash_t;
typedef agg::conv_stroke<dash_t> stroke_dash_t;
- //draw_path(gc, rgbFace, path, transform)
+ typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type;
+ typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type;
+ typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type;
+ typedef agg::renderer_scanline_bin_solid<amask_ren_type> amask_bin_renderer_type;
+
theRasterizer->reset_clipping();
_VERBOSE("RendererAgg::draw_path");
@@ -1654,52 +924,118 @@
if (!PathAgg::check(path_obj))
throw Py::TypeError("Native path object is not of correct type");
PathAgg* path = static_cast<PathAgg*>(path_obj.ptr());
- agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[2]);
+ agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]);
facepair_t face = _get_rgba_face(args[3], gc.alpha);
trans *= agg::trans_affine_scaling(1.0, -1.0);
trans *= agg::trans_affine_translation(0.0, (double)height);
- transformed_path_t tpath(*path, trans);
- // MGDTODO: See if there is any advantage to only curving if necessary
- curve_t curve(tpath);
+ transformed_path_t* tpath = NULL;
+ agg::path_storage new_path;
- set_clipbox_rasterizer(gc.cliprect);
-
- if (face.first) {
- rendererAA->color(face.second);
- theRasterizer->add_path(curve);
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
+ bool has_clippath = (gc.clippath != NULL);
+
+ if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) {
+ rendererBaseAlphaMask->clear(agg::gray8(0, 0));
+ gc.clippath->rewind(0);
+ transformed_path_t transformed_clippath(*(gc.clippath), trans);
+ theRasterizer->add_path(transformed_clippath);
+ rendererAlphaMask->color(agg::gray8(255, 255));
+ agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask);
+ lastclippath = gc.clippath;
+ lastclippath_transform = trans;
}
- if (gc.linewidth) {
- if (gc.dasha == NULL) {
- stroke_t stroke(curve);
- stroke.width(gc.linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- theRasterizer->add_path(stroke);
- } else {
- dash_t dash(curve);
- for (size_t i = 0; i < (gc.Ndash / 2); ++i)
- dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]);
- stroke_dash_t stroke(dash);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- stroke.width(gc.linewidth);
- theRasterizer->add_path(stroke); //boyle freeze is herre
+ try {
+ // If this is a straight horizontal or vertical line, quantize to nearest
+ // pixels
+ if (path->total_vertices() == 2) {
+ double x0, y0, x1, y1;
+ path->vertex(0, &x0, &y0);
+ trans.transform(&x0, &y0);
+ path->vertex(1, &x1, &y1);
+ trans.transform(&x1, &y1);
+ if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) {
+ new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5);
+ new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5);
+ tpath = new transformed_path_t(new_path, agg::trans_affine());
+ }
}
+
+ if (!tpath) {
+ tpath = new transformed_path_t(*path, trans);
+ }
+
+ // Benchmarking shows that there is no noticable slowdown to always
+ // treating paths as having curved segments. Doing so greatly
+ // simplifies the code
+ curve_t curve(*tpath);
- if ( gc.isaa ) {
- rendererAA->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
+ set_clipbox_rasterizer(gc.cliprect);
+
+ if (face.first) {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(*pixFmt, *alphaMask);
+ amask_ren_type r(pfa);
+ amask_aa_renderer_type ren(r);
+ ren.color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineP8, ren);
+ } else{
+ rendererAA->color(face.second);
+ theRasterizer->add_path(curve);
+ agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
+ }
}
- else {
- rendererBin->color(gc.color);
- agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin);
+
+ if (gc.linewidth) {
+ if (gc.dasha == NULL) {
+ stroke_t stroke(curve);
+ stroke.width(gc.linewidth);
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ theRasterizer->add_path(stroke);
+ } else {
+ dash_t dash(curve);
+ for (size_t i = 0; i < (gc.Ndash / 2); ++i)
+ dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]);
+ stroke_dash_t stroke(dash);
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ stroke.width(gc.linewidth);
+ theRasterizer->add_path(stroke);
+ }
+
+ if (gc.isaa) {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(*pixFmt, *alphaMask);
+ amask_ren_type r(pfa);
+ amask_aa_renderer_type ren(r);
+ ren.color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineP8, ren);
+ } else {
+ rendererAA->color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
+ }
+ } else {
+ if (has_clippath) {
+ pixfmt_amask_type pfa(*pixFmt, *alphaMask);
+ amask_ren_type r(pfa);
+ amask_bin_renderer_type ren(r);
+ ren.color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineP8, ren);
+ } else {
+ rendererBin->color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin);
+ }
+ }
}
+ } catch (...) {
+ delete tpath;
+ throw;
}
+ delete tpath;
+
return Py::Object();
}
@@ -2033,18 +1369,10 @@
behaviors().name("RendererAgg");
behaviors().doc("The agg backend extension module");
- add_varargs_method("draw_rectangle", &RendererAgg::draw_rectangle,
- "draw_rectangle(gc, rgbFace, l, b, w, h)\n");
- add_varargs_method("draw_ellipse", &RendererAgg::draw_ellipse,
- "draw_ellipse(gc, rgbFace, x, y, w, h)\n");
- add_varargs_method("draw_polygon", &RendererAgg::draw_polygon,
- "draw_polygon(gc, rgbFace, points)\n");
add_varargs_method("draw_path",...
[truncated message content] |
|
From: <md...@us...> - 2007-09-17 13:41:58
|
Revision: 3854
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3854&view=rev
Author: mdboom
Date: 2007-09-17 06:41:38 -0700 (Mon, 17 Sep 2007)
Log Message:
-----------
Transferring work-in-progress
Modified Paths:
--------------
branches/transforms/examples/shared_axis_demo.py
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/patches.py
branches/transforms/lib/matplotlib/path.py
branches/transforms/src/_backend_agg.cpp
branches/transforms/src/_backend_agg.h
Modified: branches/transforms/examples/shared_axis_demo.py
===================================================================
--- branches/transforms/examples/shared_axis_demo.py 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/examples/shared_axis_demo.py 2007-09-17 13:41:38 UTC (rev 3854)
@@ -36,12 +36,12 @@
s2 = exp(-t)
s3 = sin(4*pi*t)
ax1 = subplot(311)
-plot(t,s1)
+plot(t,s1, "bH")
setp( ax1.get_xticklabels(), fontsize=6)
## share x only
ax2 = subplot(312, sharex=ax1)
-plot(t, s2)
+plot(t, s2, "b<")
# make these tick labels invisible
setp( ax2.get_xticklabels(), visible=False)
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-17 13:41:38 UTC (rev 3854)
@@ -36,21 +36,25 @@
"""
pass
+ def _get_cached_native_path(self, path):
+ native_path = self._native_paths.get(path)
+ if native_path is None:
+ import matplotlib.patches
+ print "CACHE MISS", path
+ native_path = self.convert_to_native_path(path)
+ self._native_paths[path] = native_path
+ return native_path
+
def draw_path(self, gc, path, transform, rgbFace = None):
"""
Handles the caching of the native path associated with the
given path and calls the underlying backend's _draw_path to
actually do the drawing.
"""
- native_path = self._native_paths.get(path)
- if native_path is None:
- import matplotlib.patches
- print "CACHE MISS", path
- native_path = self.convert_to_native_path(path)
- self._native_paths[path] = native_path
- self._draw_path(gc, native_path, transform, rgbFace)
+ native_path = self._get_cached_native_path(path)
+ self._draw_native_path(gc, native_path, transform, rgbFace)
- def _draw_path(self, gc, native_path, transform, rgbFace):
+ def _draw_native_path(self, gc, native_path, transform, rgbFace):
"""
Draw the native path object with the given GraphicsContext and
transform. The transform passed in will always be affine.
@@ -107,9 +111,10 @@
return False
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
- pass
-
- def _draw_markers(self, bgc, path, rgbFace, x, y, trans):
+ native_marker_path = self._get_cached_native_path(marker_path)
+ self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
+
+ def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
"""
This method is currently underscore hidden because the
draw_markers method is being used as a sentinel for newstyle
@@ -130,7 +135,7 @@
vec6 = transform.as_vec6_val()
...backend dependent affine...
"""
- pass
+ raise NotImplementedError
def draw_line_collection(self, segments, transform, clipbox,
colors, linewidths, linestyle, antialiaseds,
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-17 13:41:38 UTC (rev 3854)
@@ -140,7 +140,7 @@
def convert_to_native_path(self, path):
return self._renderer.convert_to_native_path(path.vertices, path.codes)
- def _draw_path(self, gc, native_path, transform, rgbFace):
+ def _draw_native_path(self, gc, native_path, transform, rgbFace):
return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace)
def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation):
@@ -172,8 +172,12 @@
def draw_lines(self, gc, x, y, transform):
return self._renderer.draw_lines(gc, x, y, transform.to_values())
- def draw_markers(self, gc, path, color, x, y, transform):
- return self._renderer.draw_markers(gc, path, color, x, y, transform.to_values())
+ def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
+ return self._renderer.draw_markers(
+ gc,
+ native_marker_path, marker_trans.to_values(),
+ path.vertices, path.codes, trans.to_values(),
+ rgbFace)
def draw_polygon(self, *args):
return self._renderer.draw_polygon(*args)
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-17 13:41:38 UTC (rev 3854)
@@ -520,7 +520,7 @@
lineFunc(renderer, gc, self._path)
# MGDTODO: Deal with markers
- if self._marker is not None and False:
+ if self._marker is not None:
gc = renderer.new_gc()
self._set_gc_clip(gc)
gc.set_foreground(self.get_markeredgecolor())
@@ -713,6 +713,9 @@
pass
def _draw_steps(self, renderer, gc, xt, yt):
+ # MGDTODO: This is a quirky one. The step-plotting part
+ # should probably be moved to where the path is generated
+ # in recache, and then just drawn with _draw_solid
siz=len(xt)
if siz<2: return
xt2=npy.ones((2*siz,), xt.dtype)
@@ -727,323 +730,132 @@
renderer.draw_lines(gc, xt2, yt2)
def _draw_solid(self, renderer, gc, path):
- # if len(xt)<2: return
gc.set_linestyle('solid')
renderer.draw_path(gc, path, self.get_transform())
- def _draw_dashed(self, renderer, gc, xt, yt):
- if len(xt)<2: return
+ def _draw_dashed(self, renderer, gc, path):
gc.set_linestyle('dashed')
if self._dashSeq is not None:
gc.set_dashes(0, self._dashSeq)
- if self._newstyle:
- renderer.draw_lines(gc, xt, yt, self.get_transform())
- else:
- renderer.draw_lines(gc, xt, yt)
+ renderer.draw_path(gc, path, self.get_transform())
- def _draw_dash_dot(self, renderer, gc, xt, yt):
- if len(xt)<2: return
+ def _draw_dash_dot(self, renderer, gc, path):
gc.set_linestyle('dashdot')
- if self._newstyle:
- renderer.draw_lines(gc, xt, yt, self.get_transform())
- else:
- renderer.draw_lines(gc, xt, yt)
+ renderer.draw_path(gc, path, self.get_transform())
- def _draw_dotted(self, renderer, gc, xt, yt):
-
- if len(xt)<2: return
+
+ def _draw_dotted(self, renderer, gc, path):
gc.set_linestyle('dotted')
- if self._newstyle:
- renderer.draw_lines(gc, xt, yt, self.get_transform())
- else:
- renderer.draw_lines(gc, xt, yt)
+ renderer.draw_path(gc, path, self.get_transform())
- def _draw_point(self, renderer, gc, xt, yt):
+
+ def _draw_point(self, renderer, gc, path):
+ self._draw_circle(renderer, gc, path, point = True)
- r = 0.5 * renderer.points_to_pixels(self._markersize)
- r *= self._point_size_reduction
- gc.set_linewidth(0)
- if r <= 0.5:
- self._draw_pixel(renderer, gc, xt, yt)
- elif r <= 2:
- self._draw_hexagon1(renderer, gc, xt, yt, point=True)
- else:
- self._draw_circle(renderer, gc, xt, yt, point=True)
-
- def _draw_pixel(self, renderer, gc, xt, yt):
- if self._newstyle:
- rgbFace = self._get_rgb_face()
- path = agg.path_storage()
- path.move_to(-0.5, -0.5)
- path.line_to(-0.5, 0.5)
- path.line_to(0.5, 0.5)
- path.line_to(0.5, -0.5)
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_point(gc, x, y)
-
-
- def _draw_circle(self, renderer, gc, xt, yt, point=False):
-
+ def _draw_pixel(self, renderer, gc, path):
+ rgbFace = self._get_rgb_face()
+ transform = Affine2D().translate(-0.5, -0.5)
+ renderer.draw_markers(gc, Path.unit_rectangle, transform,
+ path, self.get_transform(), rgbFace)
+
+
+ def _draw_circle(self, renderer, gc, path, point=False):
w = renderer.points_to_pixels(self._markersize)
if point:
w *= self._point_size_reduction
+ w *= 0.5
-
rgbFace = self._get_rgb_face()
+ transform = Affine2D().scale(w, w)
+ renderer.draw_markers(
+ gc, Path.unit_circle(), transform, path, self.get_transform(),
+ rgbFace)
- if self._newstyle:
- N = 50.0
- r = w/2.
- rads = (2*math.pi/N)*npy.arange(N)
- xs = r*npy.cos(rads)
- ys = r*npy.sin(rads)
- # todo: use curve3!
- path = agg.path_storage()
- path.move_to(xs[0], ys[0])
- for x, y in zip(xs[1:], ys[1:]):
- path.line_to(x, y)
- path.end_poly()
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt,yt):
- renderer.draw_arc(gc, rgbFace,
- x, y, w, w, 0.0, 360.0, 0.0)
-
-
-
- def _draw_triangle_up(self, renderer, gc, xt, yt):
-
-
+ _triangle_path = Path([[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0]])
+ def _draw_triangle_up(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset, offset)
rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, self._triangle_path, transform,
+ path, self.get_transform(), rgbFace)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, offset)
- path.line_to(-offset, -offset)
- path.line_to(offset, -offset)
- path.end_poly()
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x, y+offset),
- (x-offset, y-offset),
- (x+offset, y-offset) )
- renderer.draw_polygon(gc, rgbFace, verts)
-
- def _draw_triangle_down(self, renderer, gc, xt, yt):
+ def _draw_triangle_down(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset, -offset)
rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, self._triangle_path, transform,
+ path, self.get_transform(), rgbFace)
- if self._newstyle:
-
- path = agg.path_storage()
- path.move_to(-offset, offset)
- path.line_to(offset, offset)
- path.line_to(0, -offset)
- path.end_poly()
-
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x-offset, y+offset),
- (x+offset, y+offset),
- (x, y-offset))
- renderer.draw_polygon(gc, rgbFace, verts)
-
- def _draw_triangle_left(self, renderer, gc, xt, yt):
+
+ def _draw_triangle_left(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset, offset).rotate_deg(90)
rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, self._triangle_path, transform,
+ path, self.get_transform(), rgbFace)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset, 0)
- path.line_to(offset, -offset)
- path.line_to(offset, offset)
- path.end_poly()
-
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x-offset, y),
- (x+offset, y-offset),
- (x+offset, y+offset))
- renderer.draw_polygon(gc, rgbFace, verts)
-
-
- def _draw_triangle_right(self, renderer, gc, xt, yt):
+ def _draw_triangle_right(self, renderer, gc, path):
offset = 0.5*renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset, offset).rotate_deg(-90)
rgbFace = self._get_rgb_face()
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(offset, 0)
- path.line_to(-offset, -offset)
- path.line_to(-offset, offset)
- path.end_poly()
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x+offset, y),
- (x-offset, y-offset),
- (x-offset, y+offset))
- renderer.draw_polygon(gc, rgbFace, verts)
+ renderer.draw_markers(gc, self._triangle_path, transform,
+ path, self.get_transform(), rgbFace)
-
- def _draw_square(self, renderer, gc, xt, yt):
+ def _draw_square(self, renderer, gc, path):
side = renderer.points_to_pixels(self._markersize)
- offset = side*0.5
+ transform = Affine2D().translate(-0.5, -0.5).scale(side)
rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, Path.unit_rectangle(), transform,
+ path, self.get_transform(), rgbFace)
- if self._newstyle:
-
- path = agg.path_storage()
- path.move_to(-offset, -offset)
- path.line_to(-offset, offset)
- path.line_to(offset, offset)
- path.line_to(offset, -offset)
- path.end_poly()
-
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
-
- for (x,y) in zip(xt, yt):
- renderer.draw_rectangle(
- gc, rgbFace,
- x-offset, y-offset, side, side)
-
- def _draw_diamond(self, renderer, gc, xt, yt):
- offset = 0.6*renderer.points_to_pixels(self._markersize)
+
+ def _draw_diamond(self, renderer, gc, path):
+ side = renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(side)
rgbFace = self._get_rgb_face()
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(offset, 0)
- path.line_to(0, -offset)
- path.line_to(-offset, 0)
- path.line_to(0, offset)
- path.end_poly()
+ renderer.draw_markers(gc, Path.unit_rectangle(), transform,
+ path, self.get_transform(), rgbFace)
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
-
-
- for (x,y) in zip(xt, yt):
- verts = ( (x+offset, y),
- (x, y-offset),
- (x-offset, y),
- (x, y+offset))
- renderer.draw_polygon(gc, rgbFace, verts)
-
- def _draw_thin_diamond(self, renderer, gc, xt, yt):
- offset = 0.7*renderer.points_to_pixels(self._markersize)
- xoffset = 0.6*offset
+
+ def _draw_thin_diamond(self, renderer, gc, path):
+ offset = renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(offset * 0.8, offset)
rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, Path.unit_rectangle(), transform,
+ path, self.get_transform(), rgbFace)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(xoffset, 0)
- path.line_to(0, -offset)
- path.line_to(-xoffset, 0)
- path.line_to(0, offset)
- path.end_poly()
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x+xoffset, y),
- (x, y-offset),
- (x-xoffset, y),
- (x, y+offset))
- renderer.draw_polygon(gc, rgbFace, verts)
+
+ def _draw_pentagon(self, renderer, gc, path):
+ offset = 0.5 * renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset)
+ rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, Path.unit_regular_polygon(5), transform,
+ path, self.get_transform(), rgbFace)
- def _draw_pentagon(self, renderer, gc, xt, yt):
- offset = 0.6*renderer.points_to_pixels(self._markersize)
- offsetX1 = offset*0.95
- offsetY1 = offset*0.31
- offsetX2 = offset*0.59
- offsetY2 = offset*0.81
- rgbFace = self._get_rgb_face()
+
+ def _draw_hexagon1(self, renderer, gc, path, point=False):
+ offset = 0.5 * renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset)
+ rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform,
+ path, self.get_transform(), rgbFace)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, offset)
- path.line_to(-offsetX1, offsetY1)
- path.line_to(-offsetX2, -offsetY2)
- path.line_to(+offsetX2, -offsetY2)
- path.line_to(+offsetX1, offsetY1)
- path.end_poly()
-
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x, y+offset),
- (x-offsetX1, y+offsetY1),
- (x-offsetX2, y-offsetY2),
- (x+offsetX2, y-offsetY2),
- (x+offsetX1, y+offsetY1))
- renderer.draw_polygon(gc, rgbFace, verts)
-
- def _draw_hexagon1(self, renderer, gc, xt, yt, point=False):
- offset = 0.6*renderer.points_to_pixels(self._markersize)
- if point:
- offset *= self._point_size_reduction
- offsetX1 = offset*0.87
- offsetY1 = offset*0.5
- rgbFace = self._get_rgb_face()
-
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, offset)
- path.line_to(-offsetX1, offsetY1)
- path.line_to(-offsetX1, -offsetY1)
- path.line_to(0, -offset)
- path.line_to(offsetX1, -offsetY1)
- path.line_to(offsetX1, offsetY1)
- path.end_poly()
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x, y+offset),
- (x-offsetX1, y+offsetY1),
- (x-offsetX1, y-offsetY1),
- (x, y-offset),
- (x+offsetX1, y-offsetY1),
- (x+offsetX1, y+offsetY1))
- renderer.draw_polygon(gc, rgbFace, verts)
-
+
def _draw_hexagon2(self, renderer, gc, xt, yt):
- offset = 0.6*renderer.points_to_pixels(self._markersize)
- offsetX1 = offset*0.5
- offsetY1 = offset*0.87
- rgbFace = self._get_rgb_face()
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(offset, 0)
- path.line_to(offsetX1, offsetY1)
- path.line_to(-offsetX1, offsetY1)
- path.line_to(-offset, 0)
- path.line_to(-offsetX1, -offsetY1)
- path.line_to(offsetX1, -offsetY1)
- path.end_poly()
+ offset = 0.5 * renderer.points_to_pixels(self._markersize)
+ transform = Affine2D().scale(offset).rotate_deg(30)
+ rgbFace = self._get_rgb_face()
+ renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform,
+ path, self.get_transform(), rgbFace)
- renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- verts = ( (x+offset, y),
- (x+offsetX1, y+offsetY1),
- (x-offsetX1, y+offsetY1),
- (x-offset, y),
- (x-offsetX1, y-offsetY1),
- (x+offsetX1, y-offsetY1))
- renderer.draw_polygon(gc, rgbFace, verts)
-
+
def _draw_vline(self, renderer, gc, xt, yt):
offset = 0.5*renderer.points_to_pixels(self._markersize)
if self._newstyle:
@@ -1055,6 +867,7 @@
for (x,y) in zip(xt, yt):
renderer.draw_line(gc, x, y-offset, x, y+offset)
+
def _draw_hline(self, renderer, gc, xt, yt):
offset = 0.5*renderer.points_to_pixels(self._markersize)
if self._newstyle:
@@ -1066,46 +879,31 @@
for (x,y) in zip(xt, yt):
renderer.draw_line(gc, x-offset, y, x+offset, y)
- def _draw_tickleft(self, renderer, gc, xt, yt):
+ _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]])
+ def _draw_tickleft(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-offset, 0.5)
- path.line_to(0, 0.5)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x-offset, y, x, y)
+ marker_transform = Affine2D().scale(offset, 1.0)
+ renderer.draw_markers(gc, self._tickhoriz_path, marker_transform,
+ path, self.get_transform())
- def _draw_tickright(self, renderer, gc, xt, yt):
-
+ def _draw_tickright(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(0, 0.5)
- path.line_to(offset, 0.5)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y, x+offset, y)
+ marker_transform = Affine2D().scale(-offset, 1.0)
+ renderer.draw_markers(gc, self._tickhoriz_path, marker_transform,
+ path, self.get_transform())
- _tickup_path = Path([[-0.5, 0.0], [-0.5, 1.0]])
- def _draw_tickup(self, renderer, gc, xt, yt):
+ _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]])
+ def _draw_tickup(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
marker_transform = Affine2D().scale(1.0, offset)
- renderer.draw_markers(gc, self._tickup_path, marker_transform,
- self._path, self.get_transform())
+ renderer.draw_markers(gc, self._tickvert_path, marker_transform,
+ path, self.get_transform())
- def _draw_tickdown(self, renderer, gc, xt, yt):
+ def _draw_tickdown(self, renderer, gc, path):
offset = renderer.points_to_pixels(self._markersize)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-0.5, -offset)
- path.line_to(-0.5, 0)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y-offset, x, y)
+ marker_transform = Affine2D().scale(1.0, -offset)
+ renderer.draw_markers(gc, self._tickvert_path, marker_transform,
+ path, self.get_transform())
def _draw_plus(self, renderer, gc, xt, yt):
offset = 0.5*renderer.points_to_pixels(self._markersize)
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/lib/matplotlib/patches.py 2007-09-17 13:41:38 UTC (rev 3854)
@@ -288,7 +288,6 @@
self._update()
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
-
def _update(self):
self.update_from(self.patch)
if self.props is not None:
@@ -316,8 +315,7 @@
"""
- _path = Path(
- [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
+ _path = Path.unit_rectangle()
def __str__(self):
return str(self.__class__).split('.')[-1] \
@@ -424,8 +422,6 @@
"""
A regular polygon patch.
"""
- _polygon_cache = {}
-
def __str__(self):
return "Poly%d(%g,%g)"%(self.numVertices,self.xy[0],self.xy[1])
@@ -442,14 +438,7 @@
"""
Patch.__init__(self, **kwargs)
- path = self._polygon_cache[numVertices]
- if path is None:
- theta = 2*npy.pi/numVertices * npy.arange(numVertices)
- verts = npy.hstack((npy.cos(theta), npy.sin(theta)))
- path = Path(verts)
- self._polygon_cache[numVertices] = path
-
- self._path = path
+ self._path = Path.unit_regular_polygon(numVertices)
self._poly_transform = transforms.Affine2D() \
.scale(radius) \
.rotate(orientation) \
Modified: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/lib/matplotlib/path.py 2007-09-17 13:41:38 UTC (rev 3854)
@@ -1,21 +1,25 @@
import numpy as npy
-class Path:
+VALIDATE_PATHS = True
+
+class Path(object):
# Path codes
STOP = 0
MOVETO = 1 # 1 vertex
LINETO = 2 # 1 vertex
CURVE3 = 3 # 2 vertices
CURVE4 = 4 # 3 vertices
+ CLOSEPOLY = 5
###
# MGDTODO: I'm not sure these are supported by PS/PDF/SVG,
# so if they don't, we probably shouldn't
- CURVEN = 5
- CATROM = 6
- UBSPLINE = 7
+ CURVEN = 6
+ CATROM = 7
+ UBSPLINE = 8
####
- CLOSEPOLY = 0x0F # 0 vertices
+ NUM_VERTICES = [0, 1, 1, 2, 3, 0]
+
code_type = npy.uint8
def __init__(self, vertices, codes=None, closed=True):
@@ -38,9 +42,14 @@
self._codes = codes
assert self._codes.ndim == 1
- # MGDTODO: Maybe we should add some quick-and-dirty check that
- # the number of vertices is correct for the code array
+ if VALIDATE_PATHS:
+ i = 0
+ NUM_VERTICES = self.NUM_VERTICES
+ for code in codes:
+ i += NUM_VERTICES[code]
+ assert i == len(self.vertices)
+
def _get_codes(self):
return self._codes
codes = property(_get_codes)
@@ -48,3 +57,74 @@
def _get_vertices(self):
return self._vertices
vertices = property(_get_vertices)
+
+ def iter_endpoints(self):
+ i = 0
+ NUM_VERTICES = self.NUM_VERTICES
+ vertices = self.vertices
+ for code in self.codes:
+ num_vertices = NUM_VERTICES[code]
+ if num_vertices >= 1:
+ i += num_vertices - 1
+ yield vertices[i]
+ i += 1
+
+ _unit_rectangle = None
+ #@classmethod
+ def unit_rectangle(cls):
+ if cls._unit_rectangle is None:
+ cls._unit_rectangle = \
+ Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
+ return cls._unit_rectangle
+ unit_rectangle = classmethod(unit_rectangle)
+
+ _unit_regular_polygons = {}
+ #@classmethod
+ def unit_regular_polygon(cls, numVertices):
+ path = cls._unit_regular_polygons.get(numVertices)
+ if path is None:
+ theta = 2*npy.pi/numVertices * npy.arange(numVertices)
+ # This is to make sure the polygon always "points-up"
+ theta += npy.pi / 2.0
+ verts = npy.vstack((npy.cos(theta), npy.sin(theta))).transpose()
+ path = Path(verts)
+ cls._unit_regular_polygons[numVertices] = path
+ return path
+ unit_regular_polygon = classmethod(unit_regular_polygon)
+
+ _unit_circle = None
+ #@classmethod
+ def unit_circle(cls):
+ # MGDTODO: Optimize?
+ if cls._unit_circle is None:
+ offset = 4.0 * (npy.sqrt(2) - 1) / 3.0
+ vertices = npy.array(
+ [[-1.0, 0.0],
+
+ [-1.0, offset],
+ [-offset, 1.0],
+ [0.0, 1.0],
+
+ [offset, 1.0],
+ [1.0, offset],
+ [1.0, 0.0],
+
+ [1.0, -offset],
+ [offset, -1.0],
+ [0.0, -1.0],
+
+ [-offset, -1.0],
+ [-1.0, -offset],
+ [-1.0, 0.0]],
+ npy.float_)
+ codes = npy.array(
+ [cls.MOVETO,
+ cls.CURVE4,
+ cls.CURVE4,
+ cls.CURVE4,
+ cls.CURVE4,
+ cls.CLOSEPOLY],
+ cls.code_type)
+ cls._unit_circle = Path(vertices, codes)
+ return cls._unit_circle
+ unit_circle = classmethod(unit_circle)
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-17 13:41:38 UTC (rev 3854)
@@ -64,6 +64,20 @@
Py::Float(seq[5]));
}
+// MGDTODO: Implement this as a nice iterator
+inline void get_next_vertex(const char* & vertex_i, const char* vertex_end,
+ double& x, double& y,
+ size_t next_vertex_stride,
+ size_t next_axis_stride) {
+ if (vertex_i + next_axis_stride >= vertex_end)
+ throw Py::ValueError("Error parsing path. Read past end of vertices");
+ x = *(double*)vertex_i;
+ y = *(double*)(vertex_i + next_axis_stride);
+ vertex_i += next_vertex_stride;
+}
+
+#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride)
+
GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) :
dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0),
cliprect(NULL), clippath(NULL),
@@ -1255,146 +1269,134 @@
Py::Object
RendererAgg::draw_markers(const Py::Tuple& args) {
+ typedef agg::conv_transform<agg::path_storage> transformed_path_t;
+ typedef agg::conv_curve<transformed_path_t> curve_t;
+ typedef agg::conv_stroke<curve_t> stroke_t;
+ typedef agg::conv_dash<curve_t> dash_t;
+ typedef agg::conv_stroke<dash_t> stroke_dash_t;
+
theRasterizer->reset_clipping();
- _VERBOSE("RendererAgg::_draw_markers_cache");
- args.verify_length(6);
+ args.verify_length(7);
- _VERBOSE("RendererAgg::_draw_markers_cache setting gc");
GCAgg gc = GCAgg(args[0], dpi);
+ Py::Object marker_path_obj = args[1];
+ if (!PathAgg::check(marker_path_obj))
+ throw Py::TypeError("Native path object is not of correct type");
+ PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr());
+ agg::trans_affine marker_trans = py_sequence_to_agg_transformation_matrix(args[2]);
+ Py::Object vertices_obj = args[3];
+ Py::Object codes_obj = args[4];
+ agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[5]);
+ facepair_t face = _get_rgba_face(args[6], gc.alpha);
+
+ // Deal with the difference in y-axis direction
+ marker_trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_translation(0.0, (double)height);
+ marker_path->rewind(0);
+ transformed_path_t marker_path_transformed(*marker_path, marker_trans);
+ curve_t marker_path_curve(marker_path_transformed);
- agg::path_storage *ppath;
-
- swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *");
- assert(descr);
- if (SWIG_ConvertPtr(args[1].ptr(),(void **)(&ppath), descr, 0) == -1) {
- throw Py::TypeError("Could not convert path_storage");
- }
- facepair_t face = _get_rgba_face(args[2], gc.alpha);
-
- Py::Object xo = args[3];
- Py::Object yo = args[4];
-
- PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1);
-
- if (xa==NULL)
- throw Py::TypeError("RendererAgg::_draw_markers_cache expected numerix array");
-
-
- PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1);
-
- if (ya==NULL)
- throw Py::TypeError("RendererAgg::_draw_markers_cache expected numerix array");
-
- agg::trans_affine xytrans = py_sequence_to_agg_transformation_matrix(args[5]);
-
- size_t Nx = xa->dimensions[0];
- size_t Ny = ya->dimensions[0];
-
- if (Nx!=Ny)
- throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str());
-
-
- double heightd = double(height);
-
-
- ppath->rewind(0);
- ppath->flip_y(0,0);
- typedef agg::conv_curve<agg::path_storage> curve_t;
- curve_t curve(*ppath);
-
//maxim's suggestions for cached scanlines
agg::scanline_storage_aa8 scanlines;
theRasterizer->reset();
agg::int8u* fillCache = NULL;
- unsigned fillSize = 0;
- if (face.first) {
- theRasterizer->add_path(curve);
+ agg::int8u* strokeCache = NULL;
+ PyArrayObject* vertices = NULL;
+ PyArrayObject* codes = NULL;
+
+ try {
+ vertices = (PyArrayObject*)PyArray_ContiguousFromObject
+ (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2);
+ if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2)
+ throw Py::ValueError("Invalid vertices array.");
+ codes = (PyArrayObject*)PyArray_ContiguousFromObject
+ (codes_obj.ptr(), PyArray_UINT8, 1, 1);
+ if (!codes)
+ throw Py::ValueError("Invalid codes array.");
+
+ unsigned fillSize = 0;
+ if (face.first) {
+ theRasterizer->add_path(marker_path_curve);
+ agg::render_scanlines(*theRasterizer, *slineP8, scanlines);
+ fillSize = scanlines.byte_size();
+ fillCache = new agg::int8u[fillSize]; // or any container
+ scanlines.serialize(fillCache);
+ }
+
+ stroke_t stroke(marker_path_curve);
+ stroke.width(gc.linewidth);
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ theRasterizer->reset();
+ theRasterizer->add_path(stroke);
agg::render_scanlines(*theRasterizer, *slineP8, scanlines);
- fillSize = scanlines.byte_size();
- fillCache = new agg::int8u[fillSize]; // or any container
- scanlines.serialize(fillCache);
- }
+ unsigned strokeSize = scanlines.byte_size();
+ strokeCache = new agg::int8u[strokeSize]; // or any container
+ scanlines.serialize(strokeCache);
- agg::conv_stroke<curve_t> stroke(curve);
- stroke.width(gc.linewidth);
- stroke.line_cap(gc.cap);
- stroke.line_join(gc.join);
- theRasterizer->reset();
- theRasterizer->add_path(stroke);
- agg::render_scanlines(*theRasterizer, *slineP8, scanlines);
- unsigned strokeSize = scanlines.byte_size();
- agg::int8u* strokeCache = new agg::int8u[strokeSize]; // or any container
- scanlines.serialize(strokeCache);
-
- theRasterizer->reset_clipping();
-
-
- if (gc.cliprect==NULL) {
- rendererBase->reset_clipping(true);
- }
- else {
- int l = (int)(gc.cliprect[0]) ;
- int b = (int)(gc.cliprect[1]) ;
- int w = (int)(gc.cliprect[2]) ;
- int h = (int)(gc.cliprect[3]) ;
- rendererBase->clip_box(l, height-(b+h),l+w, height-b);
- }
-
-
- double thisx, thisy;
- for (size_t i=0; i<Nx; i++) {
- thisx = *(double *)(xa->data + i*xa->strides[0]);
- thisy = *(double *)(ya->data + i*ya->strides[0]);
-
- // MGDTODO
-// if (mpltransform->need_nonlinear_api())
-// try {
-// mpltransform->nonlinear_only_api(&thisx, &thisy);
-// }
-// catch(...) {
-// continue;
-// }
+ theRasterizer->reset_clipping();
+ if (gc.cliprect==NULL) {
+ rendererBase->reset_clipping(true);
+ }
+ else {
+ int l = (int)(gc.cliprect[0]) ;
+ int b = (int)(gc.cliprect[1]) ;
+ int w = (int)(gc.cliprect[2]) ;
+ int h = (int)(gc.cliprect[3]) ;
+ rendererBase->clip_box(l, height-(b+h),l+w, height-b);
+ }
- xytrans.transform(&thisx, &thisy);
-
- thisy = heightd - thisy; //flipy
-
- thisx = (int)thisx + 0.5;
- thisy = (int)thisy + 0.5;
- if (thisx<0) continue;
- if (thisy<0) continue;
- if (thisx>width) continue;
- if (thisy>height) continue;
-
+ size_t next_vertex_stride = vertices->strides[0];
+ size_t next_axis_stride = vertices->strides[1];
+ size_t code_stride = codes->strides[0];
+
+ const char* vertex_i = vertices->data;
+ const char* code_i = codes->data;
+ const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]);
+
+ size_t N = codes->dimensions[0];
+ double x, y;
+
agg::serialized_scanlines_adaptor_aa8 sa;
agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl;
-
- if (face.first) {
- //render the fill
- sa.init(fillCache, fillSize, thisx, thisy);
- rendererAA->color(face.second);
- agg::render_scanlines(sa, sl, *rendererAA);
+
+ for (size_t i=0; i < N; i++) {
+ size_t num_vertices = NUM_VERTICES[(int)(*code_i)];
+ if (num_vertices) {
+ for (size_t j=0; j<num_vertices; ++j)
+ GET_NEXT_VERTEX(x, y);
+ trans.transform(&x, &y);
+
+ if (face.first) {
+ //render the fill
+ sa.init(fillCache, fillSize, x, y);
+ rendererAA->color(face.second);
+ agg::render_scanlines(sa, sl, *rendererAA);
+ }
+
+ //render the stroke
+ sa.init(strokeCache, strokeSize, x, y);
+ rendererAA->color(gc.color);
+ agg::render_scanlines(sa, sl, *rendererAA);
+ }
+ code_i += code_stride;
}
-
- //render the stroke
- sa.init(strokeCache, strokeSize, thisx, thisy);
- rendererAA->color(gc.color);
- agg::render_scanlines(sa, sl, *rendererAA);
-
- } //for each marker
+ } catch(...) {
+ Py_XDECREF(vertices);
+ Py_XDECREF(codes);
+ delete[] fillCache;
+ delete[] strokeCache;
+ }
- Py_XDECREF(xa);
- Py_XDECREF(ya);
-
- if (face.first)
- delete [] fillCache;
+ Py_XDECREF(vertices);
+ Py_XDECREF(codes);
+ delete [] fillCache;
delete [] strokeCache;
- //jdh
- _VERBOSE("RendererAgg::_draw_markers_cache done");
return Py::Object();
}
@@ -1552,21 +1554,6 @@
}
-inline void get_next_vertex(const char* & vertex_i, const char* vertex_end,
- double& x, double& y,
- size_t next_vertex_stride,
- size_t next_axis_stride) {
- if (vertex_i + next_axis_stride >= vertex_end)
- throw Py::ValueError("Error parsing path. Read past end of vertices");
- x = *(double*)vertex_i;
- y = *(double*)(vertex_i + next_axis_stride);
- vertex_i += next_vertex_stride;
-}
-
-#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride)
-
-
-
Py::Object
RendererAgg::convert_to_native_path(const Py::Tuple& args) {
_VERBOSE("RendererAgg::draw_image");
@@ -2059,7 +2046,7 @@
add_varargs_method("draw_lines", &RendererAgg::draw_lines,
"draw_lines(gc, x, y,)\n");
add_varargs_method("draw_markers", &RendererAgg::draw_markers,
- "draw_markers(gc, path, x, y)\n");
+ "draw_markers(gc, marker_path, marker_trans, vertices, codes, rgbFace)\n");
add_varargs_method("draw_text_image", &RendererAgg::draw_text_image,
"draw_text_image(font_image, x, y, r, g, b, a)\n");
add_varargs_method("draw_image", &RendererAgg::draw_image,
Modified: branches/transforms/src/_backend_agg.h
===================================================================
--- branches/transforms/src/_backend_agg.h 2007-09-15 04:01:56 UTC (rev 3853)
+++ branches/transforms/src/_backend_agg.h 2007-09-17 13:41:38 UTC (rev 3854)
@@ -45,8 +45,10 @@
#define LINETO 2
#define CURVE3 3
#define CURVE4 4
-#define CLOSEPOLY 0x0F
+#define CLOSEPOLY 5
+const size_t NUM_VERTICES[] = { 0, 1, 1, 2, 3, 0 };
+
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <jo...@us...> - 2007-09-15 04:01:58
|
Revision: 3853
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3853&view=rev
Author: jouni
Date: 2007-09-14 21:01:56 -0700 (Fri, 14 Sep 2007)
Log Message:
-----------
Bugfix and doc fixes in type1font.py
Modified Paths:
--------------
trunk/matplotlib/lib/matplotlib/type1font.py
Modified: trunk/matplotlib/lib/matplotlib/type1font.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/type1font.py 2007-09-14 17:57:52 UTC (rev 3852)
+++ trunk/matplotlib/lib/matplotlib/type1font.py 2007-09-15 04:01:56 UTC (rev 3853)
@@ -1,15 +1,15 @@
"""
A class representing a Type 1 font.
-This version merely allows reading in pfa and pfb files, stores the
-data in pfa format, and allows reading the parts of the data in a
-format suitable for embedding in pdf files. A more complete class
-might support subsetting.
+This version merely reads pfa and pfb files and splits them for
+embedding in pdf files. There is no support yet for subsetting or
+anything like that.
-Usage: font = Type1Font(filename)
- somefile.write(font.data) # writes out font in pfa format
- len1, len2, len3 = font.lengths() # needed for pdf embedding
+Usage (subject to change):
+ font = Type1Font(filename)
+ clear_part, encrypted_part, finale = font.parts
+
Source: Adobe Technical Note #5040, Supporting Downloadable PostScript
Language Fonts.
@@ -32,8 +32,7 @@
def _read(self, file):
rawdata = file.read()
if not rawdata.startswith(chr(128)):
- self.data = rawdata
- return
+ return rawdata
data = ''
while len(rawdata) > 0:
@@ -101,4 +100,6 @@
if __name__ == '__main__':
import sys
font = Type1Font(sys.argv[1])
- sys.stdout.write(font.data)
+ parts = font.parts
+ print len(parts[0]), len(parts[1]), len(parts[2])
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-14 18:01:00
|
Revision: 3852
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3852&view=rev
Author: mdboom
Date: 2007-09-14 10:57:52 -0700 (Fri, 14 Sep 2007)
Log Message:
-----------
Sends paths to backend only once, and after that uses the "native" path by
reference with a changing transform. Started recongfiguring
patches.py to use only Paths under the hood (to take advantage of this
caching). Removed many methods from backend_agg that should
eventually be replaced by draw_path, at least in theory.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/patches.py
branches/transforms/lib/matplotlib/transforms.py
branches/transforms/src/_backend_agg.cpp
branches/transforms/src/_backend_agg.h
Added Paths:
-----------
branches/transforms/lib/matplotlib/path.py
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-14 17:57:52 UTC (rev 3852)
@@ -4,7 +4,7 @@
"""
from __future__ import division
-import os, sys, warnings, copy
+import os, sys, warnings, copy, weakref
import numpy as npy
import matplotlib.numerix.npyma as ma
@@ -17,7 +17,10 @@
class RendererBase:
"""An abstract base class to handle drawing/rendering operations
"""
-
+ # This will cache paths across rendering instances
+ # Each subclass of RenderBase should define this -->
+ # _paths = weakref.WeakKeyDictionary()
+
def __init__(self):
self._texmanager = None
@@ -33,7 +36,35 @@
"""
pass
+ def draw_path(self, gc, path, transform, rgbFace = None):
+ """
+ Handles the caching of the native path associated with the
+ given path and calls the underlying backend's _draw_path to
+ actually do the drawing.
+ """
+ native_path = self._native_paths.get(path)
+ if native_path is None:
+ import matplotlib.patches
+ print "CACHE MISS", path
+ native_path = self.convert_to_native_path(path)
+ self._native_paths[path] = native_path
+ self._draw_path(gc, native_path, transform, rgbFace)
+ def _draw_path(self, gc, native_path, transform, rgbFace):
+ """
+ Draw the native path object with the given GraphicsContext and
+ transform. The transform passed in will always be affine.
+ """
+ raise NotImplementedError
+
+ def convert_to_native_path(self, path):
+ """
+ Backends will normally will override this, but if they don't need any
+ special optimizations, they can just have the generic path data
+ passed to them in draw_path.
+ """
+ return path
+
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
rotation):
"""
@@ -75,6 +106,9 @@
"""
return False
+ def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
+ pass
+
def _draw_markers(self, bgc, path, rgbFace, x, y, trans):
"""
This method is currently underscore hidden because the
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 17:57:52 UTC (rev 3852)
@@ -69,7 +69,7 @@
"""
from __future__ import division
-import os, sys
+import os, sys, weakref
import numpy as npy
@@ -95,7 +95,15 @@
The renderer handles all the drawing primitives using a graphics
context instance that controls the colors/styles
"""
-
+ # MGDTODO: Renderers seem to get created and destroyed fairly
+ # often so the paths are cached at the class (not instance) level.
+ # However, this dictionary is only directly used by RendererBase,
+ # so it seems funny to define it here. However, if we didn't, the
+ # native paths would be shared across renderers, which is
+ # obviously bad. Seems like a good use of metaclasses, but that
+ # also seems like a heavy solution for a minor problem.
+ _native_paths = weakref.WeakKeyDictionary()
+
debug=1
texd = {} # a cache of tex image rasters
def __init__(self, width, height, dpi):
@@ -129,6 +137,12 @@
if __debug__: verbose.report('RendererAgg.__init__ done',
'debug-annoying')
+ def convert_to_native_path(self, path):
+ return self._renderer.convert_to_native_path(path.vertices, path.codes)
+
+ def _draw_path(self, gc, native_path, transform, rgbFace):
+ return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace)
+
def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation):
"""
Draw an arc centered at x,y with width and height and angles
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-14 17:57:52 UTC (rev 3852)
@@ -10,14 +10,14 @@
import numpy as npy
-import agg
import numerix.ma as ma
from matplotlib import verbose
import artist
from artist import Artist, setp
from cbook import iterable, is_string_like, is_numlike
from colors import colorConverter
-from transforms import Bbox
+from path import Path
+from transforms import Affine2D, Bbox
from matplotlib import rcParams
@@ -284,9 +284,6 @@
self.set_data(xdata, ydata)
self._logcache = None
- # TODO: do we really need 'newstyle'
- self._newstyle = False
-
def contains(self, mouseevent):
"""Test whether the mouse event occurred on the line. The pick radius determines
the precision of the location test (usually within five points of the value). Use
@@ -427,6 +424,7 @@
if len(x) != len(y):
raise RuntimeError('xdata and ydata must be the same length')
+ # MGDTODO: Deal with segments
mx = ma.getmask(x)
my = ma.getmask(y)
mask = ma.mask_or(mx, my)
@@ -439,7 +437,9 @@
self._x = npy.asarray(x, float)
self._y = npy.asarray(y, float)
-
+ self._path = Path(npy.vstack((self._x, self._y)).transpose(),
+ closed=False)
+
self._logcache = None
@@ -508,30 +508,19 @@
gc.set_joinstyle(join)
gc.set_capstyle(cap)
- if self._newstyle:
- # transform in backend
- xt = self._x
- yt = self._y
- else:
- x, y = self._get_plottable()
- if len(x)==0: return
- xt, yt = self.get_transform().numerix_x_y(x, y)
-
-
-
funcname = self._lineStyles.get(self._linestyle, '_draw_nothing')
lineFunc = getattr(self, funcname)
+ # MGDTODO: Deal with self._segments
if self._segments is not None:
for ii in self._segments:
lineFunc(renderer, gc, xt[ii[0]:ii[1]], yt[ii[0]:ii[1]])
else:
- lineFunc(renderer, gc, xt, yt)
-
-
- if self._marker is not None:
-
+ lineFunc(renderer, gc, self._path)
+
+ # MGDTODO: Deal with markers
+ if self._marker is not None and False:
gc = renderer.new_gc()
self._set_gc_clip(gc)
gc.set_foreground(self.get_markeredgecolor())
@@ -539,7 +528,7 @@
gc.set_alpha(self._alpha)
funcname = self._markers.get(self._marker, '_draw_nothing')
markerFunc = getattr(self, funcname)
- markerFunc(renderer, gc, xt, yt)
+ markerFunc(renderer, gc, self._path)
#renderer.close_group('line2d')
@@ -720,7 +709,7 @@
self.set_linestyle('--')
self._dashSeq = seq # TODO: offset ignored for now
- def _draw_nothing(self, renderer, gc, xt, yt):
+ def _draw_nothing(self, renderer, gc, path):
pass
def _draw_steps(self, renderer, gc, xt, yt):
@@ -737,13 +726,10 @@
else:
renderer.draw_lines(gc, xt2, yt2)
- def _draw_solid(self, renderer, gc, xt, yt):
- if len(xt)<2: return
+ def _draw_solid(self, renderer, gc, path):
+ # if len(xt)<2: return
gc.set_linestyle('solid')
- if self._newstyle:
- renderer.draw_lines(gc, xt, yt, self.get_transform())
- else:
- renderer.draw_lines(gc, xt, yt)
+ renderer.draw_path(gc, path, self.get_transform())
def _draw_dashed(self, renderer, gc, xt, yt):
@@ -1103,16 +1089,12 @@
for (x,y) in zip(xt, yt):
renderer.draw_line(gc, x, y, x+offset, y)
+ _tickup_path = Path([[-0.5, 0.0], [-0.5, 1.0]])
def _draw_tickup(self, renderer, gc, xt, yt):
offset = renderer.points_to_pixels(self._markersize)
- if self._newstyle:
- path = agg.path_storage()
- path.move_to(-0.5, 0)
- path.line_to(-0.5, offset)
- renderer.draw_markers(gc, path, None, xt, yt, self.get_transform())
- else:
- for (x,y) in zip(xt, yt):
- renderer.draw_line(gc, x, y, x, y+offset)
+ marker_transform = Affine2D().scale(1.0, offset)
+ renderer.draw_markers(gc, self._tickup_path, marker_transform,
+ self._path, self.get_transform())
def _draw_tickdown(self, renderer, gc, xt, yt):
offset = renderer.points_to_pixels(self._markersize)
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/lib/matplotlib/patches.py 2007-09-14 17:57:52 UTC (rev 3852)
@@ -11,8 +11,8 @@
import matplotlib.nxutils as nxutils
import matplotlib.mlab as mlab
import matplotlib.artist as artist
+from matplotlib.path import Path
-
# these are not available for the object inspector until after the
# class is build so we define an initial set here for the init
# function and they will be overridden after object defn
@@ -73,7 +73,7 @@
self._antialiased = antialiased
self._hatch = hatch
self.fill = fill
-
+
if len(kwargs): artist.setp(self, **kwargs)
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
@@ -84,8 +84,10 @@
Returns T/F, {}
"""
- if callable(self._contains): return self._contains(self,mouseevent)
+ # MGDTODO: This will probably need to be implemented in C++
+ if callable(self._contains): return self._contains(self,mouseevent)
+
try:
# TODO: make this consistent with patch collection algorithm
x, y = self.get_transform().inverse_xy_tup((mouseevent.x, mouseevent.y))
@@ -107,7 +109,6 @@
self.set_figure(other.get_figure())
self.set_alpha(other.get_alpha())
-
def get_antialiased(self):
return self._antialiased
@@ -210,22 +211,16 @@
if self._hatch:
gc.set_hatch(self._hatch )
- verts = self.get_verts()
- tverts = self.get_transform()(verts)
+ path = self.get_path()
+ transform = self.get_transform()
- # MGDTODO: This result is an Nx2 numpy array, which could be passed
- # directly to renderer.draw_polygon since it currently expects
- # a list of tuples so we're converting it to that now.
- tverts = [tuple(x) for x in tverts]
-
- renderer.draw_polygon(gc, rgbFace, tverts)
+ renderer.draw_path(gc, path, transform, rgbFace)
-
#renderer.close_group('patch')
- def get_verts(self):
+ def get_path(self):
"""
- Return the vertices of the patch
+ Return the path of this patch
"""
raise NotImplementedError('Derived must override')
@@ -286,9 +281,10 @@
%(Patch)s
"""
Patch.__init__(self)
- self.ox, self.oy = ox, oy
self.patch = patch
self.props = props
+ self.ox, self.oy = ox, oy
+ self._shadow_transform = transforms.Affine2D.translate(self.ox, self.oy)
self._update()
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
@@ -306,18 +302,13 @@
self.set_facecolor((r,g,b))
self.set_edgecolor((r,g,b))
+
+ def get_path(self):
+ return self.patch.get_path()
- def get_verts(self):
- verts = self.patch.get_verts()
- xs = self.convert_xunits([x+self.ox for x,y in verts])
- ys = self.convert_yunits([y+self.oy for x,y in verts])
- return zip(xs, ys)
-
- def _draw(self, renderer):
- 'draw the shadow'
- self._update()
- Patch.draw(self, renderer)
-
+ def get_transform(self):
+ return self._transform + self._shadow_transform
+
class Rectangle(Patch):
"""
Draw a rectangle with lower left at xy=(x,y) with specified
@@ -325,12 +316,16 @@
"""
+ _path = Path(
+ [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
+
def __str__(self):
return str(self.__class__).split('.')[-1] \
+ "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height)
- def __init__(self, xy, width, height,
- **kwargs):
+ # MGDTODO: Perhaps pass in a Bbox here instead, then the updates will
+ # happen automatically (without needing to call set_x etc.
+ def __init__(self, xy, width, height, **kwargs):
"""
xy is an x,y tuple lower, left
@@ -344,38 +339,41 @@
Patch.__init__(self, **kwargs)
- self.xy = list(xy)
- self.width, self.height = width, height
+ self._bbox = transforms.Bbox.from_lbwh(xy[0], xy[1], width, height)
+ self._rect_transform = transforms.BboxTransform(
+ transforms.Bbox.unit(), self._bbox)
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
-
- def get_verts(self):
+ def get_path(self):
"""
Return the vertices of the rectangle
"""
- x, y = self.xy
- left, right = self.convert_xunits((x, x + self.width))
- bottom, top = self.convert_yunits((y, y + self.height))
+ # This is a "class-static" variable, so all rectangles in the plot
+ # will be shared (and merely have different transforms)
+ return self._path
- return npy.array([[left, bottom], [left, top],
- [right, top], [right, bottom]],
- npy.float_)
+ # MGDTODO: Convert units
+# left, right = self.convert_xunits((x, x + self.width))
+# bottom, top = self.convert_yunits((y, y + self.height))
+ def get_transform(self):
+ return self._rect_transform + self._transform
+
def get_x(self):
"Return the left coord of the rectangle"
- return self.xy[0]
+ return self._bbox.xmin
def get_y(self):
"Return the bottom coord of the rectangle"
- return self.xy[1]
+ return self._bbox.ymin
def get_width(self):
"Return the width of the rectangle"
- return self.width
+ return self._bbox.width
def get_height(self):
"Return the height of the rectangle"
- return self.height
+ return self._bbox.height
def set_x(self, x):
"""
@@ -383,7 +381,7 @@
ACCEPTS: float
"""
- self.xy[0] = x
+ self._bbox.xmin = x
def set_y(self, y):
"""
@@ -391,7 +389,7 @@
ACCEPTS: float
"""
- self.xy[1] = y
+ self._bbox.ymin = y
def set_width(self, w):
"""
@@ -399,7 +397,7 @@
ACCEPTS: float
"""
- self.width = w
+ self._bbox.width = w
def set_height(self, h):
"""
@@ -407,7 +405,7 @@
ACCEPTS: float
"""
- self.height = h
+ self._bbox.height = h
def set_bounds(self, *args):
"""
@@ -419,15 +417,15 @@
l,b,w,h = args[0]
else:
l,b,w,h = args
- self.xy = [l,b]
- self.width = w
- self.height = h
+ self._bbox.bounds = l,b,w,h
class RegularPolygon(Patch):
"""
A regular polygon patch.
"""
+ _polygon_cache = {}
+
def __str__(self):
return "Poly%d(%g,%g)"%(self.numVertices,self.xy[0],self.xy[1])
@@ -444,32 +442,27 @@
"""
Patch.__init__(self, **kwargs)
- self.xy = list(xy)
- self.numVertices = numVertices
- self.radius = radius
- self.orientation = orientation
+ path = self._polygon_cache[numVertices]
+ if path is None:
+ theta = 2*npy.pi/numVertices * npy.arange(numVertices)
+ verts = npy.hstack((npy.cos(theta), npy.sin(theta)))
+ path = Path(verts)
+ self._polygon_cache[numVertices] = path
+ self._path = path
+ self._poly_transform = transforms.Affine2D() \
+ .scale(radius) \
+ .rotate(orientation) \
+ .translate(*xy)
+
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
+ def get_path(self):
+ return self._path
-
- def get_verts(self):
- theta = 2*npy.pi/self.numVertices*npy.arange(self.numVertices) + \
- self.orientation
- r = float(self.radius)
- x, y = map(float, self.xy)
-
- xs = x + r*npy.cos(theta)
- ys = y + r*npy.sin(theta)
-
- #xs = self.convert_xunits(xs)
- #ys = self.convert_yunits(ys)
-
-
- self.verts = zip(xs, ys)
-
- return self.verts
-
+ def get_transform(self):
+ return self._poly_transform + self._transform
+
class Polygon(Patch):
"""
A general polygon patch.
@@ -485,22 +478,20 @@
%(Patch)s
See Patch documentation for additional kwargs
"""
-
+ # MGDTODO: This should encourage the use of numpy arrays of shape Nx2
Patch.__init__(self, **kwargs)
if not isinstance(xy, list):
xy = list(xy)
- self.xy = xy
+ self._path = Path(xy, closed=False)
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
-
-
def get_verts(self):
- xs, ys = zip(*self.xy)[:2]
+ return self._path
+
+ # MGDTODO: Convert units
xs = self.convert_xunits(xs)
ys = self.convert_yunits(ys)
- return zip(xs, ys)
-
class Wedge(Polygon):
def __str__(self):
return "Wedge(%g,%g)"%self.xy[0]
@@ -516,6 +507,7 @@
%(Patch)s
"""
+ # MGDTODO: Implement me
xc, yc = center
rads = (math.pi/180.)*npy.arange(theta1, theta2+0.1*dtheta, dtheta)
xs = r*npy.cos(rads)+xc
@@ -543,6 +535,7 @@
Valid kwargs are:
%(Patch)s
"""
+ # MGDTODO: Implement me
arrow = npy.array( [
[ 0.0, 0.1 ], [ 0.0, -0.1],
[ 0.8, -0.1 ], [ 0.8, -0.3],
@@ -586,6 +579,7 @@
%(Patch)s
"""
+ # MGDTODO: Implement me
if head_width is None:
head_width = 3 * width
if head_length is None:
@@ -659,6 +653,7 @@
%(Patch)s
"""
+ # MGDTODO: Implement me
self.dpi = dpi
self.xytip = xytip
self.xybase = xybase
@@ -731,6 +726,7 @@
%(Patch)s
"""
+ # MGDTODO: Implement me
self.center = xy
self.radius = radius
RegularPolygon.__init__(self, xy,
@@ -767,6 +763,7 @@
Valid kwargs are:
%(Patch)s
"""
+ # MGDTODO: Implement me
Patch.__init__(self, **kwargs)
# self.center = npy.array(xy, npy.float)
@@ -833,6 +830,7 @@
%(Patch)s
"""
+ # MGDTODO: Implement me
if kwargs.has_key('resolution'):
import warnings
warnings.warn('Circle is now scale free. Use CirclePolygon instead!', DeprecationWarning)
Added: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py (rev 0)
+++ branches/transforms/lib/matplotlib/path.py 2007-09-14 17:57:52 UTC (rev 3852)
@@ -0,0 +1,50 @@
+import numpy as npy
+
+class Path:
+ # Path codes
+ STOP = 0
+ MOVETO = 1 # 1 vertex
+ LINETO = 2 # 1 vertex
+ CURVE3 = 3 # 2 vertices
+ CURVE4 = 4 # 3 vertices
+ ###
+ # MGDTODO: I'm not sure these are supported by PS/PDF/SVG,
+ # so if they don't, we probably shouldn't
+ CURVEN = 5
+ CATROM = 6
+ UBSPLINE = 7
+ ####
+ CLOSEPOLY = 0x0F # 0 vertices
+
+ code_type = npy.uint8
+
+ def __init__(self, vertices, codes=None, closed=True):
+ self._vertices = npy.asarray(vertices, npy.float_)
+ assert self._vertices.ndim == 2
+ assert self._vertices.shape[1] == 2
+
+ if codes is None:
+ if closed:
+ codes = self.LINETO * npy.ones(
+ self._vertices.shape[0] + 1, self.code_type)
+ codes[0] = self.MOVETO
+ codes[-1] = self.CLOSEPOLY
+ else:
+ codes = self.LINETO * npy.ones(
+ self._vertices.shape[0], self.code_type)
+ codes[0] = self.MOVETO
+ else:
+ codes = npy.asarray(codes, self.code_type)
+ self._codes = codes
+
+ assert self._codes.ndim == 1
+ # MGDTODO: Maybe we should add some quick-and-dirty check that
+ # the number of vertices is correct for the code array
+
+ def _get_codes(self):
+ return self._codes
+ codes = property(_get_codes)
+
+ def _get_vertices(self):
+ return self._vertices
+ vertices = property(_get_vertices)
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-14 17:57:52 UTC (rev 3852)
@@ -403,6 +403,9 @@
Transform.__init__(self)
self._inverted = None
+ def __array__(self):
+ return self.get_matrix()
+
def _do_invalidation(self):
result = self._inverted is None
self._inverted = None
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-14 17:57:52 UTC (rev 3852)
@@ -341,6 +341,7 @@
}
+// MGDTODO: Remove this method (it has been conglomerated into draw_path
template <class VS>
void
RendererAgg::_fill_and_stroke(VS& path,
@@ -1399,68 +1400,6 @@
}
-
-
-// Py::Object
-// RendererAgg::draw_path(const Py::Tuple& args) {
-// //draw_path(gc, rgbFace, path, transform)
-// theRasterizer->reset_clipping();
-
-// _VERBOSE("RendererAgg::draw_path");
-// args.verify_length(4);
-
-// GCAgg gc = GCAgg(args[0], dpi);
-// facepair_t face = _get_rgba_face(args[1], gc.alpha);
-
-// agg::path_storage *path;
-// swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *");
-// assert(descr);
-// if (SWIG_ConvertPtr(args[2].ptr(),(void **)(&path), descr, 0) == -1)
-// throw Py::TypeError("Could not convert path_storage");
-
-
-// Transformation* mpltransform = static_cast<Transformation*>(args[3].ptr());
-
-// double a, b, c, d, tx, ty;
-// try {
-// mpltransform->affine_params_api(&a, &b, &c, &d, &tx, &ty);
-// }
-// catch(...) {
-// throw Py::ValueError("Domain error on affine_params_api in RendererAgg::draw_path");
-// }
-
-// agg::trans_affine xytrans = agg::trans_affine(a,b,c,d,tx,ty);
-
-// double heightd = double(height);
-// agg::path_storage tpath; // the mpl transformed path
-// bool needNonlinear = mpltransform->need_nonlinear_api();
-// size_t Nx = path->total_vertices();
-// double x, y;
-// unsigned cmd;
-// bool curvy = false;
-// for (size_t i=0; i<Nx; i++) {
-// cmd = path->vertex(i, &x, &y);
-// if (cmd==agg::path_cmd_curve3 || cmd==agg::path_cmd_curve4) curvy=true;
-// if (needNonlinear)
-// try {
-// mpltransform->nonlinear_only_api(&x, &y);
-// }
-// catch (...) {
-// throw Py::ValueError("Domain error on nonlinear_only_api in RendererAgg::draw_path");
-
-// }
-
-// //use agg's transformer?
-// xytrans.transform(&x, &y);
-// y = heightd - y; //flipy
-// tpath.add_vertex(x,y,cmd);
-// }
-
-// _fill_and_stroke(tpath, gc, face, curvy);
-// return Py::Object();
-
-// }
-
/**
* This is a custom span generator that converts spans in the
* 8-bit inverted greyscale font buffer to rgba that agg can use.
@@ -1613,8 +1552,172 @@
}
+inline void get_next_vertex(const char* & vertex_i, const char* vertex_end,
+ double& x, double& y,
+ size_t next_vertex_stride,
+ size_t next_axis_stride) {
+ if (vertex_i + next_axis_stride >= vertex_end)
+ throw Py::ValueError("Error parsing path. Read past end of vertices");
+ x = *(double*)vertex_i;
+ y = *(double*)(vertex_i + next_axis_stride);
+ vertex_i += next_vertex_stride;
+}
+#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride)
+
+
+
Py::Object
+RendererAgg::convert_to_native_path(const Py::Tuple& args) {
+ _VERBOSE("RendererAgg::draw_image");
+ args.verify_length(2);
+
+ Py::Object vertices_obj = args[0];
+ Py::Object codes_obj = args[1];
+
+ PyArrayObject* vertices = NULL;
+ PyArrayObject* codes = NULL;
+ PathAgg* path = NULL;
+
+ try {
+ vertices = (PyArrayObject*)PyArray_ContiguousFromObject
+ (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2);
+ if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2)
+ throw Py::ValueError("Invalid vertices array.");
+ codes = (PyArrayObject*)PyArray_ContiguousFromObject
+ (codes_obj.ptr(), PyArray_UINT8, 1, 1);
+ if (!codes)
+ throw Py::ValueError("Invalid codes array.");
+
+ path = new PathAgg();
+
+ size_t next_vertex_stride = vertices->strides[0];
+ size_t next_axis_stride = vertices->strides[1];
+ size_t code_stride = codes->strides[0];
+
+ const char* vertex_i = vertices->data;
+ const char* code_i = codes->data;
+ const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]);
+
+ size_t N = codes->dimensions[0];
+ double x0, y0, x1, y1, x2, y2;
+
+ for (size_t i = 0; i < N; ++i) {
+ switch (*(unsigned char*)(code_i)) {
+ case MOVETO:
+ GET_NEXT_VERTEX(x0, y0);
+ path->move_to(x0, y0);
+ _VERBOSE("MOVETO");
+ break;
+ case LINETO:
+ GET_NEXT_VERTEX(x0, y0);
+ path->line_to(x0, y0);
+ _VERBOSE("LINETO");
+ break;
+ case CURVE3:
+ GET_NEXT_VERTEX(x0, y0);
+ GET_NEXT_VERTEX(x1, y1);
+ path->curve3(x0, y0, x1, y1);
+ path->curvy = true;
+ _VERBOSE("CURVE3");
+ break;
+ case CURVE4:
+ GET_NEXT_VERTEX(x0, y0);
+ GET_NEXT_VERTEX(x1, y1);
+ GET_NEXT_VERTEX(x2, y2);
+ path->curve4(x0, y0, x1, y1, x2, y2);
+ path->curvy = true;
+ _VERBOSE("CURVE4");
+ break;
+ case CLOSEPOLY:
+ path->close_polygon();
+ _VERBOSE("CLOSEPOLY");
+ break;
+ }
+ code_i += code_stride;
+ }
+ } catch(...) {
+ Py_XDECREF(vertices);
+ Py_XDECREF(codes);
+ delete path;
+ throw;
+ }
+
+ Py_XDECREF(vertices);
+ Py_XDECREF(codes);
+
+ return Py::asObject(path);
+}
+
+Py::Object
+RendererAgg::draw_path(const Py::Tuple& args) {
+ typedef agg::conv_transform<agg::path_storage> transformed_path_t;
+ typedef agg::conv_curve<transformed_path_t> curve_t;
+ typedef agg::conv_stroke<curve_t> stroke_t;
+ typedef agg::conv_dash<curve_t> dash_t;
+ typedef agg::conv_stroke<dash_t> stroke_dash_t;
+ //draw_path(gc, rgbFace, path, transform)
+ theRasterizer->reset_clipping();
+
+ _VERBOSE("RendererAgg::draw_path");
+ args.verify_length(4);
+
+ GCAgg gc = GCAgg(args[0], dpi);
+ Py::Object path_obj = args[1];
+ if (!PathAgg::check(path_obj))
+ throw Py::TypeError("Native path object is not of correct type");
+ PathAgg* path = static_cast<PathAgg*>(path_obj.ptr());
+ agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[2]);
+ facepair_t face = _get_rgba_face(args[3], gc.alpha);
+
+ trans *= agg::trans_affine_scaling(1.0, -1.0);
+ trans *= agg::trans_affine_translation(0.0, (double)height);
+
+ transformed_path_t tpath(*path, trans);
+ // MGDTODO: See if there is any advantage to only curving if necessary
+ curve_t curve(tpath);
+
+ set_clipbox_rasterizer(gc.cliprect);
+
+ if (face.first) {
+ rendererAA->color(face.second);
+ theRasterizer->add_path(curve);
+ agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
+ }
+
+ if (gc.linewidth) {
+ if (gc.dasha == NULL) {
+ stroke_t stroke(curve);
+ stroke.width(gc.linewidth);
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ theRasterizer->add_path(stroke);
+ } else {
+ dash_t dash(curve);
+ for (size_t i = 0; i < (gc.Ndash / 2); ++i)
+ dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]);
+ stroke_dash_t stroke(dash);
+ stroke.line_cap(gc.cap);
+ stroke.line_join(gc.join);
+ stroke.width(gc.linewidth);
+ theRasterizer->add_path(stroke); //boyle freeze is herre
+ }
+
+ if ( gc.isaa ) {
+ rendererAA->color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA);
+ }
+ else {
+ rendererBin->color(gc.color);
+ agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin);
+ }
+ }
+
+ return Py::Object();
+}
+
+
+Py::Object
RendererAgg::write_rgba(const Py::Tuple& args) {
_VERBOSE("RendererAgg::write_rgba");
@@ -1949,6 +2052,10 @@
"draw_ellipse(gc, rgbFace, x, y, w, h)\n");
add_varargs_method("draw_polygon", &RendererAgg::draw_polygon,
"draw_polygon(gc, rgbFace, points)\n");
+ add_varargs_method("draw_path", &RendererAgg::draw_path,
+ "draw_path(gc, rgbFace, native_path, transform)\n");
+ add_varargs_method("convert_to_native_path", &RendererAgg::convert_to_native_path,
+ "convert_to_native_path(vertices, codes)\n");
add_varargs_method("draw_lines", &RendererAgg::draw_lines,
"draw_lines(gc, x, y,)\n");
add_varargs_method("draw_markers", &RendererAgg::draw_markers,
@@ -1976,10 +2083,13 @@
add_varargs_method("restore_region", &RendererAgg::restore_region,
"restore_region(region)");
-
-
}
+void PathAgg::init_type()
+{
+ behaviors().name("PathAgg");
+ behaviors().doc("A native Agg path object");
+}
extern "C"
DL_EXPORT(void)
Modified: branches/transforms/src/_backend_agg.h
===================================================================
--- branches/transforms/src/_backend_agg.h 2007-09-14 13:03:31 UTC (rev 3851)
+++ branches/transforms/src/_backend_agg.h 2007-09-14 17:57:52 UTC (rev 3852)
@@ -39,6 +39,14 @@
#include "agg_scanline_p.h"
#include "agg_vcgen_markers_term.h"
+// These are copied directly from path.py, and must be kept in sync
+#define STOP 0
+#define MOVETO 1
+#define LINETO 2
+#define CURVE3 3
+#define CURVE4 4
+#define CLOSEPOLY 0x0F
+
typedef agg::pixfmt_rgba32 pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa;
@@ -163,6 +171,8 @@
Py::Object draw_markers(const Py::Tuple & args);
Py::Object draw_text_image(const Py::Tuple & args);
Py::Object draw_image(const Py::Tuple & args);
+ Py::Object draw_path(const Py::Tuple & args);
+ Py::Object convert_to_native_path(const Py::Tuple & args);
Py::Object write_rgba(const Py::Tuple & args);
Py::Object write_png(const Py::Tuple & args);
@@ -229,7 +239,22 @@
agg::path_storage *lastclippath;
};
+// A completely opaque data type used only to pass native path
+// data to/from Python. Python can't do anything with the data
+// other than create and then use it.
+class PathAgg :
+ public agg::path_storage,
+ public Py::PythonExtension<PathAgg> {
+public:
+ PathAgg() : curvy(false) {}
+
+ static void init_type(void);
+
+ bool curvy;
+};
+
+
// the extension module
class _backend_agg_module : public Py::ExtensionModule<_backend_agg_module>
{
@@ -240,6 +265,7 @@
BufferRegion::init_type();
RendererAgg::init_type();
+ PathAgg::init_type();
add_keyword_method("RendererAgg", &_backend_agg_module::new_renderer,
"RendererAgg(width, height, dpi)");
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-14 13:03:33
|
Revision: 3851
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3851&view=rev
Author: mdboom
Date: 2007-09-14 06:03:31 -0700 (Fri, 14 Sep 2007)
Log Message:
-----------
Removed transforms on the C++ side -- removed many methods that depend
on it in backend_agg in preparation for path generalization.
Lots of general renaming...
Modified Paths:
--------------
branches/transforms/lib/matplotlib/artist.py
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/figure.py
branches/transforms/lib/matplotlib/finance.py
branches/transforms/lib/matplotlib/legend.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/table.py
branches/transforms/lib/matplotlib/text.py
branches/transforms/lib/matplotlib/ticker.py
branches/transforms/lib/matplotlib/widgets.py
branches/transforms/setup.py
branches/transforms/setupext.py
branches/transforms/src/_backend_agg.cpp
branches/transforms/src/_backend_agg.h
branches/transforms/src/_gtkagg.cpp
branches/transforms/src/_tkagg.cpp
Added Paths:
-----------
branches/transforms/lib/matplotlib/transforms.py
Removed Paths:
-------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/src/_transforms.cpp
branches/transforms/src/_transforms.h
Deleted: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -1,854 +0,0 @@
-"""
-A set of classes to handle transformations.
-
-2007 Michael Droettboom
-"""
-
-import numpy as npy
-from numpy.linalg import inv
-from sets import Set
-
-# MGDTODO: The name of this module is bad, since it deals with
-# non-affine transformations as well. It should probably just be
-# "transforms", but we already had one of those... ;)
-
-# MGDTODO: This creates a ton of cyclical references. We may want to
-# consider using weak references
-
-# MGDTODO: deep copying is probably incorrect wrt the parent/child
-# relationships
-
-class TransformNode(object):
- def __init__(self):
- self._parents = Set()
-
- def invalidate(self):
- if not self._do_invalidation():
- for parent in self._parents:
- parent.invalidate()
-
- def _do_invalidation(self):
- return False
-
- def set_children(self, children):
- for child in children:
- getattr(self, child)._parents.add(self)
- self._children = children
-
-# def replace_child(self, index, child):
-# children = self._children
-# getattr(self, children[index])._parents.remove(self)
-# setattr(self, children[index], child)
-# # We have to reset children in case two or more
-# # of the children are the same
-# for child in children:
-# getattr(self, child)._parents.add(self)
-# self.invalidate()
-
-class BboxBase(TransformNode):
- '''
- This is the read-only part of a bounding-box
- '''
-
- def __init__(self):
- TransformNode.__init__(self)
-
- def __array__(self):
- return self.get_points()
-
- # MGDTODO: Probably a more efficient ways to do this...
- def _get_xmin(self):
- return self.get_points()[0, 0]
- xmin = property(_get_xmin)
-
- def _get_ymin(self):
- return self.get_points()[0, 1]
- ymin = property(_get_ymin)
-
- def _get_xmax(self):
- return self.get_points()[1, 0]
- xmax = property(_get_xmax)
-
- def _get_ymax(self):
- return self.get_points()[1, 1]
- ymax = property(_get_ymax)
-
- def _get_min(self):
- return self.get_points()[0]
- min = property(_get_min)
-
- def _get_max(self):
- return self.get_points()[1]
- max = property(_get_max)
-
- def _get_intervalx(self):
- return self.get_points()[:, 0]
- intervalx = property(_get_intervalx)
-
- def _get_intervaly(self):
- return self.get_points()[:, 1]
- intervaly = property(_get_intervaly)
-
- def _get_width(self):
- return self.xmax - self.xmin
- width = property(_get_width)
-
- def _get_height(self):
- return self.ymax - self.ymin
- height = property(_get_height)
-
- def _get_bounds(self):
- return (self.xmin, self.ymin,
- self.xmax - self.xmin, self.ymax - self.ymin)
- bounds = property(_get_bounds)
-
- def get_points(self):
- return NotImplementedError()
-
- # MGDTODO: Optimize
- def containsx(self, x):
- return x >= self.xmin and x <= self.xmax
-
- def containsy(self, y):
- return y >= self.ymin and y <= self.ymax
-
- def contains(self, x, y):
- return self.containsx(x) and self.containsy(y)
-
- def overlapsx(self, other):
- return self.containsx(other.xmin) \
- or self.containsx(other.xmax)
-
- def overlapsy(self, other):
- return self.containsy(other.ymin) \
- or self.containsx(other.ymax)
-
- def overlaps(self, other):
- return self.overlapsx(other) \
- and self.overlapsy(other)
-
- def fully_containsx(self, x):
- return x > self.xmin and x < self.xmax
-
- def fully_containsy(self, y):
- return y > self.ymin and y < self.ymax
-
- def fully_contains(self, x, y):
- return self.fully_containsx(x) \
- and self.fully_containsy(y)
-
- def fully_overlapsx(self, other):
- return self.fully_containsx(other.xmin) \
- or self.fully_containsx(other.xmax)
-
- def fully_overlapsy(self, other):
- return self.fully_containsy(other.ymin) \
- or self.fully_containsx(other.ymax)
-
- def fully_overlaps(self, other):
- return self.fully_overlapsx(other) and \
- self.fully_overlapsy(other)
-
-
-class Bbox(BboxBase):
- def __init__(self, points):
- BboxBase.__init__(self)
- self._points = npy.asarray(points, npy.float_)
-
- #@staticmethod
- def unit():
- return Bbox.from_lbrt(0., 0., 1., 1.)
- unit = staticmethod(unit)
-
- #@staticmethod
- def from_lbwh(left, bottom, width, height):
- return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
- from_lbwh = staticmethod(from_lbwh)
-
- #@staticmethod
- def from_lbrt(*args):
- points = npy.array(args, dtype=npy.float_).reshape(2, 2)
- return Bbox(points)
- from_lbrt = staticmethod(from_lbrt)
-
- def __copy__(self):
- return Bbox(self._points.copy())
-
- def __deepcopy__(self, memo):
- return Bbox(self._points.copy())
-
- def __cmp__(self, other):
- # MGDTODO: Totally suboptimal
- if isinstance(other, Bbox) and (self._points == other._points).all():
- return 0
- return -1
-
- def __repr__(self):
- return 'Bbox(%s)' % repr(self._points)
- __str__ = __repr__
-
- # JDH: the update method will update the box limits from the
- # existing limits and the new data; it appears here you are just
- # using the new data. We use an "ignore" flag to specify whether
- # you want to include the existing data or not in the update
- def update_from_data(self, x, y, ignore=True):
- self._points = npy.array(
- [[x.min(), y.min()], [x.max(), y.max()]],
- npy.float_)
- self.invalidate()
-
- # MGDTODO: Probably a more efficient ways to do this...
- def _set_xmin(self, val):
- self._points[0, 0] = val
- self.invalidate()
- xmin = property(BboxBase._get_xmin, _set_xmin)
-
- def _set_ymin(self, val):
- self._points[0, 1] = val
- self.invalidate()
- ymin = property(BboxBase._get_ymin, _set_ymin)
-
- def _set_xmax(self, val):
- self._points[1, 0] = val
- self.invalidate()
- xmax = property(BboxBase._get_xmax, _set_xmax)
-
- def _set_ymax(self, val):
- self._points[1, 1] = val
- self.invalidate()
- ymax = property(BboxBase._get_ymax, _set_ymax)
-
- def _set_min(self, val):
- self._points[0] = val
- self.invalidate()
- min = property(BboxBase._get_min, _set_min)
-
- def _set_max(self, val):
- self._points[1] = val
- self.invalidate()
- max = property(BboxBase._get_max, _set_max)
-
- def _set_intervalx(self, interval):
- self._points[:, 0] = interval
- self.invalidate()
- intervalx = property(BboxBase._get_intervalx, _set_intervalx)
-
- def _set_intervaly(self, interval):
- self._points[:, 1] = interval
- self.invalidate()
- intervaly = property(BboxBase._get_intervaly, _set_intervaly)
-
- def _set_bounds(self, bounds):
- l,b,w,h = bounds
- self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
- self.invalidate()
- bounds = property(BboxBase._get_bounds, _set_bounds)
-
- def get_points(self):
- return self._points
-
- def set_points(self, points):
- self._points = points
- self.invalidate()
-
- def set(self, other):
- self._points = other.get_points()
- self.invalidate()
-
- def transformed(self, transform):
- return Bbox(transform(self._points))
-
- def inverse_transformed(self, transform):
- return Bbox(transform.inverted()(self._points))
-
- def expanded(self, sw, sh):
- width = self.width
- height = self.height
- deltaw = (sw * width - width) / 2.0
- deltah = (sh * height - height) / 2.0
- a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
- return Bbox(self._points + a)
-
- #@staticmethod
- def union(bboxes):
- """
- Return the Bbox that bounds all bboxes
- """
- assert(len(bboxes))
-
- if len(bboxes) == 1:
- return bboxes[0]
-
- bbox = bboxes[0]
- xmin = bbox.xmin
- ymin = bbox.ymin
- xmax = bbox.xmax
- ymax = bbox.ymax
-
- for bbox in bboxes[1:]:
- xmin = min(xmin, bbox.xmin)
- ymin = min(ymin, bbox.ymin)
- xmax = max(xmax, bbox.xmax)
- ymax = max(ymax, bbox.ymax)
-
- return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
- union = staticmethod(union)
-
-class TransformedBbox(BboxBase):
- def __init__(self, bbox, transform):
- assert isinstance(bbox, Bbox)
- assert isinstance(transform, Transform)
-
- BboxBase.__init__(self)
- self.bbox = bbox
- self.transform = transform
- self.set_children(['bbox', 'transform'])
- self._points = None
-
- def __repr__(self):
- return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
- __str__ = __repr__
-
- def _do_invalidation(self):
- self._points = None
-
- def get_points(self):
- if self._points is None:
- self._points = self.transform(self.bbox.get_points())
- return self._points
-
-# MGDTODO: This code probably works, but I don't think it's a good idea
-# (from a code clarity perspective)
-# class BlendedBbox(BboxBase):
-# def __init__(self, bbox_x, bbox_y):
-# assert isinstance(bbox_x, BboxBase)
-# assert isinstance(bbox_y, BboxBase)
-
-# BboxBase.__init__(self)
-# self._x = bbox_x
-# self._y = bbox_y
-# self.set_children(['_x', '_y'])
-# self._points = None
-
-# def __repr__(self):
-# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
-# __str__ = __repr__
-
-# def _do_invalidation(self):
-# self._points = None
-
-# def get_points(self):
-# if self._points is None:
-# # MGDTODO: Optimize
-# if self._x == self._y:
-# self._points = self._x.get_points()
-# else:
-# x_points = self._x.get_points()
-# y_points = self._y.get_points()
-# self._points = npy.array(
-# [[x_points[0,0], y_points[0,1]],
-# [x_points[1,0], y_points[1,1]]],
-# npy.float_)
-# return self._points
-
-# def _set_intervalx(self, pair):
-# # MGDTODO: Optimize
-# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]])
-# self.replace_child(0, bbox)
-# intervalx = property(BboxBase._get_intervalx, _set_intervalx)
-
-# def _set_intervaly(self, pair):
-# # MGDTODO: Optimize
-# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]])
-# self.replace_child(1, bbox)
-# intervaly = property(BboxBase._get_intervaly, _set_intervaly)
-
-class Transform(TransformNode):
- def __init__(self):
- TransformNode.__init__(self)
-
- def __call__(self, points):
- raise NotImplementedError()
-
- def __add__(self, other):
- if isinstance(other, Transform):
- return composite_transform_factory(self, other)
- raise TypeError(
- "Can not add Transform to object of type '%s'" % type(other))
-
- def __radd__(self, other):
- if isinstance(other, Transform):
- return composite_transform_factory(other, self)
- raise TypeError(
- "Can not add Transform to object of type '%s'" % type(other))
-
- def transform_point(self, point):
- return self.__call__(npy.asarray([point]))[0]
-
- def has_inverse(self):
- raise NotImplementedError()
-
- def inverted(self):
- raise NotImplementedError()
-
- def is_separable(self):
- return False
-
- def is_affine(self):
- return False
-
-class Affine2DBase(Transform):
- input_dims = 2
- output_dims = 2
-
- def __init__(self):
- Transform.__init__(self)
- self._inverted = None
-
- def _do_invalidation(self):
- result = self._inverted is None
- self._inverted = None
- return result
-
- #@staticmethod
- def _concat(a, b):
- return npy.dot(b, a)
- _concat = staticmethod(_concat)
-
- #@staticmethod
- def concat(a, b):
- return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix()))
- concat = staticmethod(concat)
-
- def to_values(self):
- mtx = self.get_matrix()
- return tuple(mtx[:2].swapaxes(0, 1).flatten())
-
- #@staticmethod
- def matrix_from_values(a, b, c, d, e, f):
- affine = npy.zeros((3, 3), npy.float_)
- affine[0, ] = a, c, e
- affine[1, ] = b, d, f
- affine[2, 2] = 1
- return affine
- matrix_from_values = staticmethod(matrix_from_values)
-
- def get_matrix(self):
- raise NotImplementedError()
-
- def __call__(self, points):
- """
- Applies the transformation to an array of 2D points and
- returns the result.
-
- points must be a numpy array of shape (N, 2), where N is the
- number of points.
- """
- # MGDTODO: The major speed trap here is just converting to
- # the points to an array in the first place. If we can use
- # more arrays upstream, that should help here.
- if not isinstance(points, npy.ndarray):
- import traceback
- print '-' * 60
- print 'A non-numpy array was passed in for transformation. Please '
- print 'correct this.'
- print "".join(traceback.format_stack())
- print points
- mtx = self.get_matrix()
- points = npy.asarray(points, npy.float_)
- points = points.transpose()
- points = npy.dot(mtx[0:2, 0:2], points)
- points = points + mtx[0:2, 2:]
- return points.transpose()
-
- def inverted(self):
- if self._inverted is None:
- mtx = self.get_matrix()
- self._inverted = Affine2D(inv(mtx))
- return self._inverted
-
- def is_separable(self):
- mtx = self.get_matrix()
- return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
-
- def is_affine(self):
- return True
-
-
-class Affine2D(Affine2DBase):
- input_dims = 2
- output_dims = 2
-
- def __init__(self, matrix = None):
- """
- Initialize an Affine transform from a 3x3 numpy float array.
-
- a c e
- b d f
- 0 0 1
- """
- Affine2DBase.__init__(self)
- if matrix is None:
- matrix = npy.identity(3)
- else:
- assert matrix.shape == (3, 3)
- self._mtx = matrix
- self._inverted = None
-
- def __repr__(self):
- return "Affine2D(%s)" % repr(self._mtx)
- __str__ = __repr__
-
- def __cmp__(self, other):
- if (isinstance(other, Affine2D) and
- (self.get_matrix() == other.get_matrix()).all()):
- return 0
- return -1
-
- def __copy__(self):
- return Affine2D(self._mtx.copy())
-
- def __deepcopy__(self, memo):
- return Affine2D(self._mtx.copy())
-
- #@staticmethod
- def from_values(a, b, c, d, e, f):
- return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f))
- from_values = staticmethod(from_values)
-
- def get_matrix(self):
- return self._mtx
-
- def set_matrix(self, mtx):
- self._mtx = mtx
- self.invalidate()
-
- def set(self, other):
- self._mtx = other.get_matrix()
- self.invalidate()
-
- #@staticmethod
- def identity():
- return Affine2D(npy.identity(3))
- identity = staticmethod(identity)
-
- def clear(self):
- self._mtx = npy.identity(3)
- self.invalidate()
- return self
-
- def rotate(self, theta):
- a = npy.cos(theta)
- b = npy.sin(theta)
- rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0)
- self._mtx = self._concat(self._mtx, rotate_mtx)
- self.invalidate()
- return self
-
- def rotate_deg(self, degrees):
- return self.rotate(degrees*npy.pi/180.)
-
- def rotate_around(self, x, y, theta):
- return self.translate(-x, -y).rotate(theta).translate(x, y)
-
- def rotate_deg_around(self, x, y, degrees):
- return self.translate(-x, -y).rotate_deg(degrees).translate(x, y)
-
- def translate(self, tx, ty):
- translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty)
- self._mtx = self._concat(self._mtx, translate_mtx)
- self.invalidate()
- return self
-
- def scale(self, sx, sy=None):
- if sy is None:
- sy = sx
- scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.)
- self._mtx = self._concat(self._mtx, scale_mtx)
- self.invalidate()
- return self
-
- def inverted(self):
- if self._inverted is None:
- mtx = self.get_matrix()
- self._inverted = Affine2D(inv(mtx))
- return self._inverted
-
- def is_separable(self):
- mtx = self.get_matrix()
- return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
-
- def is_affine(self):
- return True
-
-class BlendedTransform(Transform):
- def __init__(self, x_transform, y_transform):
- assert x_transform.is_separable()
- assert y_transform.is_separable()
-
- Transform.__init__(self)
- self._x = x_transform
- self._y = y_transform
- self.set_children(['_x', '_y'])
-
- def __call__(self, points):
- if self._x == self._y:
- return self._x(points)
-
- x_points = self._x(points)
- y_points = self._y(points)
- # This works because we already know the transforms are
- # separable
- return npy.hstack((x_points[:, 0:1], y_points[:, 1:2]))
-
-# def set_x_transform(self, x_transform):
-# self.replace_child(0, x_transform)
-
-# def set_y_transform(self, y_transform):
-# self.replace_child(1, y_transform)
-
-class BlendedAffine2D(Affine2DBase, BlendedTransform):
- def __init__(self, x_transform, y_transform):
- assert x_transform.is_affine()
- assert y_transform.is_affine()
- assert x_transform.is_separable()
- assert y_transform.is_separable()
- BlendedTransform.__init__(self, x_transform, y_transform)
-
- Affine2DBase.__init__(self)
- self._mtx = None
-
- def __repr__(self):
- return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
- __str__ = __repr__
-
- def _do_invalidation(self):
- if self._mtx is not None:
- self._mtx = None
- Affine2DBase._do_invalidation(self)
- return False
- return True
-
- def is_separable(self):
- return True
-
- def get_matrix(self):
- if self._mtx is None:
- if self._x == self._y:
- self._mtx = self._x.get_matrix()
- else:
- x_mtx = self._x.get_matrix()
- y_mtx = self._y.get_matrix()
- # This works because we already know the transforms are
- # separable, though normally one would want to set b and
- # c to zero.
- self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
- return self._mtx
-
-class CompositeTransform(Transform):
- def __init__(self, a, b):
- assert a.output_dims == b.input_dims
- self.input_dims = a.input_dims
- self.output_dims = b.output_dims
-
- Transform.__init__(self)
- self._a = a
- self._b = b
- self.set_children(['_a', '_b'])
-
- def __call__(self, points):
- return self._b(self._a(points))
-
-class CompositeAffine2D(Affine2DBase):
- def __init__(self, a, b):
- assert a.is_affine()
- assert b.is_affine()
-
- Affine2DBase.__init__(self)
- self._a = a
- self._b = b
- self.set_children(['_a', '_b'])
- self._mtx = None
-
- def __repr__(self):
- return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
- __str__ = __repr__
-
- def _do_invalidation(self):
- self._mtx = None
- Affine2DBase._do_invalidation(self)
-
- def get_matrix(self):
- if self._mtx is None:
- self._mtx = self._concat(
- self._a.get_matrix(),
- self._b.get_matrix())
- return self._mtx
-
-class BboxTransform(Affine2DBase):
- def __init__(self, boxin, boxout):
- assert isinstance(boxin, BboxBase)
- assert isinstance(boxout, BboxBase)
-
- Affine2DBase.__init__(self)
- self._boxin = boxin
- self._boxout = boxout
- self.set_children(['_boxin', '_boxout'])
- self._mtx = None
- self._inverted = None
-
- def __repr__(self):
- return "BboxTransform(%s, %s)" % (self._boxin, self._boxout)
- __str__ = __repr__
-
- def _do_invalidation(self):
- if self._mtx is not None:
- self._mtx = None
- Affine2DBase._do_invalidation(self)
- return False
- return True
-
- def is_separable(self):
- return True
-
- def get_matrix(self):
- if self._mtx is None:
- boxin = self._boxin
- boxout = self._boxout
- x_scale = boxout.width / boxin.width
- y_scale = boxout.height / boxin.height
-
- # MGDTODO: Optimize
- affine = Affine2D() \
- .translate(-boxin.xmin, -boxin.ymin) \
- .scale(x_scale, y_scale) \
- .translate(boxout.xmin, boxout.ymin)
-
- self._mtx = affine._mtx
- return self._mtx
-
-def blend_xy_sep_transform(x_transform, y_transform):
- if x_transform.is_affine() and y_transform.is_affine():
- return BlendedAffine2D(x_transform, y_transform)
- return BlendedTransform(x_transform, y_transform)
-
-def composite_transform_factory(a, b):
- if a.is_affine() and b.is_affine():
- return CompositeAffine2D(a, b)
- return CompositeTransform(a, b)
-
-# MGDTODO: There's probably a better place for this
-def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True):
- '''
- Ensure the endpoints of a range are not too close together.
-
- "too close" means the interval is smaller than 'tiny' times
- the maximum absolute value.
-
- If they are too close, each will be moved by the 'expander'.
- If 'increasing' is True and vmin > vmax, they will be swapped,
- regardless of whether they are too close.
- '''
- swapped = False
- if vmax < vmin:
- vmin, vmax = vmax, vmin
- swapped = True
- if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny:
- if vmin == 0.0:
- vmin = -expander
- vmax = expander
- else:
- vmin -= expander*abs(vmin)
- vmax += expander*abs(vmax)
- if swapped and not increasing:
- vmin, vmax = vmax, vmin
- return vmin, vmax
-
-# MGDTODO: Optimize
-def interval_contains(interval, val):
- return interval[0] <= val and interval[1] >= val
-
-def interval_contains_open(interval, val):
- return interval[0] < val and interval[1] > val
-
-if __name__ == '__main__':
- import copy
- from random import random
- import timeit
-
- bbox = Bbox.from_lbrt(10., 15., 20., 25.)
- assert bbox.xmin == 10
- assert bbox.ymin == 15
- assert bbox.xmax == 20
- assert bbox.ymax == 25
-
- assert npy.all(bbox.min == [10, 15])
- assert npy.all(bbox.max == [20, 25])
- assert npy.all(bbox.intervalx == (10, 20))
- assert npy.all(bbox.intervaly == (15, 25))
-
- assert bbox.width == 10
- assert bbox.height == 10
-
- assert bbox.bounds == (10, 15, 10, 10)
-
- print npy.asarray(bbox)
-
- bbox.intervalx = (11, 21)
- bbox.intervaly = (16, 26)
-
- assert bbox.bounds == (11, 16, 10, 10)
-
- bbox.xmin = 12
- bbox.ymin = 17
- bbox.xmax = 22
- bbox.ymax = 27
-
- assert bbox.bounds == (12, 17, 10, 10)
-
- bbox = Bbox.from_lbwh(10, 11, 12, 13)
- assert bbox.bounds == (10, 11, 12, 13)
-
- bbox_copy = copy.copy(bbox)
- assert bbox == bbox_copy
- bbox_copy.max = (14, 15)
- assert bbox.bounds == (10, 11, 12, 13)
- assert bbox_copy.bounds == (10, 11, 4, 4)
-
- bbox1 = Bbox([[10., 15.], [20., 25.]])
- bbox2 = Bbox([[30., 35.], [40., 45.]])
- trans = BboxTransform(bbox1, bbox2)
- bbox3 = bbox1.transformed(trans)
- assert bbox3 == bbox2
-
- translation = Affine2D().translate(10, 20)
- assert translation.to_values() == (1, 0, 0, 1, 10, 20)
- scale = Affine2D().scale(10, 20)
- assert scale.to_values() == (10, 0, 0, 20, 0, 0)
- rotation = Affine2D().rotate_deg(30)
- print rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
- -0.49999999999999994, 0.86602540378443871,
- 0.0, 0.0)
-
- points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_)
- translated_points = translation(points)
- assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all()
- scaled_points = scale(points)
- print scaled_points
- rotated_points = rotation(points)
- print rotated_points
-
- tpoints1 = rotation(translation(scale(points)))
- trans_sum = scale + translation + rotation
- tpoints2 = trans_sum(points)
- print tpoints1, tpoints2
- print tpoints1 == tpoints2
- # Need to do some sort of fuzzy comparison here?
- # assert (tpoints1 == tpoints2).all()
-
- # Here are some timing tests
- points = npy.asarray([(random(), random()) for i in xrange(10000)])
- t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points")
- print "Time to transform 10000 x 10 points:", t.timeit(10)
-
-__all__ = ['Transform', 'Affine2D']
Modified: branches/transforms/lib/matplotlib/artist.py
===================================================================
--- branches/transforms/lib/matplotlib/artist.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/artist.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -1,7 +1,7 @@
from __future__ import division
import sys, re
from cbook import iterable, flatten
-from affine import Affine2D
+from transforms import Affine2D
import matplotlib.units as units
## Note, matplotlib artists use the doc strings for set and get
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -9,7 +9,6 @@
rcParams = matplotlib.rcParams
from matplotlib import artist as martist
-from matplotlib import affine as maffine
from matplotlib import agg
from matplotlib import axis as maxis
from matplotlib import cbook
@@ -29,6 +28,7 @@
from matplotlib import table as mtable
from matplotlib import text as mtext
from matplotlib import ticker as mticker
+from matplotlib import transforms as mtransforms
iterable = cbook.iterable
is_string_like = cbook.is_string_like
@@ -481,7 +481,7 @@
"""
martist.Artist.__init__(self)
- self._position = maffine.Bbox.from_lbwh(*rect)
+ self._position = mtransforms.Bbox.from_lbwh(*rect)
self._originalPosition = copy.deepcopy(self._position)
self.set_axes(self)
self.set_aspect('auto')
@@ -612,7 +612,7 @@
"""
martist.Artist.set_figure(self, fig)
- self.bbox = maffine.TransformedBbox(self._position, fig.transFigure)
+ self.bbox = mtransforms.TransformedBbox(self._position, fig.transFigure)
#these will be updated later as data is added
self._set_lim_and_transforms()
@@ -631,7 +631,7 @@
set the dataLim and viewLim BBox attributes and the
transData and transAxes Transformation attributes
"""
- Bbox = maffine.Bbox
+ Bbox = mtransforms.Bbox
self.viewLim = Bbox.unit()
if self._sharex is not None:
@@ -647,20 +647,11 @@
self.dataLim = Bbox.unit()
- self.transAxes = maffine.BboxTransform(
+ self.transAxes = mtransforms.BboxTransform(
Bbox.unit(), self.bbox)
- localTransData = maffine.BboxTransform(
+ self.transData = mtransforms.BboxTransform(
self.viewLim, self.bbox)
- if self._sharex:
- transDataX = self._sharex.transData
- else:
- transDataX = localTransData
- if self._sharey:
- transDataY = self._sharey.transData
- else:
- transDataY = localTransData
- self.transData = localTransData # maffine.blend_xy_sep_transform(transDataX, transDataY)
def get_position(self, original=False):
@@ -1538,7 +1529,7 @@
# and min(xmin, xmax)<=0):
# raise ValueError('Cannot set nonpositive limits with log transform')
- xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False)
+ xmin, xmax = mtransforms.nonsingular(xmin, xmax, increasing=False)
self.viewLim.intervalx = (xmin, xmax)
if emit:
@@ -1670,7 +1661,7 @@
# and min(ymin, ymax)<=0):
# raise ValueError('Cannot set nonpositive limits with log transform')
- ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False)
+ ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False)
self.viewLim.intervaly = (ymin, ymax)
if emit:
self.callbacks.process('ylim_changed', self)
@@ -2167,7 +2158,7 @@
%(Annotation)s
"""
a = mtext.Annotation(*args, **kwargs)
- a.set_transform(maffine.Affine2D())
+ a.set_transform(mtransforms.Affine2D())
self._set_artist_props(a)
if kwargs.has_key('clip_on'): a.set_clip_box(self.bbox)
self.texts.append(a)
@@ -2206,7 +2197,7 @@
%(Line2D)s
"""
- trans = maffine.blend_xy_sep_transform(self.transAxes, self.transData)
+ trans = mtransforms.blend_xy_sep_transform(self.transAxes, self.transData)
l, = self.plot([xmin,xmax], [y,y], transform=trans, scalex=False, **kwargs)
return l
@@ -2242,7 +2233,7 @@
%(Line2D)s
"""
- trans = maffine.blend_xy_sep_transform( self.transData, self.transAxes )
+ trans = mtransforms.blend_xy_sep_transform( self.transData, self.transAxes )
l, = self.plot([x,x], [ymin,ymax] , transform=trans, scaley=False, **kwargs)
return l
@@ -2281,7 +2272,7 @@
%(Polygon)s
"""
# convert y axis units
- trans = maffine.blend_xy_sep_transform( self.transAxes, self.transData)
+ trans = mtransforms.blend_xy_sep_transform( self.transAxes, self.transData)
verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(trans)
@@ -2321,7 +2312,7 @@
%(Polygon)s
"""
# convert x axis units
- trans = maffine.blend_xy_sep_transform(self.transData, self.transAxes)
+ trans = mtransforms.blend_xy_sep_transform(self.transData, self.transAxes)
verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(trans)
@@ -4104,7 +4095,7 @@
offsets = zip(x,y),
transOffset = self.transData,
)
- collection.set_transform(maffine.Affine2D())
+ collection.set_transform(mtransforms.Affine2D())
collection.set_alpha(alpha)
collection.update(kwargs)
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -15,10 +15,10 @@
from ticker import NullFormatter, FixedFormatter, ScalarFormatter, LogFormatter
from ticker import NullLocator, FixedLocator, LinearLocator, LogLocator, AutoLocator
-from affine import Affine2D, Bbox, blend_xy_sep_transform, interval_contains, \
- interval_contains_open
from font_manager import FontProperties
from text import Text, TextWithDash, _process_text_args
+from transforms import Affine2D, Bbox, blended_transform_factory, interval_contains, \
+ interval_contains_open
from patches import bbox_artist
import matplotlib.units as units
@@ -236,7 +236,7 @@
xaxis=True,
)
- trans = blend_xy_sep_transform(
+ trans = blended_transform_factory(
self.axes.transData, self.axes.transAxes)
#offset the text downward with a post transformation
trans = trans + Affine2D().translate(0, -1 * self._padPixels)
@@ -261,7 +261,7 @@
horizontalalignment='center',
)
- trans = blend_xy_sep_transform(
+ trans = blended_transform_factory(
self.axes.transData, self.axes.transAxes)
# offset the text upward with a post transformation
trans = trans + Affine2D().translate(0, self._padPixels)
@@ -279,7 +279,7 @@
marker = self._xtickmarkers[0],
markersize=self._size,
)
- l.set_transform(blend_xy_sep_transform(
+ l.set_transform(blended_transform_factory(
self.axes.transData, self.axes.transAxes) )
self._set_artist_props(l)
return l
@@ -295,7 +295,7 @@
markersize=self._size,
)
- l.set_transform(blend_xy_sep_transform(
+ l.set_transform(blended_transform_factory(
self.axes.transData, self.axes.transAxes) )
self._set_artist_props(l)
return l
@@ -310,7 +310,7 @@
antialiased=False,
)
l.set_transform(
- blend_xy_sep_transform(
+ blended_transform_factory(
self.axes.transData, self.axes.transAxes))
l.set_clip_box(self.axes.bbox)
self._set_artist_props(l)
@@ -359,7 +359,7 @@
dashdirection=0,
xaxis=False,
)
- trans = blend_xy_sep_transform(
+ trans = blended_transform_factory(
self.axes.transAxes, self.axes.transData)
# offset the text leftward with a post transformation
trans = trans + Affine2D().translate(-1 * self._padPixels, 0)
@@ -382,7 +382,7 @@
xaxis=False,
horizontalalignment='left',
)
- trans = blend_xy_sep_transform(
+ trans = blended_transform_factory(
self.axes.transAxes, self.axes.transData)
# offset the text rightward with a post transformation
trans = trans + Affine2D().translate(self._padPixels, 0)
@@ -401,7 +401,7 @@
markersize=self._size,
)
l.set_transform(
- blend_xy_sep_transform(
+ blended_transform_factory(
self.axes.transAxes, self.axes.transData))
self._set_artist_props(l)
return l
@@ -417,7 +417,7 @@
)
l.set_transform(
- blend_xy_sep_transform(
+ blended_transform_factory(
self.axes.transAxes, self.axes.transData))
self._set_artist_props(l)
return l
@@ -432,7 +432,7 @@
antialiased=False,
)
- l.set_transform( blend_xy_sep_transform(
+ l.set_transform( blended_transform_factory(
self.axes.transAxes, self.axes.transData) )
l.set_clip_box(self.axes.bbox)
@@ -979,7 +979,7 @@
verticalalignment='top',
horizontalalignment='center',
)
- label.set_transform( blend_xy_sep_transform(
+ label.set_transform( blended_transform_factory(
self.axes.transAxes, Affine2D() ))
self._set_artist_props(label)
@@ -994,7 +994,7 @@
verticalalignment='top',
horizontalalignment='right',
)
- offsetText.set_transform( blend_xy_sep_transform(
+ offsetText.set_transform( blended_transform_factory(
self.axes.transAxes, Affine2D() ))
self._set_artist_props(offsetText)
self.offset_text_position='bottom'
@@ -1169,7 +1169,7 @@
horizontalalignment='right',
rotation='vertical',
)
- label.set_transform( blend_xy_sep_transform(
+ label.set_transform( blended_transform_factory(
Affine2D(), self.axes.transAxes) )
self._set_artist_props(label)
@@ -1184,7 +1184,7 @@
verticalalignment = 'bottom',
horizontalalignment = 'left',
)
- offsetText.set_transform(blend_xy_sep_transform(
+ offsetText.set_transform(blended_transform_factory(
self.axes.transAxes, Affine2D()) )
self._set_artist_props(offsetText)
self.offset_text_position='left'
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -84,7 +84,7 @@
from matplotlib.font_manager import findfont
from matplotlib.ft2font import FT2Font, LOAD_DEFAULT
from matplotlib.mathtext import MathTextParser
-from matplotlib.affine import Bbox
+from matplotlib.transforms import Bbox
from _backend_agg import RendererAgg as _RendererAgg
@@ -115,15 +115,10 @@
'debug-annoying')
# self.draw_polygon = self._renderer.draw_polygon
self.draw_rectangle = self._renderer.draw_rectangle
- self.draw_path = self._renderer.draw_path
# MGDTODO -- remove these lines
# self.draw_lines = self._renderer.draw_lines
# self.draw_markers = self._renderer.draw_markers
self.draw_image = self._renderer.draw_image
- self.draw_line_collection = self._renderer.draw_line_collection
- self.draw_quad_mesh = self._renderer.draw_quad_mesh
- self.draw_poly_collection = self._renderer.draw_poly_collection
- self.draw_regpoly_collection = self._renderer.draw_regpoly_collection
self.copy_from_bbox = self._renderer.copy_from_bbox
self.restore_region = self._renderer.restore_region
Modified: branches/transforms/lib/matplotlib/figure.py
===================================================================
--- branches/transforms/lib/matplotlib/figure.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/figure.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -18,8 +18,8 @@
from text import Text, _process_text_args
from legend import Legend
-from affine import Affine2D, Bbox, BboxTransform, TransformedBbox
from ticker import FormatStrFormatter
+from transforms import Affine2D, Bbox, BboxTransform, TransformedBbox
from cm import ScalarMappable
from contour import ContourSet
import warnings
Modified: branches/transforms/lib/matplotlib/finance.py
===================================================================
--- branches/transforms/lib/matplotlib/finance.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/finance.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -22,7 +22,7 @@
from matplotlib.colors import colorConverter
from lines import Line2D, TICKLEFT, TICKRIGHT
from patches import Rectangle
-from matplotlib.affine import Affine2D
+from matplotlib.transforms import Affine2D
Modified: branches/transforms/lib/matplotlib/legend.py
===================================================================
--- branches/transforms/lib/matplotlib/legend.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/legend.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -34,7 +34,7 @@
from patches import Patch, Rectangle, RegularPolygon, Shadow, bbox_artist, draw_bbox
from collections import LineCollection, RegularPolyCollection, PatchCollection
from text import Text
-from affine import Bbox, BboxTransform
+from transforms import Bbox, BboxTransform
def line_cuts_bbox(line, bbox):
""" Return True if and only if line cuts bbox. """
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -14,10 +14,10 @@
import numerix.ma as ma
from matplotlib import verbose
import artist
-from affine import Bbox
from artist import Artist, setp
from cbook import iterable, is_string_like, is_numlike
from colors import colorConverter
+from transforms import Bbox
from matplotlib import rcParams
Modified: branches/transforms/lib/matplotlib/table.py
===================================================================
--- branches/transforms/lib/matplotlib/table.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/table.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -29,7 +29,7 @@
from patches import Rectangle
from cbook import enumerate, is_string_like, flatten
from text import Text
-from affine import Bbox
+from transforms import Bbox
Modified: branches/transforms/lib/matplotlib/text.py
===================================================================
--- branches/transforms/lib/matplotlib/text.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/text.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -15,7 +15,7 @@
from cbook import enumerate, is_string_like, maxdict, is_numlike
from font_manager import FontProperties
from patches import bbox_artist, YAArrow
-from affine import Affine2D, Bbox
+from transforms import Affine2D, Bbox
from lines import Line2D
import matplotlib.nxutils as nxutils
Modified: branches/transforms/lib/matplotlib/ticker.py
===================================================================
--- branches/transforms/lib/matplotlib/ticker.py 2007-09-14 12:24:20 UTC (rev 3850)
+++ branches/transforms/lib/matplotlib/ticker.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -109,7 +109,7 @@
import matplotlib as mpl
from matplotlib import verbose, rcParams
from matplotlib import cbook
-from matplotlib import affine as maffine
+from matplotlib import transforms as mtransforms
@@ -540,7 +540,7 @@
def autoscale(self):
'autoscale the view limits'
self.verify_intervals()
- return maffine.nonsingular(*self.dataInterval.get_bounds())
+ return mtransforms.nonsingular(*self.dataInterval.get_bounds())
def pan(self, numsteps):
'Pan numticks (can be positive or negative)'
@@ -682,7 +682,7 @@
vmin = math.floor(scale*vmin)/scale
vmax = math.ceil(scale*vmax)/scale
- return maffine.nonsingular(vmin, vmax)
+ return mtransforms.nonsingular(vmin, vmax)
def closeto(x,y):
@@ -766,7 +766,7 @@
vmin -=1
vmax +=1
- return maffine.nonsingular(vmin, vmax)
+ return mtransforms.nonsingular(vmin, vmax)
def scale_range(vmin, vmax, n = 1, threshold=100):
dv = abs(vmax - vmin)
@@ -833,12 +833,12 @@
def __call__(self):
vmin, vmax = self.axis.get_view_interval()
- vmin, vmax = maffine.nonsingular(vmin, vmax, expander = 0.05)
+ vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander = 0.05)
return self.bin_boundaries(vmin, vmax)
def autoscale(self):
dmin, dmax = self.axis.get_data_interval()
- dmin, dmax = maffine.nonsingular(dmin, dmax, expander = 0.05)
+ dmin, dmax = mtransforms.nonsingular(dmin, dmax, expander = 0.05)
return npy.take(self.bin_boundaries(dmin, dmax), [0,-1])
@@ -939,7 +939,7 @@
if vmin==vmax:
vmin = decade_down(vmin,self._base)
vmax = decade_up(vmax,self._base)
- return maffine.nonsingular(vmin, vmax)
+ return mtransforms.nonsingular(vmin, vmax)
class AutoLocator(MaxNLocator):
def __init__(self):
Copied: branches/transforms/lib/matplotlib/transforms.py (from rev 3849, branches/transforms/lib/matplotlib/affine.py)
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py (rev 0)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-14 13:03:31 UTC (rev 3851)
@@ -0,0 +1,852 @@
+"""
+A set of classes to handle transformations.
+
+2007 Michael Droettboom
+"""
+
+import numpy as npy
+from numpy.linalg import inv
+from sets import Set
+
+# MGDTODO: This creates a ton of cyclical references. We may want to
+# consider using weak references
+
+# MGDTODO: deep copying is probably incorrect wrt the parent/child
+# relationships
+
+class TransformNode(object):
+ def __init__(self):
+ self._parents = Set()
+
+ def invalidate(self):
+ if not self._do_invalidation():
+ for parent in self._parents:
+ parent.invalidate()
+
+ def _do_invalidation(self):
+ return False
+
+ def set_children(self, children):
+ for child in children:
+ getattr(self, child)._parents.add(self)
+ self._children = children
+
+ # MGDTODO: decide whether we need this in-place updating and
+ # remove if not
+# def replace_child(self, index, child):
+# children = self._children
+# getattr(self, children[index])._parents.remove(self)
+# setattr(self, children[index], child)
+# # We have to reset children in case two or more
+# # of the children are the same
+# for child in children:
+# getattr(self, child)._parents.add(self)
+# self.invalidate()
+
+class BboxBase(TransformNode):
+ '''
+ This is the read-only part of a bounding-box
+ '''
+
+ def __init__(self):
+ TransformNode.__init__(self)
+
+ def __array__(self):
+ return self.get_points()
+
+ # MGDTODO: Probably a more efficient ways to do this...
+ def _get_xmin(self):
+ return self.get_points()[0, 0]
+ xmin = property(_get_xmin)
+
+ def _get_ymin(self):
+ return self.get_points()[0, 1]
+ ymin = property(_get_ymin)
+
+ def _get_xmax(self):
+ return self.get_points()[1, 0]
+ xmax = property(_get_xmax)
+
+ def _get_ymax(self):
+ return self.get_points()[1, 1]
+ ymax = property(_get_ymax)
+
+ def _get_min(self):
+ return self.get_points()[0]
+ min = property(_get_min)
+
+ def _get_max(self):
+ return self.get_points()[1]
+ max = property(_get_max)
+
+ def _get_intervalx(self):
+ return self.get_points()[:, 0]
+ intervalx = property(_get_intervalx)
+
+ def _get_intervaly(self):
+ return self.get_points()[:, 1]
+ intervaly = property(_get_intervaly)
+
+ def _get_width(self):
+ return self.xmax - self.xmin
+ width = property(_get_width)
+
+ def _get_height(self):
+ return self.ymax - self.ymin
+ height = property(_get_height)
+
+ def _get_bounds(self):
+ return (self.xmin, self.ymin,
+ self.xmax - self.xmin, self.ymax - self.ymin)
+ bounds = property(_get_bounds)
+
+ def get_points(self):
+ return NotImplementedError()
+
+ # MGDTODO: Optimize
+ def containsx(self, x):
+ return x >= self.xmin and x <= self.xmax
+
+ def containsy(self, y):
+ return y >= self.ymin and y <= self.ymax
+
+ def contains(self, x, y):
+ return self.containsx(x) and self.containsy(y)
+
+ def overlapsx(self, other):
+ return self.containsx(other.xmin) \
+ or self.containsx(other.xmax)
+
+ def overlapsy(self, other):
+ return self.containsy(other.ymin) \
+ or self.containsx(other.ymax)
+
+ def overlaps(self, other):
+ return self.overlapsx(other) \
+ and self.overlapsy(other)
+
+ def fully_containsx(self, x):
+ return x > self.xmin and x < self.xmax
+
+ def fully_containsy(self, y):
+ return y > self.ymin and y < self.ymax
+
+ def fully_contains(self, x, y):
+ return self.fully_containsx(x) \
+ and self.fully_containsy(y)
+
+ def fully_overlapsx(self, other):
+ return self.fully_containsx(other.xmin) \
+ or self.fully_containsx(other.xmax)
+
+ def fully_overlapsy(self, other):
+ return self.fully_containsy(other.ymin) \
+ or self.fully_containsx(other.ymax)
+
+ def fully_overlaps(self, other):
+ return self.fully_overlapsx(other) and \
+ self.fully_overlapsy(other)
+
+
+class Bbox(BboxBase):
+ def __init__(self, points):
+ BboxBase.__init__(self)
+ self._points = npy.asarray(points, npy.float_)
+
+ #@staticmethod
+ def unit():
+ return Bbox.from_lbrt(0., 0., 1., 1.)
+ unit = staticmethod(unit)
+
+ #@staticmethod
+ def from_lbwh(left, bottom, width, height):
+ return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
+ from_lbwh = staticmethod(from_lbwh)
+
+ #@staticmethod
+ def from_lbrt(*args):
+ points = npy.array(args, dtype=npy.float_).reshape(2, 2)
+ return Bbox(points)
+ from_lbrt = staticmethod(from_lbrt)
+
+ def __copy__(self):
+ return Bbox(self._points.copy())
+
+ def __deepcopy__(self, memo):
+ return Bbox(self._points.copy())
+
+ def __cmp__(self, other):
+ # MGDTODO: Totally suboptimal
+ if isinstance(other, Bbox) and (self._points == other._points).all():
+ return 0
+ return -1
+
+ def __repr__(self):
+ return 'Bbox(%s)' % repr(self._points)
+ __str__ = __repr__
+
+ # JDH: the update method will update the box limits from the
+ # existing limits and the new data; it appears here you are just
+ # using the new data. We use an "ignore" flag to specify whether
+ # you want to include the existing data or not in the update
+ def update_from_data(self, x, y, ignore=True):
+ self._points = npy.array(
+ [[x.min(), y.min()], [x.max(), y.max()]],
+ npy.float_)
+ self.invalidate()
+
+ # MGDTODO: Probably a more efficient ways to do this...
+ def _set_xmin(self, val):
+ self._points[0, 0] = val
+ self.invalidate()
+ xmin = property(BboxBase._get_xmin, _set_xmin)
+
+ def _set_ymin(self, val):
+ self._points[0, 1] = val
+ self.invalidate()
+ ymin = property(BboxBase._get_ymin, _set_ymin)
+
+ def _set_xmax(self, val):
+ self._points[1, 0] = val
+ self.invalidate()
+ xmax = property(BboxBase._get_xmax, _set_xmax)
+
+ def _set_ymax(self, val):
+ self._points[1, 1] = val
+ self.invalidate()
+ ymax = property(BboxBase._get_ymax, _set_ymax)
+
+ def _set_min(self, val):
+ self._points[0] = val
+ self.invalidate()
+ min = property(BboxBase._get_min, _set_min)
+
+ def _set_max(self, val):
+ self._points[1] = val
+ self.invalidate()
+ max = property(BboxBase._get_max, _set_max)
+
+ def _set_intervalx(self, interval):
+ self._points[:, 0] = interval
+ self.invalidate()
+ intervalx = property(BboxBase._get_intervalx, _set_intervalx)
+
+ def _set_intervaly(self, interval):
+ self._points[:, 1] = interval
+ self.invalidate()
+ intervaly = property(BboxBase._get_intervaly, _set_intervaly)
+
+ def _set_bounds(self, bounds):
+ l,b,w,h = bounds
+ self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
+ self.invalidate()
+ bounds = property(BboxBase._get_bounds, _set_bounds)
+
+ def get_points(self):
+ return self._points
+
+ def set_points(self, points):
+ self._points = points
+ self.invalidate()
+
+ def set(self, other):
+ self._points = other.get_points()
+ self.invalidate()
+
+ def transformed(self, transform):
+ return Bbox(transform(self._points))
+
+ def inverse_transformed(self, transform):
+ return Bbox(transform.inverted()(self._points))
+
+ def expanded(self, sw, sh):
+ width = self.width
+ height = self.height
+ deltaw = (sw * width - width) / 2.0
+ deltah = (sh * height - height) / 2.0
+ a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
+ return Bbox(self._points + a)
+
+ #@staticmethod
+ def union(bboxes):
+ """
+ Return the Bbox that bounds all bboxes
+ """
+ assert(len(bboxes))
+
+ if len(bboxes) == 1:
+ return bboxes[0]
+
+ bbox = bboxes[0]
+ xmin = bbox.xmin
+ ymin = bbox.ymin
+ xmax = bbox.xmax
+ ymax = bbox.ymax
+
+ for bbox in bboxes[1:]:
+ xmin = min(xmin, bbox.xmin)
+ ymin = min(ymin, bbox.ymin)
+ xmax = max(xmax, bbox.xmax)
+ ymax = max(ymax, bbox.ymax)
+
+ return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
+ union = staticmethod(union)
+
+class TransformedBbox(BboxBase):
+ def __init__(self, bbox, transform):
+ assert isinstance(bbox, Bbox)
+ assert isinstance(transform, Transform)
+
+ BboxBase.__init__(self)
+ self.bbox = bbox
+ self.transform = transform
+ self.set_children(['bbox', 'transform'])
+ self._points = None
+
+ def __repr__(self):
+ return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
+ __str__ = __repr__
+
+ def _do_invalidation(self):
+ self._points = None
+
+ def get_points(self):
+ if self._points is None:
+ self._points = self.transform(self.bbox.get_points())
+ return self._points
+
+# MGDTODO: This code probably works, but I don't think it's a good idea
+# (from a code clarity perspective)
+# class BlendedBbox(BboxBase):
+# def __init__(self, bbox_x, bbox_y):
+# assert isinstance(bbox_x, BboxBase)
+# assert isinstance(bbox_y, BboxBase)
+
+# BboxBase.__init__(self)
+# self._x = bbox_x
+# self._y = bbox_y
+# self.set_children(['_x', '_y'])
+# self._points = None
+
+# def __repr__(self):
+# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
+# __str__ = __repr__
+
+# def _do_invalidation(self):
+# self._points = None
+
+# def get_points(self):
+# if self._points is None:
+# # MGDTODO: Optimize
+# if self._x == self._y:
+# self._points = self._x.get_points()
+# else:
+# x_points = self._x.get_points()
+# y_points = self._y.get_points()
+# self._points = npy.array(
+# [[x_points[0,0], y_points[0,1]],
+# [x_points[1,0], y_points[1,1]]],
+# npy.float_)
+# return self._points
+
+# def _set_intervalx(self, pair):
+# # MGDTODO: Optimize
+# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]])
+# self.replace_child(0, bbox)
+# intervalx = property(BboxBase._get_intervalx, _set_intervalx)
+
+# def _set_intervaly(self, pair):
+# # MGDTODO: Optimize
+# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]])
+# self.replace_child(1, bbox)
+# intervaly = property(BboxBase._get_intervaly, _set_intervaly)
+
+class Transform(TransformNode):
+ def __init__(self):
+ TransformNode.__init__(self)
+
+ def __call__(self, points):
+ raise NotImplementedError()
+
+ def __add__(self, other):
+ if isinstance(other, Transform):
+ return composite_transform_factory(self, other)
+ raise TypeError(
+ "Can not add Transform to object of type '%s'" % type(other))
+
+ def __radd__(self, other):
+ if isinstance(other, Transform):
+ return composite_transform_factory(other, self)
+ raise TypeError(
+ "Can not add Transform to object of type '%s'" % type(other))
+
+ def transform_point(self, point):
+ return self.__call__(npy.asarray([point]))[0]
+
+ def has_inverse(self):
+ raise NotImplementedError()
+
+ def inverted(self):
+ raise NotImplementedError()
+
+ def is_separable(self):
+ return False
+
+ def is_affine(self):
+ return False
+
+class Affine2DBase(Transform):
+ input_dims = 2
+ output_dims = 2
+
+ def __init__(self):
+ Transform.__init__(self)
+ self._inverted = None
+
+ def _do_invalidation(self):
+ result = self._inverted is None
+ self._inverted = None
+ return result
+
+ #@staticmethod
+ def _concat(a, b):
+ return npy.dot(b, a)
+ _concat = staticmethod(_concat)
+
+ #@staticmethod
+ def concat(a, b):
+ return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix()))
+ concat = staticmethod(concat)
+
+ def to_values(self):
+ mtx = self.get_matrix()
+ return tuple(mtx[:2].swapaxes(0, 1).flatten())
+
+ #@staticmethod
+ def matrix_from_values(a, b, c, d, e, f):
+ affine = npy.zeros((3, 3), npy.float_)
+ affine[0, ] = a, c, e
+ affine[1, ] = b, d, f
+ affine[2, 2] = 1
+ return affine
+ matrix_from_values = staticmethod(matrix_from_values)
+
+ def get_matrix(self):
+ raise NotImplementedError()
+
+ def __call__(self, points):
+ """
+ Applies the transformation to an array of 2D points and
+ returns the result.
+
+ points must be a numpy array of shape (N, 2), where N is the
+ number of points.
+ """
+ # MGDTODO: The major speed trap here is just converting to
+ # the points to an array in the first place. If we can use
+ # more arrays upstream, that should help here.
+ if not isinstance(points, npy.ndarray):
+ import traceback
+ print '-' * 60
+ print 'A non-numpy array was passed in for transformation. Please '
+ print 'correct this.'
+ print "".join(traceback.format_stack())
+ print points
+ mtx = self.get_matrix()...
[truncated message content] |
|
From: <md...@us...> - 2007-09-14 12:24:22
|
Revision: 3850
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3850&view=rev
Author: mdboom
Date: 2007-09-14 05:24:20 -0700 (Fri, 14 Sep 2007)
Log Message:
-----------
Deleting this file to rename affine.py
Removed Paths:
-------------
branches/transforms/lib/matplotlib/transforms.py
Deleted: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-14 12:23:06 UTC (rev 3849)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-14 12:24:20 UTC (rev 3850)
@@ -1,652 +0,0 @@
-# """
-# The transforms module is broken into two parts, a collection of
-# classes written in the extension module _transforms to handle
-# efficient transformation of data, and some helper functions in
-# transforms to make it easy to instantiate and use those objects.
-# Hence the core of this module lives in _transforms.
-
-# The transforms class is built around the idea of a LazyValue. A
-# LazyValue is a base class that defines a method get that returns the
-# value. The concrete derived class Value wraps a float, and simply
-# returns the value of that float. The concrete derived class BinOp
-# allows binary operations on LazyValues, so you can add them, multiply
-# them, etc. When you do something like
-
-# inches = Value(8)
-# dpi = Value(72)
-# width = inches * dpi
-
-# width is a BinOp instance (that tells you the width of the figure in
-# pixels). Later, if the figure size in changed, ie we call
-
-# inches.set(10)
-
-# The width variable is automatically updated because it stores a
-# pointer to the inches variable, not the value. Since a BinOp is also
-# a lazy value, you can define binary operations on BinOps as well, such
-# as
-
-# middle = Value(0.5) * width
-
-# Pairs of LazyValue instances can occur as instances of two classes:
-
-# pt = Point( Value(x), Value(y)) # where x, y are numbers
-# pt.x(), pt.y() return Value(x), Value(y))
-
-# iv = Interval( Value(x), Value(y))
-# iv.contains(z) returns True if z is in the closed interval
-# iv.contains_open(z): same for open interval
-# iv.span() returns y-x as a float
-# iv.get_bounds() returns (x,y) as a tuple of floats
-# iv.set_bounds(x, y) allows input of new floats
-# iv.update(seq) updates the bounds to include all elements
-# in a sequence of floats
-# iv.shift(s) shifts the interval by s, a float
-
-# The bounding box class Bbox is also heavily used, and is defined by a
-# lower left point ll and an upper right point ur. The points ll and ur
-# are given by Point(x, y) instances, where x and y are LazyValues. So
-# you can represent a point such as
-
-# ll = Point( Value(0), Value(0) ) # the origin
-# ur = Point( width, height ) # the upper right of the figure
-
-# where width and height are defined as above, using the product of the
-# figure width in inches and the dpi. This is, in face, how the Figure
-# bbox is defined
-
-# bbox = Bbox(ll, ur)
-
-# A bbox basically defines an x,y coordinate system, with ll giving the
-# lower left of the coordinate system and ur giving the upper right.
-
-# The bbox methods are
-
-# ll() - return the lower left Point
-# ur() - return the upper right Point
-# contains(x,y) - return True if self contains point
-# overlaps(bbox) - return True if self overlaps bbox
-# overlapsx(bbox) - return True if self overlaps bbox in the x interval
-# overlapsy(bbox) - return True if self overlaps bbox in the y interval
-# intervalx() - return the x Interval instance
-# intervaly() - return the y interval instance
-# get_bounds() - get the left, bottom, width, height bounding tuple
-# update(xys, ignore) - update the bbox to bound all the xy tuples in
-# xys; if ignore is true ignore the current contents of bbox and
-# just bound the tuples. If ignore is false, bound self + tuples
-# width() - return the width of the bbox
-# height() - return the height of the bbox
-# xmax() - return the x coord of upper right
-# ymax() - return the y coord of upper right
-# xmin() - return the x coord of lower left
-# ymin() - return the y coord of lower left
-# scale(sx,sy) - scale the bbox by sx, sy
-# deepcopy() - return a deep copy of self (pointers are lost)
-
-
-# The basic transformation maps one bbox to another, with an optional
-# nonlinear transformation of one of coordinates (eg log scaling).
-
-# The base class for transformations is Transformation, and the concrete
-# derived classes are SeparableTransformation and Affine. Earlier
-# versions of matplotlib handled transformation of x and y separately
-# (ie we assumed all transformations were separable) but this makes it
-# difficult to do rotations or polar transformations, for example. All
-# artists contain their own transformation, defaulting to the identity
-# transform.
-
-# The signature of a separable transformation instance is
-
-# trans = SeparableTransformation(bbox1, bbox2, funcx, funcy)
-
-# where funcx and funcy operate on x and y. The typical linear
-# coordinate transformation maps one bounding box to another, with funcx
-# and funcy both identity. Eg,
-
-# transData = Transformation(viewLim, displayLim,
-# Func(IDENTITY), Func(IDENTITY))
-
-# maps the axes view limits to display limits. If the xaxis scaling is
-# changed to log, one simply calls
-
-# transData.get_funcx().set_type(LOG10)
-
-# For more general transformations including rotation, the Affine class
-# is provided, which is constructed with 6 LazyValue instances:
-# a, b, c, d, tx, ty. These give the values of the matrix transformation
-
-# [xo = |a c| [xi + [tx
-# yo] |b d| yi] ty]
-
-# where if sx, sy are the scaling components, tx, y are the translation
-# components, and alpha is the rotation
-
-# a = sx*cos(alpha);
-# b = -sx*sin(alpha);
-# c = sy*sin(alpha);
-# d = sy*cos(alpha);
-
-# The affine transformation can be accomplished for row vectors with a
-# single matrix multiplication
-# X_new = X_old * M
-# where
-# M = [ a b 0
-# c d 0
-# tx ty 1]
-# and each X is the row vector [x, y, 1]. Hence M is
-# the transpose of the matrix representation given in
-# http://en.wikipedia.org/wiki/Affine_transformation,
-# which is for the more usual column-vector representation
-# of the position.)
-
-
-
-# From a user perspective, the most important Tranformation methods are
-
-# All transformations
-# -------------------
-# freeze() - eval and freeze the lazy objects
-# thaw() - release the lazy objects
-
-# xy_tup(xy) - transform the tuple (x,y)
-# seq_x_y(x, y) - transform the python sequences x and y
-# numerix_x_y(x, y) - x and y are numerix 1D arrays
-# numerix_xy(xy) - xy is a numerix array of shape (N,2)
-# inverse_numerix_xy(xy)- inverse of the above
-# seq_xy_tups(seq) - seq is a sequence of xy tuples or a (N,2) array
-# inverse_xy_tup(xy) - apply the inverse transformation to tuple xy
-
-# set_offset(xy, trans) - xy is an x,y tuple and trans is a
-# Transformation instance. This will apply a post transformational
-# offset of all future transformations by xt,yt = trans.xy_tup(xy[0], xy[1])
-
-# deepcopy() - returns a deep copy; references are lost
-# shallowcopy() - returns a shallow copy excluding the offset
-
-# Separable transformations
-# -------------------------
-
-# get_bbox1() - return the input bbox
-# get_bbox2() - return the output bbox
-# set_bbox1() - set the input bbox
-# set_bbox2() - set the output bbox
-# get_funcx() - return the Func instance on x
-# get_funcy() - return the Func instance on y
-# set_funcx() - set the Func instance on x
-# set_funcy() - set the Func instance on y
-
-
-# Affine transformations
-# ----------------------
-
-# as_vec6() - return the affine as length 6 list of Values
-
-
-# In general, you shouldn't need to construct your own transformations,
-# but should use the helper functions defined in this module.
-
-
-# zero - return Value(0)
-# one - return Value(1)
-# origin - return Point(zero(), zero())
-# unit_bbox - return the 0,0 to 1,1 bounding box
-# identity_affine - An affine identity transformation
-# identity_transform - An identity separable transformation
-# translation_transform - a pure translational affine
-# scale_transform - a pure scale affine
-# scale_sep_transform - a pure scale separable transformation
-# scale_translation_transform - a scale and translate affine
-# bound_vertices - return the bbox that bounds all the xy tuples
-# bbox_all - return the bbox that bounds all the bboxes
-# lbwh_to_bbox - build a bbox from tuple
-# left, bottom, width, height tuple
-
-# multiply_affines - return the affine that is the matrix product of
-# the two affines
-
-# get_bbox_transform - return a SeparableTransformation instance that
-# transforms one bbox to another
-
-# blend_xy_sep_transform - mix the x and y components of two separable
-# transformations into a new transformation.
-# This allows you to specify x and y in
-# different coordinate systems
-
-# transform_bbox - apply a transformation to a bbox and return the
-# transformed bbox
-
-# inverse_transform_bbox - apply the inverse transformation of a bbox
-# and return the inverse transformed bbox
-
-# offset_copy - make a copy with an offset
-
-
-# The units/transform_unit.py code has many examples.
-
-# A related and partly overlapping class, PBox, has been added to the
-# original transforms module to facilitate Axes repositioning and resizing.
-# At present, the differences between Bbox and PBox include:
-
-# Bbox works with the bounding box, the coordinates of the lower-left
-# and upper-right corners; PBox works with the lower-left coordinates
-# and the width, height pair (left, bottom, width, height, or 'lbwh').
-# Obviously, these are equivalent, but lbwh is what is used by
-# Axes._position, and it is the natural specification for the types of
-# manipulations for which the PBox class was made.
-
-# Bbox uses LazyValues grouped in pairs as 'll' and 'ur' Point objects;
-# PBox uses a 4-element list, subclassed from the python list.
-
-# Bbox and PBox methods are mostly quite different, reflecting their
-# different original purposes. Similarly, the CXX implementation of
-# Bbox is good for methods such as update and for lazy evaluation, but
-# for PBox intended uses, involving very little calculation, pure
-# python probably is adequate.
-
-# In the future we may reimplement the PBox using Bbox
-# and transforms, or eliminate it entirely by adding its methods
-# and attributes to Bbox and/or putting them elsewhere in this module.
-# """
-# from __future__ import division
-# import math
-# import numpy as npy
-
-# from matplotlib._transforms import Value, Point, Interval, Bbox, Affine
-# from matplotlib._transforms import IDENTITY, LOG10, POLAR, Func, FuncXY
-# from matplotlib._transforms import SeparableTransformation
-# from matplotlib._transforms import NonseparableTransformation
-
-# def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True):
-# '''
-# Ensure the endpoints of a range are not too close together.
-
-# "too close" means the interval is smaller than 'tiny' times
-# the maximum absolute value.
-
-# If they are too close, each will be moved by the 'expander'.
-# If 'increasing' is True and vmin > vmax, they will be swapped,
-# regardless of whether they are too close.
-# '''
-# swapped = False
-# if vmax < vmin:
-# vmin, vmax = vmax, vmin
-# swapped = True
-# if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny:
-# if vmin==0.0:
-# vmin = -expander
-# vmax = expander
-# else:
-# vmin -= expander*abs(vmin)
-# vmax += expander*abs(vmax)
-# if swapped and not increasing:
-# vmin, vmax = vmax, vmin
-# return vmin, vmax
-
-
-# def zero(): return Value(0)
-
-# def one() : return Value(1)
-
-# def origin():
-# return Point( zero(), zero() )
-
-# def unit_bbox():
-# """
-# Get a 0,0 -> 1,1 Bbox instance
-# """
-# return Bbox( origin(), Point( one(), one() ) )
-
-# def identity_affine():
-# """
-# Get an affine transformation that maps x,y -> x,y
-# """
-
-# return Affine(one(), zero(), zero(), one(), zero(), zero())
-
-# def identity_transform():
-# """
-# Get an affine transformation that maps x,y -> x,y
-# """
-# return SeparableTransformation(unit_bbox(), unit_bbox(),
-# Func(IDENTITY),
-# Func(IDENTITY))
-
-# def translation_transform(tx, ty):
-# """
-# return a pure tranlational transformation tx and ty are LazyValue
-# instances (Values or binary operations on values)
-# """
-# return Affine(one(), zero(), zero(), one(), tx, ty)
-
-# def scale_transform(sx, sy):
-# """
-# Return a pure scale transformation as an Affine instance; sx and
-# sy are LazyValue instances (Values or binary opertations on
-# values)
-# """
-# return Affine(sx, zero(), zero(), sy, zero(), zero())
-
-# def scale_sep_transform(sx, sy):
-# """
-# Return a pure scale transformation as a SeparableTransformation;
-# sx and sy are LazyValue instances (Values or binary opertations on
-# values)
-# """
-
-# bboxin = unit_bbox()
-# bboxout = Bbox( Point( zero(), zero() ),
-# Point( sx, sy ) )
-# return SeparableTransformation(
-# bboxin, bboxout,
-# Func(IDENTITY), Func(IDENTITY))
-
-
-
-# def bound_vertices(verts):
-# """
-# Return the Bbox of the sequence of x,y tuples in verts
-# """
-# # this is a good candidate to move to _transforms
-# bbox = unit_bbox()
-# bbox.update(verts, 1)
-# return bbox
-
-# def bbox_all(bboxes):
-# """
-# Return the Bbox that bounds all bboxes
-# """
-# # this is a good candidate to move to _transforms
-# assert(len(bboxes))
-
-# if len(bboxes)==1: return bboxes[0]
-
-# bbox = bboxes[0]
-# minx = bbox.xmin()
-# miny = bbox.ymin()
-# maxx = bbox.xmax()
-# maxy = bbox.ymax()
-
-# for bbox in bboxes[1:]:
-# thisminx = bbox.xmin()
-# thisminy = bbox.ymin()
-# thismaxx = bbox.xmax()
-# thismaxy = bbox.ymax()
-
-# if thisminx < minx : minx = thisminx
-# if thismaxx > maxx : maxx = thismaxx
-# if thisminy < miny : miny = thisminy
-# if thismaxy > maxy : maxy = thismaxy
-
-# return Bbox( Point( Value(minx), Value(miny) ),
-# Point( Value(maxx), Value(maxy) )
-# )
-
-# def lbwh_to_bbox(l,b,w,h):
-# return Bbox( Point( Value(l), Value(b)),
-# Point( Value(l+w), Value(b + h) ) )
-
-
-# def invert_vec6(v):
-# """
-# v is a,b,c,d,tx,ty vec6 repr of an affine transformation.
-# Return the inverse of v as a vec6
-# """
-# a,b,c,d,tx,ty = v
-# M = npy.array([ [a,b,0], [c,d,0], [tx,ty,1]], dtype=npy.float64)
-# Mi = npy.linalg.inv(M)
-# return Mi[:,:2].ravel()
-
-# def multiply_affines( v1, v2):
-# """
-# v1 and v2 are Affine instances
-# """
-
-# a1, b1, c1, d1, tx1, ty1 = v1.as_vec6()
-# a2, b2, c2, d2, tx2, ty2 = v2.as_vec6()
-
-# a = a1 * a2 + c1 * b2
-# b = b1 * a2 + d1 * b2
-# c = a1 * c2 + c1 * d2
-# d = b1 * c2 + d1 * d2
-# tx = a1 * tx2 + c1 * ty2 + tx1
-# ty = b1 * tx2 + d1 * ty2 + ty1
-# return Affine(a,b,c,d,tx,ty)
-
-# def get_bbox_transform(boxin, boxout):
-# """
-# return the transform that maps transform one bounding box to
-# another
-# """
-# return SeparableTransformation(
-# boxin, boxout, Func(IDENTITY), Func( IDENTITY))
-
-
-# def blend_xy_sep_transform(trans1, trans2):
-# """
-# If trans1 and trans2 are SeparableTransformation instances, you can
-# build a new SeparableTransformation from them by extracting the x and y
-# bounding points and functions and recomposing a new SeparableTransformation
-
-# This function extracts all the relevant bits from trans1 and
-# trans2 and returns the new Transformation instance. This is
-# useful, for example, if you want to specify x in data coordinates
-# and y in axes coordinates.
-# """
-
-# inboxx = trans1.get_bbox1()
-# inboxy = trans2.get_bbox1()
-
-# outboxx = trans1.get_bbox2()
-# outboxy = trans2.get_bbox2()
-
-# xminIn = inboxx.ll().x()
-# xmaxIn = inboxx.ur().x()
-# xminOut = outboxx.ll().x()
-# xmaxOut = outboxx.ur().x()
-
-# yminIn = inboxy.ll().y()
-# ymaxIn = inboxy.ur().y()
-# yminOut = outboxy.ll().y()
-# ymaxOut = outboxy.ur().y()
-
-# funcx = trans1.get_funcx()
-# funcy = trans2.get_funcy()
-
-# boxin = Bbox( Point(xminIn, yminIn), Point(xmaxIn, ymaxIn) )
-# boxout = Bbox( Point(xminOut, yminOut), Point(xmaxOut, ymaxOut) )
-
-# return SeparableTransformation(boxin, boxout, funcx, funcy)
-
-
-# def transform_bbox(trans, bbox):
-# 'transform the bbox to a new bbox'
-# xmin, xmax = bbox.intervalx().get_bounds()
-# ymin, ymax = bbox.intervaly().get_bounds()
-
-# xmin, ymin = trans.xy_tup((xmin, ymin))
-# xmax, ymax = trans.xy_tup((xmax, ymax))
-
-# return Bbox(Point(Value(xmin), Value(ymin)),
-# Point(Value(xmax), Value(ymax)))
-
-# def inverse_transform_bbox(trans, bbox):
-# 'inverse transform the bbox'
-# xmin, xmax = bbox.intervalx().get_bounds()
-# ymin, ymax = bbox.intervaly().get_bounds()
-
-# xmin, ymin = trans.inverse_xy_tup((xmin, ymin))
-# xmax, ymax = trans.inverse_xy_tup((xmax, ymax))
-# return Bbox(Point(Value(xmin), Value(ymin)),
-# Point(Value(xmax), Value(ymax)))
-
-
-# def offset_copy(trans, fig=None, x=0, y=0, units='inches'):
-# '''
-# Return a shallow copy of a transform with an added offset.
-# args:
-# trans is any transform
-# kwargs:
-# fig is the current figure; it can be None if units are 'dots'
-# x, y give the offset
-# units is 'inches', 'points' or 'dots'
-# '''
-# newtrans = trans.shallowcopy()
-# if units == 'dots':
-# newtrans.set_offset((x,y), identity_transform())
-# return newtrans
-# if fig is None:
-# raise ValueError('For units of inches or points a fig kwarg is needed')
-# if units == 'points':
-# x /= 72.0
-# y /= 72.0
-# elif not units == 'inches':
-# raise ValueError('units must be dots, points, or inches')
-# tx = Value(x) * fig.dpi
-# ty = Value(y) * fig.dpi
-# newtrans.set_offset((0,0), translation_transform(tx, ty))
-# return newtrans
-
-
-# def get_vec6_scales(v):
-# 'v is an affine vec6 a,b,c,d,tx,ty; return sx, sy'
-# a,b,c,d = v[:4]
-# sx = math.sqrt(a**2 + b**2)
-# sy = math.sqrt(c**2 + d**2)
-# return sx, sy
-
-# def get_vec6_rotation(v):
-# 'v is an affine vec6 a,b,c,d,tx,ty; return rotation in degrees'
-# sx, sy = get_vec6_scales(v)
-# c,d = v[2:4]
-# angle = math.atan2(c,d)/math.pi*180
-# return angle
-
-
-
-# class PBox(list):
-# '''
-# A left-bottom-width-height (lbwh) specification of a bounding box,
-# such as is used to specify the position of an Axes object within
-# a Figure.
-# It is a 4-element list with methods for changing the size, shape,
-# and position relative to its container.
-# '''
-# coefs = {'C': (0.5, 0.5),
-# 'SW': (0,0),
-# 'S': (0.5, 0),
-# 'SE': (1.0, 0),
-# 'E': (1.0, 0.5),
-# 'NE': (1.0, 1.0),
-# 'N': (0.5, 1.0),
-# 'NW': (0, 1.0),
-# 'W': (0, 0.5)}
-# def __init__(self, box, container=None, llur=False):
-# if len(box) != 4:
-# raise ValueError("Argument must be iterable of length 4")
-# if llur:
-# box = [box[0], box[1], box[2]-box[0], box[3]-box[1]]
-# list.__init__(self, box)
-# self.set_container(container)
-
-# def as_llur(self):
-# return [self[0], self[1], self[0]+self[2], self[1]+self[3]]
-
-# def set_container(self, box=None):
-# if box is None:
-# box = self
-# if len(box) != 4:
-# raise ValueError("Argument must be iterable of length 4")
-# self._container = list(box)
-
-# def get_container(self, box):
-# return self._container
-
-# def anchor(self, c, container=None):
-# '''
-# Shift to position c within its container.
-
-# c can be a sequence (cx, cy) where cx, cy range from 0 to 1,
-# where 0 is left or bottom and 1 is right or top.
-
-# Alternatively, c can be a string: C for centered,
-# S for bottom-center, SE for bottom-left, E for left, etc.
-
-# Optional arg container is the lbwh box within which the
-# PBox is positioned; it defaults to the initial
-# PBox.
-# '''
-# if container is None:
-# container = self._container
-# l,b,w,h = container
-# if isinstance(c, str):
-# cx, cy = self.coefs[c]
-# else:
-# cx, cy = c
-# W,H = self[2:]
-# self[:2] = l + cx * (w-W), b + cy * (h-H)
-# return self
-
-# def shrink(self, mx, my):
-# '''
-# Shrink the box by mx in the x direction and my in the y direction.
-# The lower left corner of the box remains unchanged.
-# Normally mx and my will be <= 1, but this is not enforced.
-# '''
-# assert mx >= 0 and my >= 0
-# self[2:] = mx * self[2], my * self[3]
-# return self
-
-# def shrink_to_aspect(self, box_aspect, fig_aspect = 1):
-# '''
-# Shrink the box so that it is as large as it can be while
-# having the desired aspect ratio, box_aspect.
-# If the box coordinates are relative--that is, fractions of
-# a larger box such as a figure--then the physical aspect
-# ratio of that figure is specified with fig_aspect, so
-# that box_aspect can also be given as a ratio of the
-# absolute dimensions, not the relative dimensions.
-# '''
-# assert box_aspect > 0 and fig_aspect > 0
-# l,b,w,h = self._container
-# H = w * box_aspect/fig_aspect
-# if H <= h:
-# W = w
-# else:
-# W = h * fig_aspect/box_aspect
-# H = h
-# self[2:] = W,H
-# return self
-
-# def splitx(self, *args):
-# '''
-# e.g., PB.splitx(f1, f2, ...)
-
-# Returns a list of new PBoxes formed by
-# splitting the original one (PB) with vertical lines
-# at fractional positions f1, f2, ...
-# '''
-# boxes = []
-# xf = [0] + list(args) + [1]
-# l,b,w,h = self[:]
-# for xf0, xf1 in zip(xf[:-1], xf[1:]):
-# boxes.append(PBox([l+xf0*w, b, (xf1-xf0)*w, h]))
-# return boxes
-
-# def splity(self, *args):
-# '''
-# e.g., PB.splity(f1, f2, ...)
-
-# Returns a list of new PBoxes formed by
-# splitting the original one (PB) with horizontal lines
-# at fractional positions f1, f2, ..., with y measured
-# positive up.
-# '''
-# boxes = []
-# yf = [0] + list(args) + [1]
-# l,b,w,h = self[:]
-# for yf0, yf1 in zip(yf[:-1], yf[1:]):
-# boxes.append(PBox([l, b+yf0*h, w, (yf1-yf0)*h]))
-# return boxes
-
-
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-14 12:23:11
|
Revision: 3849
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3849&view=rev
Author: mdboom
Date: 2007-09-14 05:23:06 -0700 (Fri, 14 Sep 2007)
Log Message:
-----------
Committing this file so I can rename it
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-13 18:00:10 UTC (rev 3848)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-14 12:23:06 UTC (rev 3849)
@@ -847,12 +847,8 @@
# assert (tpoints1 == tpoints2).all()
# Here are some timing tests
- points = [(random(), random()) for i in xrange(10000)]
+ points = npy.asarray([(random(), random()) for i in xrange(10000)])
t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points")
- print "Time to transform 10000 x 10 points as tuples:", t.timeit(10)
-
- points2 = npy.asarray(points)
- t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, points2")
- print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10)
+ print "Time to transform 10000 x 10 points:", t.timeit(10)
__all__ = ['Transform', 'Affine2D']
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-13 18:00:12
|
Revision: 3848
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3848&view=rev
Author: mdboom
Date: 2007-09-13 11:00:10 -0700 (Thu, 13 Sep 2007)
Log Message:
-----------
New milestone -- resizing figure window works. shared_axis_demo.py
works. (Uses callbacks to track changes between axes's).
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/lib/matplotlib/artist.py
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/figure.py
branches/transforms/lib/matplotlib/pyplot.py
branches/transforms/lib/matplotlib/text.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -20,284 +20,461 @@
class TransformNode(object):
def __init__(self):
- self._parents = Set()
-
+ self._parents = Set()
+
def invalidate(self):
- if not self._do_invalidation():
- for parent in self._parents:
- parent.invalidate()
+ if not self._do_invalidation():
+ for parent in self._parents:
+ parent.invalidate()
def _do_invalidation(self):
- return False
-
- def add_children(self, children):
- for child in children:
- child._parents.add(self)
+ return False
-class Bbox(TransformNode):
+ def set_children(self, children):
+ for child in children:
+ getattr(self, child)._parents.add(self)
+ self._children = children
+
+# def replace_child(self, index, child):
+# children = self._children
+# getattr(self, children[index])._parents.remove(self)
+# setattr(self, children[index], child)
+# # We have to reset children in case two or more
+# # of the children are the same
+# for child in children:
+# getattr(self, child)._parents.add(self)
+# self.invalidate()
+
+class BboxBase(TransformNode):
+ '''
+ This is the read-only part of a bounding-box
+ '''
+
+ def __init__(self):
+ TransformNode.__init__(self)
+
+ def __array__(self):
+ return self.get_points()
+
+ # MGDTODO: Probably a more efficient ways to do this...
+ def _get_xmin(self):
+ return self.get_points()[0, 0]
+ xmin = property(_get_xmin)
+
+ def _get_ymin(self):
+ return self.get_points()[0, 1]
+ ymin = property(_get_ymin)
+
+ def _get_xmax(self):
+ return self.get_points()[1, 0]
+ xmax = property(_get_xmax)
+
+ def _get_ymax(self):
+ return self.get_points()[1, 1]
+ ymax = property(_get_ymax)
+
+ def _get_min(self):
+ return self.get_points()[0]
+ min = property(_get_min)
+
+ def _get_max(self):
+ return self.get_points()[1]
+ max = property(_get_max)
+
+ def _get_intervalx(self):
+ return self.get_points()[:, 0]
+ intervalx = property(_get_intervalx)
+
+ def _get_intervaly(self):
+ return self.get_points()[:, 1]
+ intervaly = property(_get_intervaly)
+
+ def _get_width(self):
+ return self.xmax - self.xmin
+ width = property(_get_width)
+
+ def _get_height(self):
+ return self.ymax - self.ymin
+ height = property(_get_height)
+
+ def _get_bounds(self):
+ return (self.xmin, self.ymin,
+ self.xmax - self.xmin, self.ymax - self.ymin)
+ bounds = property(_get_bounds)
+
+ def get_points(self):
+ return NotImplementedError()
+
+ # MGDTODO: Optimize
+ def containsx(self, x):
+ return x >= self.xmin and x <= self.xmax
+
+ def containsy(self, y):
+ return y >= self.ymin and y <= self.ymax
+
+ def contains(self, x, y):
+ return self.containsx(x) and self.containsy(y)
+
+ def overlapsx(self, other):
+ return self.containsx(other.xmin) \
+ or self.containsx(other.xmax)
+
+ def overlapsy(self, other):
+ return self.containsy(other.ymin) \
+ or self.containsx(other.ymax)
+
+ def overlaps(self, other):
+ return self.overlapsx(other) \
+ and self.overlapsy(other)
+
+ def fully_containsx(self, x):
+ return x > self.xmin and x < self.xmax
+
+ def fully_containsy(self, y):
+ return y > self.ymin and y < self.ymax
+
+ def fully_contains(self, x, y):
+ return self.fully_containsx(x) \
+ and self.fully_containsy(y)
+
+ def fully_overlapsx(self, other):
+ return self.fully_containsx(other.xmin) \
+ or self.fully_containsx(other.xmax)
+
+ def fully_overlapsy(self, other):
+ return self.fully_containsy(other.ymin) \
+ or self.fully_containsx(other.ymax)
+
+ def fully_overlaps(self, other):
+ return self.fully_overlapsx(other) and \
+ self.fully_overlapsy(other)
+
+
+class Bbox(BboxBase):
def __init__(self, points):
- TransformNode.__init__(self)
- self._points = npy.asarray(points, npy.float_)
- self.track = False
+ BboxBase.__init__(self)
+ self._points = npy.asarray(points, npy.float_)
#@staticmethod
def unit():
- return Bbox.from_lbrt(0., 0., 1., 1.)
+ return Bbox.from_lbrt(0., 0., 1., 1.)
unit = staticmethod(unit)
#@staticmethod
def from_lbwh(left, bottom, width, height):
- return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
+ return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
from_lbwh = staticmethod(from_lbwh)
#@staticmethod
def from_lbrt(*args):
- points = npy.array(args, dtype=npy.float_).reshape(2, 2)
- return Bbox(points)
+ points = npy.array(args, dtype=npy.float_).reshape(2, 2)
+ return Bbox(points)
from_lbrt = staticmethod(from_lbrt)
def __copy__(self):
- return Bbox(self._points.copy())
+ return Bbox(self._points.copy())
def __deepcopy__(self, memo):
- return Bbox(self._points.copy())
+ return Bbox(self._points.copy())
def __cmp__(self, other):
- # MGDTODO: Totally suboptimal
- if isinstance(other, Bbox) and (self._points == other._points).all():
- return 0
- return -1
+ # MGDTODO: Totally suboptimal
+ if isinstance(other, Bbox) and (self._points == other._points).all():
+ return 0
+ return -1
def __repr__(self):
- return 'Bbox(%s)' % repr(self._points)
+ return 'Bbox(%s)' % repr(self._points)
__str__ = __repr__
- def __array__(self):
- return self._points
-
# JDH: the update method will update the box limits from the
# existing limits and the new data; it appears here you are just
# using the new data. We use an "ignore" flag to specify whether
# you want to include the existing data or not in the update
def update_from_data(self, x, y, ignore=True):
- self._points = npy.array([[x.min(), y.min()], [x.max(), y.max()]], npy.float_)
- self.invalidate()
+ self._points = npy.array(
+ [[x.min(), y.min()], [x.max(), y.max()]],
+ npy.float_)
+ self.invalidate()
# MGDTODO: Probably a more efficient ways to do this...
- def _get_xmin(self):
- return self._points[0, 0]
def _set_xmin(self, val):
- self._points[0, 0] = val
- self.invalidate()
- xmin = property(_get_xmin, _set_xmin)
+ self._points[0, 0] = val
+ self.invalidate()
+ xmin = property(BboxBase._get_xmin, _set_xmin)
- def _get_ymin(self):
- return self._points[0, 1]
def _set_ymin(self, val):
- self._points[0, 1] = val
- self.invalidate()
- ymin = property(_get_ymin, _set_ymin)
+ self._points[0, 1] = val
+ self.invalidate()
+ ymin = property(BboxBase._get_ymin, _set_ymin)
- def _get_xmax(self):
- return self._points[1, 0]
def _set_xmax(self, val):
- self._points[1, 0] = val
- self.invalidate()
- xmax = property(_get_xmax, _set_xmax)
+ self._points[1, 0] = val
+ self.invalidate()
+ xmax = property(BboxBase._get_xmax, _set_xmax)
- def _get_ymax(self):
- return self._points[1, 1]
def _set_ymax(self, val):
- self._points[1, 1] = val
- self.invalidate()
- ymax = property(_get_ymax, _set_ymax)
+ self._points[1, 1] = val
+ self.invalidate()
+ ymax = property(BboxBase._get_ymax, _set_ymax)
- def _get_min(self):
- return self._points[0]
def _set_min(self, val):
- self._points[0] = val
- self.invalidate()
- min = property(_get_min, _set_min)
+ self._points[0] = val
+ self.invalidate()
+ min = property(BboxBase._get_min, _set_min)
- def _get_max(self):
- return self._points[1]
def _set_max(self, val):
- self._points[1] = val
- self.invalidate()
- max = property(_get_max, _set_max)
+ self._points[1] = val
+ self.invalidate()
+ max = property(BboxBase._get_max, _set_max)
- def _get_intervalx(self):
- return self._points[:,0]
def _set_intervalx(self, interval):
- self._points[:,0] = interval
- self.invalidate()
- intervalx = property(_get_intervalx, _set_intervalx)
+ self._points[:, 0] = interval
+ self.invalidate()
+ intervalx = property(BboxBase._get_intervalx, _set_intervalx)
- def _get_intervaly(self):
- return self._points[:,1]
def _set_intervaly(self, interval):
- self._points[:,1] = interval
- self.invalidate()
- intervaly = property(_get_intervaly, _set_intervaly)
+ self._points[:, 1] = interval
+ self.invalidate()
+ intervaly = property(BboxBase._get_intervaly, _set_intervaly)
- def _get_width(self):
- return self.xmax - self.xmin
- width = property(_get_width)
+ def _set_bounds(self, bounds):
+ l,b,w,h = bounds
+ self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
+ self.invalidate()
+ bounds = property(BboxBase._get_bounds, _set_bounds)
- def _get_height(self):
- return self.ymax - self.ymin
- height = property(_get_height)
+ def get_points(self):
+ return self._points
- def _get_bounds(self):
- return (self.xmin, self.ymin,
- self.xmax - self.xmin, self.ymax - self.ymin)
- def _set_bounds(self, bounds):
- l,b,w,h = bounds
- self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
- self.invalidate()
- bounds = property(_get_bounds, _set_bounds)
-
+ def set_points(self, points):
+ self._points = points
+ self.invalidate()
+
+ def set(self, other):
+ self._points = other.get_points()
+ self.invalidate()
+
def transformed(self, transform):
- return Bbox(transform(self._points))
+ return Bbox(transform(self._points))
def inverse_transformed(self, transform):
- return Bbox(transform.inverted()(self._points))
+ return Bbox(transform.inverted()(self._points))
def expanded(self, sw, sh):
- width = self.width
- height = self.height
- deltaw = (sw * width - width) / 2.0
- deltah = (sh * height - height) / 2.0
- a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
- return Bbox(self._points + a)
+ width = self.width
+ height = self.height
+ deltaw = (sw * width - width) / 2.0
+ deltah = (sh * height - height) / 2.0
+ a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
+ return Bbox(self._points + a)
- def contains(self, x, y):
- return (x >= self.xmin and x <= self.xmax and
- y >= self.ymin and y <= self.ymax)
-
#@staticmethod
def union(bboxes):
- """
- Return the Bbox that bounds all bboxes
- """
- assert(len(bboxes))
+ """
+ Return the Bbox that bounds all bboxes
+ """
+ assert(len(bboxes))
- if len(bboxes) == 1:
- return bboxes[0]
+ if len(bboxes) == 1:
+ return bboxes[0]
- bbox = bboxes[0]
- xmin = bbox.xmin
- ymin = bbox.ymin
- xmax = bbox.xmax
- ymax = bbox.ymax
+ bbox = bboxes[0]
+ xmin = bbox.xmin
+ ymin = bbox.ymin
+ xmax = bbox.xmax
+ ymax = bbox.ymax
- for bbox in bboxes[1:]:
- xmin = min(xmin, bbox.xmin)
- ymin = min(ymin, bbox.ymin)
- xmax = max(xmax, bbox.xmax)
- ymax = max(ymax, bbox.ymax)
+ for bbox in bboxes[1:]:
+ xmin = min(xmin, bbox.xmin)
+ ymin = min(ymin, bbox.ymin)
+ xmax = max(xmax, bbox.xmax)
+ ymax = max(ymax, bbox.ymax)
- return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
+ return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
union = staticmethod(union)
+
+class TransformedBbox(BboxBase):
+ def __init__(self, bbox, transform):
+ assert isinstance(bbox, Bbox)
+ assert isinstance(transform, Transform)
+
+ BboxBase.__init__(self)
+ self.bbox = bbox
+ self.transform = transform
+ self.set_children(['bbox', 'transform'])
+ self._points = None
+
+ def __repr__(self):
+ return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
+ __str__ = __repr__
+ def _do_invalidation(self):
+ self._points = None
+
+ def get_points(self):
+ if self._points is None:
+ self._points = self.transform(self.bbox.get_points())
+ return self._points
+
+# MGDTODO: This code probably works, but I don't think it's a good idea
+# (from a code clarity perspective)
+# class BlendedBbox(BboxBase):
+# def __init__(self, bbox_x, bbox_y):
+# assert isinstance(bbox_x, BboxBase)
+# assert isinstance(bbox_y, BboxBase)
+
+# BboxBase.__init__(self)
+# self._x = bbox_x
+# self._y = bbox_y
+# self.set_children(['_x', '_y'])
+# self._points = None
+
+# def __repr__(self):
+# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
+# __str__ = __repr__
+
+# def _do_invalidation(self):
+# self._points = None
+
+# def get_points(self):
+# if self._points is None:
+# # MGDTODO: Optimize
+# if self._x == self._y:
+# self._points = self._x.get_points()
+# else:
+# x_points = self._x.get_points()
+# y_points = self._y.get_points()
+# self._points = npy.array(
+# [[x_points[0,0], y_points[0,1]],
+# [x_points[1,0], y_points[1,1]]],
+# npy.float_)
+# return self._points
+
+# def _set_intervalx(self, pair):
+# # MGDTODO: Optimize
+# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]])
+# self.replace_child(0, bbox)
+# intervalx = property(BboxBase._get_intervalx, _set_intervalx)
+
+# def _set_intervaly(self, pair):
+# # MGDTODO: Optimize
+# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]])
+# self.replace_child(1, bbox)
+# intervaly = property(BboxBase._get_intervaly, _set_intervaly)
+
class Transform(TransformNode):
def __init__(self):
- TransformNode.__init__(self)
+ TransformNode.__init__(self)
def __call__(self, points):
- raise NotImplementedError()
+ raise NotImplementedError()
def __add__(self, other):
- if isinstance(other, Transform):
- return composite_transform_factory(self, other)
- raise TypeError("Can not add Transform to object of type '%s'" % type(other))
+ if isinstance(other, Transform):
+ return composite_transform_factory(self, other)
+ raise TypeError(
+ "Can not add Transform to object of type '%s'" % type(other))
def __radd__(self, other):
- if isinstance(other, Transform):
- return composite_transform_factory(other, self)
- raise TypeError("Can not add Transform to object of type '%s'" % type(other))
+ if isinstance(other, Transform):
+ return composite_transform_factory(other, self)
+ raise TypeError(
+ "Can not add Transform to object of type '%s'" % type(other))
def transform_point(self, point):
- return self.__call__(npy.asarray([point]))[0]
+ return self.__call__(npy.asarray([point]))[0]
def has_inverse(self):
- raise NotImplementedError()
+ raise NotImplementedError()
def inverted(self):
- raise NotImplementedError()
+ raise NotImplementedError()
def is_separable(self):
- return False
+ return False
def is_affine(self):
- return False
+ return False
class Affine2DBase(Transform):
input_dims = 2
output_dims = 2
def __init__(self):
- Transform.__init__(self)
- self._inverted = None
+ Transform.__init__(self)
+ self._inverted = None
def _do_invalidation(self):
- result = self._inverted is None
- self._inverted = None
- return result
+ result = self._inverted is None
+ self._inverted = None
+ return result
#@staticmethod
def _concat(a, b):
return npy.dot(b, a)
_concat = staticmethod(_concat)
+
+ #@staticmethod
+ def concat(a, b):
+ return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix()))
+ concat = staticmethod(concat)
def to_values(self):
- mtx = self.get_matrix()
- return tuple(mtx[:2].swapaxes(0, 1).flatten())
+ mtx = self.get_matrix()
+ return tuple(mtx[:2].swapaxes(0, 1).flatten())
#@staticmethod
def matrix_from_values(a, b, c, d, e, f):
- affine = npy.zeros((3,3), npy.float_)
- affine[0,] = a, c, e
- affine[1,] = b, d, f
- affine[2,2] = 1
- return affine
+ affine = npy.zeros((3, 3), npy.float_)
+ affine[0, ] = a, c, e
+ affine[1, ] = b, d, f
+ affine[2, 2] = 1
+ return affine
matrix_from_values = staticmethod(matrix_from_values)
def get_matrix(self):
- raise NotImplementedError()
+ raise NotImplementedError()
def __call__(self, points):
"""
Applies the transformation to an array of 2D points and
- returns the result.
+ returns the result.
- points must be a numpy array of shape (N, 2), where N is the
- number of points.
- """
- # MGDTODO: The major speed trap here is just converting to
- # the points to an array in the first place. If we can use
- # more arrays upstream, that should help here.
- mtx = self.get_matrix()
- points = npy.asarray(points, npy.float_)
- points = points.transpose()
- points = npy.dot(mtx[0:2, 0:2], points)
- points = points + mtx[0:2, 2:]
- return points.transpose()
+ points must be a numpy array of shape (N, 2), where N is the
+ number of points.
+ """
+ # MGDTODO: The major speed trap here is just converting to
+ # the points to an array in the first place. If we can use
+ # more arrays upstream, that should help here.
+ if not isinstance(points, npy.ndarray):
+ import traceback
+ print '-' * 60
+ print 'A non-numpy array was passed in for transformation. Please '
+ print 'correct this.'
+ print "".join(traceback.format_stack())
+ print points
+ mtx = self.get_matrix()
+ points = npy.asarray(points, npy.float_)
+ points = points.transpose()
+ points = npy.dot(mtx[0:2, 0:2], points)
+ points = points + mtx[0:2, 2:]
+ return points.transpose()
def inverted(self):
- if self._inverted is None:
- mtx = self.get_matrix()
- self._inverted = Affine2D(inv(mtx))
- return self._inverted
+ if self._inverted is None:
+ mtx = self.get_matrix()
+ self._inverted = Affine2D(inv(mtx))
+ return self._inverted
def is_separable(self):
- mtx = self.get_matrix()
- return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
+ mtx = self.get_matrix()
+ return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
def is_affine(self):
- return True
+ return True
-
+
class Affine2D(Affine2DBase):
input_dims = 2
output_dims = 2
@@ -310,29 +487,29 @@
b d f
0 0 1
"""
- Affine2DBase.__init__(self)
- if matrix is None:
- matrix = npy.identity(3)
- else:
- assert matrix.shape == (3, 3)
- self._mtx = matrix
- self._inverted = None
+ Affine2DBase.__init__(self)
+ if matrix is None:
+ matrix = npy.identity(3)
+ else:
+ assert matrix.shape == (3, 3)
+ self._mtx = matrix
+ self._inverted = None
def __repr__(self):
- return "Affine2D(%s)" % repr(self._mtx)
+ return "Affine2D(%s)" % repr(self._mtx)
__str__ = __repr__
def __cmp__(self, other):
- if (isinstance(other, Affine2D) and
- (self.get_matrix() == other.get_matrix()).all()):
- return 0
- return -1
+ if (isinstance(other, Affine2D) and
+ (self.get_matrix() == other.get_matrix()).all()):
+ return 0
+ return -1
def __copy__(self):
- return Affine2D(self._mtx.copy())
+ return Affine2D(self._mtx.copy())
def __deepcopy__(self, memo):
- return Affine2D(self._mtx.copy())
+ return Affine2D(self._mtx.copy())
#@staticmethod
def from_values(a, b, c, d, e, f):
@@ -340,209 +517,224 @@
from_values = staticmethod(from_values)
def get_matrix(self):
- return self._mtx
+ return self._mtx
+
+ def set_matrix(self, mtx):
+ self._mtx = mtx
+ self.invalidate()
+
+ def set(self, other):
+ self._mtx = other.get_matrix()
+ self.invalidate()
#@staticmethod
- def concat(a, b):
- return Affine2D(Affine2D._concat(a._mtx, b._mtx))
- concat = staticmethod(concat)
-
- #@staticmethod
def identity():
return Affine2D(npy.identity(3))
identity = staticmethod(identity)
+ def clear(self):
+ self._mtx = npy.identity(3)
+ self.invalidate()
+ return self
+
def rotate(self, theta):
a = npy.cos(theta)
b = npy.sin(theta)
rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0)
self._mtx = self._concat(self._mtx, rotate_mtx)
- self.invalidate()
- return self
+ self.invalidate()
+ return self
def rotate_deg(self, degrees):
return self.rotate(degrees*npy.pi/180.)
+ def rotate_around(self, x, y, theta):
+ return self.translate(-x, -y).rotate(theta).translate(x, y)
+
+ def rotate_deg_around(self, x, y, degrees):
+ return self.translate(-x, -y).rotate_deg(degrees).translate(x, y)
+
def translate(self, tx, ty):
translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty)
self._mtx = self._concat(self._mtx, translate_mtx)
- self.invalidate()
- return self
+ self.invalidate()
+ return self
def scale(self, sx, sy=None):
- if sy is None:
- sy = sx
- scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.)
+ if sy is None:
+ sy = sx
+ scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.)
self._mtx = self._concat(self._mtx, scale_mtx)
- self.invalidate()
- return self
+ self.invalidate()
+ return self
def inverted(self):
- if self._inverted is None:
- mtx = self.get_matrix()
- self._inverted = Affine2D(inv(mtx))
- return self._inverted
+ if self._inverted is None:
+ mtx = self.get_matrix()
+ self._inverted = Affine2D(inv(mtx))
+ return self._inverted
def is_separable(self):
- mtx = self.get_matrix()
- return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
+ mtx = self.get_matrix()
+ return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
def is_affine(self):
- return True
+ return True
-class BlendedAffine2D(Affine2DBase):
+class BlendedTransform(Transform):
def __init__(self, x_transform, y_transform):
- assert x_transform.is_affine()
- assert y_transform.is_affine()
- assert x_transform.is_separable()
- assert y_transform.is_separable()
+ assert x_transform.is_separable()
+ assert y_transform.is_separable()
- Affine2DBase.__init__(self)
- self.add_children([x_transform, y_transform])
- self._x = x_transform
- self._y = y_transform
- self._mtx = None
+ Transform.__init__(self)
+ self._x = x_transform
+ self._y = y_transform
+ self.set_children(['_x', '_y'])
+ def __call__(self, points):
+ if self._x == self._y:
+ return self._x(points)
+
+ x_points = self._x(points)
+ y_points = self._y(points)
+ # This works because we already know the transforms are
+ # separable
+ return npy.hstack((x_points[:, 0:1], y_points[:, 1:2]))
+
+# def set_x_transform(self, x_transform):
+# self.replace_child(0, x_transform)
+
+# def set_y_transform(self, y_transform):
+# self.replace_child(1, y_transform)
+
+class BlendedAffine2D(Affine2DBase, BlendedTransform):
+ def __init__(self, x_transform, y_transform):
+ assert x_transform.is_affine()
+ assert y_transform.is_affine()
+ assert x_transform.is_separable()
+ assert y_transform.is_separable()
+ BlendedTransform.__init__(self, x_transform, y_transform)
+
+ Affine2DBase.__init__(self)
+ self._mtx = None
+
def __repr__(self):
- return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
+ return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
__str__ = __repr__
-
+
def _do_invalidation(self):
- if self._mtx is not None:
- self._mtx = None
- Affine2DBase._do_invalidation(self)
- return False
- return True
+ if self._mtx is not None:
+ self._mtx = None
+ Affine2DBase._do_invalidation(self)
+ return False
+ return True
- def _make__mtx(self):
- if self._mtx is None:
- x_mtx = self._x.get_matrix()
- y_mtx = self._y.get_matrix()
- # This works because we already know the transforms are
- # separable, though normally one would want to set b and
- # c to zero.
- self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
-
def is_separable(self):
- return True
+ return True
def get_matrix(self):
- self._make__mtx()
- return self._mtx
+ if self._mtx is None:
+ if self._x == self._y:
+ self._mtx = self._x.get_matrix()
+ else:
+ x_mtx = self._x.get_matrix()
+ y_mtx = self._y.get_matrix()
+ # This works because we already know the transforms are
+ # separable, though normally one would want to set b and
+ # c to zero.
+ self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
+ return self._mtx
+
+class CompositeTransform(Transform):
+ def __init__(self, a, b):
+ assert a.output_dims == b.input_dims
+ self.input_dims = a.input_dims
+ self.output_dims = b.output_dims
+
+ Transform.__init__(self)
+ self._a = a
+ self._b = b
+ self.set_children(['_a', '_b'])
+
+ def __call__(self, points):
+ return self._b(self._a(points))
-class BlendedTransform(Transform):
- def __init__(self, x_transform, y_transform):
- assert x_transform.is_separable()
- assert y_transform.is_separable()
-
- Transform.__init__(self)
- self.add_children([x_transform, y_transform])
- self._x = x_transform
- self._y = y_transform
-
- def __call__(self, points):
- x_points = self._x(points)
- y_points = self._y(points)
- # This works because we already know the transforms are
- # separable
- return npy.hstack((x_points[:, 0:1], y_points[:, 1:2]))
-
class CompositeAffine2D(Affine2DBase):
def __init__(self, a, b):
- assert a.is_affine()
- assert b.is_affine()
+ assert a.is_affine()
+ assert b.is_affine()
- Affine2DBase.__init__(self)
- self.add_children([a, b])
- self._a = a
- self._b = b
- self._mtx = None
+ Affine2DBase.__init__(self)
+ self._a = a
+ self._b = b
+ self.set_children(['_a', '_b'])
+ self._mtx = None
def __repr__(self):
- return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
+ return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
__str__ = __repr__
def _do_invalidation(self):
- self._mtx = None
- Affine2DBase._do_invalidation(self)
+ self._mtx = None
+ Affine2DBase._do_invalidation(self)
- def _make__mtx(self):
- if self._mtx is None:
- self._mtx = self._concat(
- self._a.get_matrix(),
- self._b.get_matrix())
-
def get_matrix(self):
- self._make__mtx()
- return self._mtx
-
-class CompositeTransform(Transform):
- def __init__(self, a, b):
- assert a.output_dims == b.input_dims
- self.input_dims = a.input_dims
- self.output_dims = b.output_dims
-
- Transform.__init__(self)
- self.add_children([a, b])
- self._a = a
- self._b = b
+ if self._mtx is None:
+ self._mtx = self._concat(
+ self._a.get_matrix(),
+ self._b.get_matrix())
+ return self._mtx
- def __call__(self, points):
- return self._b(self._a(points))
-
class BboxTransform(Affine2DBase):
def __init__(self, boxin, boxout):
- assert isinstance(boxin, Bbox)
- assert isinstance(boxout, Bbox)
+ assert isinstance(boxin, BboxBase)
+ assert isinstance(boxout, BboxBase)
- Affine2DBase.__init__(self)
- self.add_children([boxin, boxout])
- self._boxin = boxin
- self._boxout = boxout
- self._mtx = None
- self._inverted = None
+ Affine2DBase.__init__(self)
+ self._boxin = boxin
+ self._boxout = boxout
+ self.set_children(['_boxin', '_boxout'])
+ self._mtx = None
+ self._inverted = None
def __repr__(self):
- return "BboxTransform(%s, %s)" % (self._boxin, self._boxout)
+ return "BboxTransform(%s, %s)" % (self._boxin, self._boxout)
__str__ = __repr__
-
+
def _do_invalidation(self):
- if self._mtx is not None:
- self._mtx = None
- Affine2DBase._do_invalidation(self)
- return False
- return True
+ if self._mtx is not None:
+ self._mtx = None
+ Affine2DBase._do_invalidation(self)
+ return False
+ return True
- def _make__mtx(self):
- if self._mtx is None:
- boxin = self._boxin
- boxout = self._boxout
- x_scale = boxout.width / boxin.width
- y_scale = boxout.height / boxin.height
+ def is_separable(self):
+ return True
- # MGDTODO: Optimize
- affine = Affine2D() \
- .translate(-boxin.xmin, -boxin.ymin) \
- .scale(x_scale, y_scale) \
- .translate(boxout.xmin, boxout.ymin)
+ def get_matrix(self):
+ if self._mtx is None:
+ boxin = self._boxin
+ boxout = self._boxout
+ x_scale = boxout.width / boxin.width
+ y_scale = boxout.height / boxin.height
- self._mtx = affine._mtx
-
- def is_separable(self):
- return True
+ # MGDTODO: Optimize
+ affine = Affine2D() \
+ .translate(-boxin.xmin, -boxin.ymin) \
+ .scale(x_scale, y_scale) \
+ .translate(boxout.xmin, boxout.ymin)
- def get_matrix(self):
- self._make__mtx()
- return self._mtx
-
+ self._mtx = affine._mtx
+ return self._mtx
+
def blend_xy_sep_transform(x_transform, y_transform):
if x_transform.is_affine() and y_transform.is_affine():
- return BlendedAffine2D(x_transform, y_transform)
+ return BlendedAffine2D(x_transform, y_transform)
return BlendedTransform(x_transform, y_transform)
def composite_transform_factory(a, b):
if a.is_affine() and b.is_affine():
- return CompositeAffine2D(a, b)
+ return CompositeAffine2D(a, b)
return CompositeTransform(a, b)
# MGDTODO: There's probably a better place for this
@@ -562,7 +754,7 @@
vmin, vmax = vmax, vmin
swapped = True
if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny:
- if vmin==0.0:
+ if vmin == 0.0:
vmin = -expander
vmax = expander
else:
@@ -635,8 +827,8 @@
assert scale.to_values() == (10, 0, 0, 20, 0, 0)
rotation = Affine2D().rotate_deg(30)
print rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
- -0.49999999999999994, 0.86602540378443871,
- 0.0, 0.0)
+ -0.49999999999999994, 0.86602540378443871,
+ 0.0, 0.0)
points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_)
translated_points = translation(points)
Modified: branches/transforms/lib/matplotlib/artist.py
===================================================================
--- branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/artist.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -21,7 +21,7 @@
# http://groups.google.com/groups?hl=en&lr=&threadm=mailman.5090.1098044946.5135.python-list%40python.org&rnum=1&prev=/groups%3Fq%3D__doc__%2Bauthor%253Ajdhunter%2540ace.bsd.uchicago.edu%26hl%3Den%26btnG%3DGoogle%2BSearch
-class Artist:
+class Artist(object):
"""
Abstract base class for someone who renders into a FigureCanvas
"""
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -436,8 +436,7 @@
}
def __str__(self):
- return "Axes(%g,%g;%gx%g)"%(self._position[0].get(),self._position[1].get(),
- self._position[2].get(),self._position[3].get())
+ return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
def __init__(self, fig, rect,
axisbg = None, # defaults to rc axes.facecolor
frameon = True,
@@ -590,13 +589,13 @@
def follow_foreign_ylim(ax):
ymin, ymax = axforeign.get_ylim()
- # do not emit here or we'll get a ping png effect
+ # do not emit here or we'll get a ping pong effect
self.set_ylim(ymin, ymax, emit=False)
self.figure.canvas.draw_idle()
def follow_self_ylim(ax):
ymin, ymax = self.get_ylim()
- # do not emit here or we'll get a ping png effect
+ # do not emit here or we'll get a ping pong effect
axforeign.set_ylim(ymin, ymax, emit=False)
axforeign.figure.canvas.draw_idle()
@@ -613,65 +612,66 @@
"""
martist.Artist.set_figure(self, fig)
- l, b, w, h = self._position.bounds
- xmin = fig.bbox.xmin
- xmax = fig.bbox.xmax
- ymin = fig.bbox.ymin
- ymax = fig.bbox.ymax
- figw = xmax-xmin
- figh = ymax-ymin
- self.left = l*figw
- self.bottom = b*figh
- self.right = (l+w)*figw
- self.top = (b+h)*figh
-
- self.bbox = maffine.Bbox.from_lbrt(
- self.left, self.bottom,
- self.right, self.top,
- )
+ self.bbox = maffine.TransformedBbox(self._position, fig.transFigure)
#these will be updated later as data is added
self._set_lim_and_transforms()
+ def _shared_xlim_callback(self, ax):
+ xmin, xmax = ax.get_xlim()
+ self.set_xlim(xmin, xmax, emit=False)
+ self.figure.canvas.draw_idle()
+
+ def _shared_ylim_callback(self, ax):
+ ymin, ymax = ax.get_ylim()
+ self.set_ylim(ymin, ymax, emit=False)
+ self.figure.canvas.draw_idle()
+
def _set_lim_and_transforms(self):
"""
set the dataLim and viewLim BBox attributes and the
transData and transAxes Transformation attributes
"""
- Bbox = maffine.Bbox
+ Bbox = maffine.Bbox
+ self.viewLim = Bbox.unit()
+
if self._sharex is not None:
- left = self._sharex.viewLim.xmin()
- right = self._sharex.viewLim.xmax()
- else:
- left = 0.0
- right = 1.0
+ # MGDTODO: This may be doing at least one too many updates
+ # than necessary
+ self._sharex.callbacks.connect(
+ 'xlim_changed', self._shared_xlim_callback)
+ self.viewLim.intervalx = self._sharex.viewLim.intervalx
if self._sharey is not None:
- bottom = self._sharey.viewLim.ymin()
- top = self._sharey.viewLim.ymax()
- else:
- bottom = 0.0
- top = 1.0
+ self._sharey.callbacks.connect(
+ 'ylim_changed', self._shared_ylim_callback)
+ self.viewLim.intervaly = self._sharex.viewLim.intervaly
- self.viewLim = Bbox.from_lbrt(left, bottom, right, top)
self.dataLim = Bbox.unit()
- self.transData = maffine.BboxTransform(
- self.viewLim, self.bbox)
self.transAxes = maffine.BboxTransform(
Bbox.unit(), self.bbox)
- # MGDTODO
-# if self._sharex:
-# self.transData.set_funcx(self._sharex.transData.get_funcx())
-
-# if self._sharey:
-# self.transData.set_funcy(self._sharey.transData.get_funcy())
-
+ localTransData = maffine.BboxTransform(
+ self.viewLim, self.bbox)
+ if self._sharex:
+ transDataX = self._sharex.transData
+ else:
+ transDataX = localTransData
+ if self._sharey:
+ transDataY = self._sharey.transData
+ else:
+ transDataY = localTransData
+ self.transData = localTransData # maffine.blend_xy_sep_transform(transDataX, transDataY)
+
+
def get_position(self, original=False):
'Return the axes rectangle left, bottom, width, height'
+ # MGDTODO: This changed from returning a list to returning a Bbox
+ # If you get any errors with the result of this function, please
+ # update the calling code
if original:
- return self._originalPosition.bounds
+ return copy.copy(self._originalPosition)
else:
- return self._position.bounds
+ return copy.copy(self._position)
# return [val.get() for val in self._position]
def set_position(self, pos, which='both'):
@@ -690,14 +690,9 @@
ACCEPTS: len(4) sequence of floats
"""
if which in ('both', 'active'):
- # MGDTODO
-# # Change values within self._position--don't replace it.
-# for num,val in zip(pos, self._position):
-# val.set(num)
- self._position.bounds = pos.bounds
- # MGDTODO: side-effects
+ self._position.set(pos)
if which in ('both', 'original'):
- self._originalPosition.bounds = pos.bounds
+ self._originalPosition.set(pos)
def _set_artist_props(self, a):
@@ -1546,7 +1541,14 @@
xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False)
self.viewLim.intervalx = (xmin, xmax)
-
+ if emit:
+ self.callbacks.process('xlim_changed', self)
+ # MGDTODO: It would be nice to do this is in the above callback list,
+ # but it's difficult to tell how to initialize this at the
+ # right time
+ if self._sharex:
+ self._sharex.set_xlim(*self.viewLim.intervalx)
+
return xmin, xmax
def get_xscale(self):
@@ -1650,7 +1652,6 @@
ACCEPTS: len(2) sequence of floats
"""
-
if ymax is None and iterable(ymin):
ymin,ymax = ymin
@@ -1671,8 +1672,14 @@
ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False)
self.viewLim.intervaly = (ymin, ymax)
- if emit: self.callbacks.process('ylim_changed', self)
-
+ if emit:
+ self.callbacks.process('ylim_changed', self)
+ # MGDTODO: It would be nice to do this is in the above callback list,
+ # but it's difficult to tell how to initialize this at the
+ # right time
+ if self._sharey:
+ self._sharey.set_ylim(*self.viewLim.intervaly)
+
return ymin, ymax
def get_yscale(self):
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -1028,9 +1028,8 @@
x,y = self.label.get_position()
if self.label_position == 'bottom':
if not len(bboxes):
- bottom = self.axes.bbox.ymin()
+ bottom = self.axes.bbox.ymin
else:
-
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
@@ -1041,7 +1040,6 @@
if not len(bboxes2):
top = self.axes.bbox.ymax
else:
-
bbox = bbox_union(bboxes2)
top = bbox.ymax
@@ -1054,7 +1052,7 @@
"""
x,y = self.offsetText.get_position()
if not len(bboxes):
- bottom = self.axes.bbox.ymin()
+ bottom = self.axes.bbox.ymin
else:
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -752,7 +752,8 @@
else: # Just found one hit
self.inaxes = axes_list[0]
- try: xdata, ydata = self.inaxes.transData.inverted()([[x, y]])[0]
+ try:
+ xdata, ydata = self.inaxes.transData.inverted().transform_point((x, y))
except ValueError:
self.xdata = None
self.ydata = None
@@ -1584,8 +1585,8 @@
lims.append( (xmin, xmax, ymin, ymax) )
# Store both the original and modified positions
pos.append( (
- tuple( a.get_position(True) ),
- tuple( a.get_position() ) ) )
+ a.get_position(True),
+ a.get_position() ) )
self._views.push(lims)
self._positions.push(pos)
self.set_history_buttons()
Modified: branches/transforms/lib/matplotlib/figure.py
===================================================================
--- branches/transforms/lib/matplotlib/figure.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/figure.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -18,7 +18,7 @@
from text import Text, _process_text_args
from legend import Legend
-from affine import Bbox, BboxTransform
+from affine import Affine2D, Bbox, BboxTransform, TransformedBbox
from ticker import FormatStrFormatter
from cm import ScalarMappable
from contour import ContourSet
@@ -128,17 +128,15 @@
if facecolor is None: facecolor = rcParams['figure.facecolor']
if edgecolor is None: edgecolor = rcParams['figure.edgecolor']
+ self._dpi_scale_trans = Affine2D()
self.dpi = dpi
- figwidth = figsize[0] * dpi
- figheight = figsize[1] * dpi
- self.bbox = Bbox.from_lbwh(0, 0, figwidth, figheight)
+ self.bbox_inches = Bbox.from_lbwh(0, 0, *figsize)
+ self.bbox = TransformedBbox(self.bbox_inches, self._dpi_scale_trans)
self.frameon = frameon
- self.transFigure = BboxTransform( Bbox.unit(), self.bbox)
+ self.transFigure = BboxTransform(Bbox.unit(), self.bbox)
-
-
self.figurePatch = Rectangle(
xy=(0,0), width=1, height=1,
facecolor=facecolor, edgecolor=edgecolor,
@@ -160,6 +158,15 @@
self._cachedRenderer = None
+ def _get_dpi(self):
+ return self._dpi
+ def _set_dpi(self, dpi):
+ print "setting dpi"
+ self._dpi = dpi
+ self._dpi_scale_trans.clear().scale(dpi, dpi)
+ print self._dpi_scale_trans
+ dpi = property(_get_dpi, _set_dpi)
+
def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'):
"""
A common use case is a number of subplots with shared xaxes
@@ -325,7 +332,7 @@
w,h = args
dpival = self.dpi
- self.bbox.max = w * dpival, h * dpival
+ self.bbox_inches.max = w, h
# self.figwidth.set(w) MGDTODO
# self.figheight.set(h)
@@ -339,7 +346,7 @@
manager.resize(int(canvasw), int(canvash))
def get_size_inches(self):
- return self.bbox.max
+ return self.bbox_inches.max
# return self.figwidth.get(), self.figheight.get() MGDTODO
def get_edgecolor(self):
@@ -352,12 +359,12 @@
def get_figwidth(self):
'Return the figwidth as a float'
- return self.bbox.xmax
+ return self.bbox_inches.xmax
# return self.figwidth.get() MGDTODO
def get_figheight(self):
'Return the figheight as a float'
- return self.bbox.ymax
+ return self.bbox_inches.ymax
def get_dpi(self):
'Return the dpi as a float'
@@ -400,7 +407,7 @@
ACCEPTS: float
"""
# self.figwidth.set(val) MGDTODO
- self.bbox.xmax = val
+ self.bbox_inches.xmax = val
def set_figheight(self, val):
"""
@@ -409,7 +416,7 @@
ACCEPTS: float
"""
# MGDTODO (set())
- self.bbox.ymax = val
+ self.bbox_inches.ymax = val
def set_frameon(self, b):
"""
Modified: branches/transforms/lib/matplotlib/pyplot.py
===================================================================
--- branches/transforms/lib/matplotlib/pyplot.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/pyplot.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -481,7 +481,7 @@
byebye = []
for other in fig.axes:
if other==a: continue
- if bbox.overlaps(other.bbox, ignoreend=True):
+ if bbox.fully_overlaps(other.bbox):
byebye.append(other)
for ax in byebye: delaxes(ax)
Modified: branches/transforms/lib/matplotlib/text.py
===================================================================
--- branches/transforms/lib/matplotlib/text.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/text.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -15,7 +15,7 @@
from cbook import enumerate, is_string_like, maxdict, is_numlike
from font_manager import FontProperties
from patches import bbox_artist, YAArrow
-from affine import Bbox
+from affine import Affine2D, Bbox
from lines import Line2D
import matplotlib.nxutils as nxutils
@@ -213,30 +213,32 @@
M = self.get_rotation_matrix(xmin, ymin)
# the corners of the unrotated bounding box
- cornersHoriz = ( (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) )
- offsetLayout = []
+ cornersHoriz = npy.array(
+ [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)],
+ npy.float_)
+ offsetLayout = npy.zeros((len(lines), 2))
# now offset the individual text lines within the box
if len(lines)>1: # do the multiline aligment
malign = self._get_multialignment()
- for line, thisx, thisy, w, h in horizLayout:
+ for i, (line, thisx, thisy, w, h) in enumerate(horizLayout):
if malign=='center': offsetx = width/2.0-w/2.0
elif malign=='right': offsetx = width-w
else: offsetx = 0
thisx += offsetx
- offsetLayout.append( (thisx, thisy ))
+ offsetLayout[i] = (thisx, thisy)
else: # no additional layout needed
- offsetLayout = [ (thisx, thisy) for line, thisx, thisy, w, h in horizLayout]
+ offsetLayout[0] = horizLayout[0][1:3]
# now rotate the bbox
- cornersRotated = [npy.dot(M,npy.array([[thisx],[thisy],[1]])) for thisx, thisy in cornersHoriz]
+ cornersRotated = M(cornersHoriz)
- txs = [float(v[0][0]) for v in cornersRotated]
- tys = [float(v[1][0]) for v in cornersRotated]
+ txs = cornersRotated[:, 0]
+ tys = cornersRotated[:, 1]
# compute the bounds of the rotated box
- xmin, xmax = min(txs), max(txs)
- ymin, ymax = min(tys), max(tys)
+ xmin, xmax = txs.min(), txs.max()
+ ymin, ymax = tys.min(), tys.max()
width = xmax - xmin
height = ymax - ymin
@@ -264,17 +266,18 @@
bbox = Bbox.from_lbwh(xmin, ymin, width, height)
+
# now rotate the positions around the first x,y position
- xys = [npy.dot(M,npy.array([[thisx],[thisy],[1]])) for thisx, thisy in offsetLayout]
+ xys = M(offsetLayout)
+ tx = xys[:, 0]
+ ty = xys[:, 1]
+ tx += offsetx
+ ty += offsety
-
- tx = [float(v[0][0])+offsetx for v in xys]
- ty = [float(v[1][0])+offsety for v in xys]
-
# now inverse transform back to data coords
inverse_transform = self.get_transform().inverted()
- xys = inverse_transform(zip(tx, ty))
+ xys = inverse_transform(xys)
xs, ys = zip(*xys)
@@ -327,7 +330,7 @@
return
for line, wh, x, y in info:
- x, y = trans([[x, y]])[0]
+ x, y = trans.transform_point((x, y))
if renderer.flipy():
canvasw, canvash = renderer.get_canvas_width_height()
@@ -435,29 +438,9 @@
bbox, info = self._get_layout(self._renderer)
return bbox
-
-
def get_rotation_matrix(self, x0, y0):
+ return Affine2D().rotate_deg_around(x0, y0, self.get_rotation())
- theta = npy.pi/180.0*self.get_rotation()
- # translate x0,y0 to origin
- Torigin = npy.array([ [1, 0, -x0],
- [0, 1, -y0],
- [0, 0, 1 ]])
-
- # rotate by theta
- R = npy.array([ [npy.cos(theta), -npy.sin(theta), 0],
- [npy.sin(theta), npy.cos(theta), 0],
- [0, 0, 1]])
-
- # translate origin back to x0,y0
- Tback = npy.array([ [1, 0, x0],
- [0, 1, y0],
- [0, 0, 1 ]])
-
-
- return npy.dot(npy.dot(Tback,R), Torigin)
-
def set_backgroundcolor(self, color):
"""
Set the background color of the text by updating the bbox (see set_bbox for more info)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-13 12:50:07
|
Revision: 3847
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3847&view=rev
Author: mdboom
Date: 2007-09-13 05:50:05 -0700 (Thu, 13 Sep 2007)
Log Message:
-----------
Merged revisions 3836-3846 via svnmerge from
http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib
........
r3844 | jdh2358 | 2007-09-12 16:37:41 -0400 (Wed, 12 Sep 2007) | 1 line
fixed a bar units bug
........
r3845 | jouni | 2007-09-13 02:29:14 -0400 (Thu, 13 Sep 2007) | 3 lines
More work on dviread and usetex in pdf. It is more usable now,
so I am renaming the method from _draw_tex to draw_tex.
........
Modified Paths:
--------------
branches/transforms/API_CHANGES
branches/transforms/CHANGELOG
branches/transforms/lib/matplotlib/artist.py
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/backends/backend_pdf.py
branches/transforms/lib/matplotlib/dviread.py
branches/transforms/lib/matplotlib/mlab.py
branches/transforms/lib/matplotlib/patches.py
Added Paths:
-----------
branches/transforms/examples/units/bar_demo2.py
Property Changed:
----------------
branches/transforms/
Property changes on: branches/transforms
___________________________________________________________________
Name: svnmerge-integrated
- /trunk/matplotlib:1-3835
+ /trunk/matplotlib:1-3846
Modified: branches/transforms/API_CHANGES
===================================================================
--- branches/transforms/API_CHANGES 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/API_CHANGES 2007-09-13 12:50:05 UTC (rev 3847)
@@ -1,3 +1,5 @@
+ Made skiprows=1 the default on csv2rec
+
The gd and paint backends have been deleted.
The errorbar method and function now accept additional kwargs
Modified: branches/transforms/CHANGELOG
===================================================================
--- branches/transforms/CHANGELOG 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/CHANGELOG 2007-09-13 12:50:05 UTC (rev 3847)
@@ -1,3 +1,10 @@
+2007-09-13 The usetex support in the pdf backend is more usable now,
+ so I am enabling it. - JKS
+
+2007-09-12 Fixed a Axes.bar unit bug - JDH
+
+2007-09-10 Made skiprows=1 the default on csv2rec - JDH
+
2007-09-09 Split out the plotting part of pylab and put it in
pyplot.py; removed numerix from the remaining pylab.py,
which imports everything from pyplot.py. The intention
Copied: branches/transforms/examples/units/bar_demo2.py (from rev 3845, trunk/matplotlib/examples/units/bar_demo2.py)
===================================================================
--- branches/transforms/examples/units/bar_demo2.py (rev 0)
+++ branches/transforms/examples/units/bar_demo2.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -0,0 +1,34 @@
+"""
+plot using a variety of cm vs inches conversions. The example shows
+how default unit instrospection works (ax1), how various keywords can
+be used to set the x and y units to override the defaults (ax2, ax3,
+ax4) and how one can set the xlimits using scalars (ax3, current units
+assumed) or units (conversions applied to get the numbers to current
+units)
+
+"""
+from basic_units import cm, inch
+from pylab import figure, show, nx
+
+cms = cm *nx.arange(0, 10, 2)
+bottom=0*cm
+width=0.8*cm
+
+fig = figure()
+
+ax1 = fig.add_subplot(2,2,1)
+ax1.bar(cms, cms, bottom=bottom)
+
+ax2 = fig.add_subplot(2,2,2)
+ax2.bar(cms, cms, bottom=bottom, width=width, xunits=cm, yunits=inch)
+
+ax3 = fig.add_subplot(2,2,3)
+ax3.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm)
+ax3.set_xlim(3, 6) # scalars are interpreted in current units
+
+ax4 = fig.add_subplot(2,2,4)
+ax4.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=inch)
+#fig.savefig('simple_conversion_plot.png')
+ax4.set_xlim(3*cm, 6*cm) # cm are converted to inches
+
+show()
Modified: branches/transforms/lib/matplotlib/artist.py
===================================================================
--- branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -51,7 +51,7 @@
self._remove_method = None
def remove(self):
- '''
+ """
Remove the artist from the figure if possible. The effect will not
be visible until the figure is redrawn, e.g., with ax.draw_idle().
Call ax.relim() to update the axes limits if desired.
@@ -60,7 +60,7 @@
was added to axes with autolim=True.
Note: there is no support for removing the artist's legend entry.
- '''
+ """
# There is no method to set the callback. Instead the parent should set
# the _remove_method attribute directly. This would be a protected
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -1193,23 +1193,27 @@
def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
'look for unit kwargs and update the axis instances as necessary'
- if self.xaxis is None or self.xaxis is None: return
+ if self.xaxis is None or self.yaxis is None: return
-
+ #print 'processing', self.get_geometry()
if xdata is not None:
self.xaxis.update_units(xdata)
+ #print '\tset from xdata', self.xaxis.units
if ydata is not None:
self.yaxis.update_units(ydata)
+ #print '\tset from ydata', self.yaxis.units
# process kwargs 2nd since these will override default units
if kwargs is not None:
xunits = kwargs.pop( 'xunits', self.xaxis.units)
if xunits!=self.xaxis.units:
+ #print '\tkw setting xunits', xunits
self.xaxis.set_units(xunits)
yunits = kwargs.pop('yunits', self.yaxis.units)
if yunits!=self.yaxis.units:
+ #print '\tkw setting yunits', yunits
self.yaxis.set_units(yunits)
def in_axes(self, xwin, ywin):
@@ -3122,11 +3126,13 @@
else:
raise ValueError, 'invalid orientation: %s' % orientation
- left = npy.asarray(left)
- height = npy.asarray(height)
- width = npy.asarray(width)
- bottom = npy.asarray(bottom)
+ # do not convert to array here as unit info is lost
+ #left = npy.asarray(left)
+ #height = npy.asarray(height)
+ #width = npy.asarray(width)
+ #bottom = npy.asarray(bottom)
+
if len(linewidth) == 1: linewidth = linewidth * nbars
# if color looks like a color string, an RGB tuple or a
@@ -3169,14 +3175,14 @@
# lets do some conversions now
if self.xaxis is not None:
xconv = self.xaxis.converter
- if ( xconv ):
+ if xconv is not None:
units = self.xaxis.get_units()
left = xconv.convert( left, units )
width = xconv.convert( width, units )
if self.yaxis is not None:
yconv = self.yaxis.converter
- if ( yconv ):
+ if yconv is not None :
units = self.yaxis.get_units()
bottom = yconv.convert( bottom, units )
height = yconv.convert( height, units )
@@ -3216,12 +3222,14 @@
if xerr is not None or yerr is not None:
if orientation == 'vertical':
- x = left + 0.5*width
- y = bottom + height
+ # using list comps rather than arrays to preserve unit info
+ x = [l+0.5*w for l, w in zip(left, width)]
+ y = [b+h for b,h in zip(bottom, height)]
elif orientation == 'horizontal':
- x = left + width
- y = bottom + 0.5*height
+ # using list comps rather than arrays to preserve unit info
+ x = [l+w for l,w in zip(left, width)]
+ y = [b+0.5*h for b,h in zip(bottom, height)]
self.errorbar(
x, y,
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -821,7 +821,7 @@
return x
ret = self.converter.convert(x, self.units)
- #print 'convert_units converting: units=%s, converter=%s, in=%s, out=%s'%(self.units, self.converter, x, ret)
+ #print 'convert_units converting: axis=%s, units=%s, converter=%s, in=%s, out=%s'%(self, self.units, self.converter, x, ret)
return ret
def set_units(self, u):
Modified: branches/transforms/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -527,7 +527,7 @@
widths.append(afmdata.get_width_from_char_name(ch))
except KeyError:
matplotlib.verbose.report(
- 'No width for %s in %s' % (ch, fullname), 'debug')
+ 'No width for %s in %s' % (ch, fullname), 'debug-annoying')
widths.append(0)
differencesArray = [ Name(ch) for ch in enc ]
@@ -561,7 +561,7 @@
except KeyError:
matplotlib.verbose.report(
'No name for glyph %d in %s' % (ch, fullname),
- 'debug')
+ 'debug-annoying')
need_idx = True
@@ -1449,9 +1449,7 @@
# Pop off the global transformation
self.file.output(Op.grestore)
- def _draw_tex(self, gc, x, y, s, prop, angle):
- # Rename to draw_tex to enable
-
+ def draw_tex(self, gc, x, y, s, prop, angle):
texmanager = self.get_texmanager()
fontsize = prop.get_size_in_points()
dvifile = texmanager.make_dvi(s, fontsize)
@@ -1494,7 +1492,7 @@
elt[3][-1] += next[3][0]
elt[4] += next[4]-next[1]
else:
- elt[3] += [offset, next[3][0]]
+ elt[3] += [offset*1000.0/dvifont.size, next[3][0]]
elt[4] = next[4]
del seq[i+1]
continue
Modified: branches/transforms/lib/matplotlib/dviread.py
===================================================================
--- branches/transforms/lib/matplotlib/dviread.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/dviread.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -84,16 +84,22 @@
e = 0 # zero depth
else: # glyph
x,y,font,g,w = elt
- h = (font.scale * font.tfm.height[g]) >> 20
- e = (font.scale * font.tfm.depth[g]) >> 20
+ h = _mul2012(font._scale, font._tfm.height[g])
+ e = _mul2012(font._scale, font._tfm.depth[g])
minx = min(minx, x)
miny = min(miny, y - h)
maxx = max(maxx, x + w)
maxy = max(maxy, y + e)
maxy_pure = max(maxy_pure, y)
+ if self.dpi is None:
+ # special case for ease of debugging: output raw dvi coordinates
+ return mpl_cbook.Bunch(text=self.text, boxes=self.boxes,
+ width=maxx-minx, height=maxy_pure-miny,
+ descent=maxy-maxy_pure)
+
d = self.dpi / (72.27 * 2**16) # from TeX's "scaled points" to dpi units
- text = [ ((x-minx)*d, (maxy-y)*d, DviFont(f), g, w*d)
+ text = [ ((x-minx)*d, (maxy-y)*d, f, g, w*d)
for (x,y,f,g,w) in self.text ]
boxes = [ ((x-minx)*d, (maxy-y)*d, h*d, w*d) for (x,y,h,w) in self.boxes ]
@@ -110,11 +116,11 @@
while True:
byte = ord(self.file.read(1))
self._dispatch(byte)
- if self.state == _dvistate.inpage:
- matplotlib.verbose.report(
- 'Dvi._read: after %d at %f,%f' %
- (byte, self.h, self.v),
- 'debug-annoying')
+# if self.state == _dvistate.inpage:
+# matplotlib.verbose.report(
+# 'Dvi._read: after %d at %f,%f' %
+# (byte, self.h, self.v),
+# 'debug-annoying')
if byte == 140: # end of page
return True
if self.state == _dvistate.post_post: # end of file
@@ -225,21 +231,11 @@
# I think we can assume this is constant
self.state = _dvistate.outer
- def _width_of(self, char, font):
- width = font.tfm.width.get(char, None)
- if width is not None:
- return (width * font.scale) >> 20
-
- matplotlib.verbose.report(
- 'No width for char %d in font %s' % (char, font.name),
- 'debug')
- return 0
-
def _set_char(self, char):
if self.state != _dvistate.inpage:
raise ValueError, "misplaced set_char in dvi file"
self._put_char(char)
- self.h += self._width_of(char, self.fonts[self.f])
+ self.h += self.fonts[self.f]._width_of(char)
def _set_rule(self, a, b):
if self.state != _dvistate.inpage:
@@ -251,20 +247,33 @@
if self.state != _dvistate.inpage:
raise ValueError, "misplaced put_char in dvi file"
font = self.fonts[self.f]
- if font.vf is None:
+ if font._vf is None:
self.text.append((self.h, self.v, font, char,
- self._width_of(char, font)))
+ font._width_of(char)))
+# matplotlib.verbose.report(
+# 'Dvi._put_char: %d,%d %d' %(self.h, self.v, char),
+# 'debug-annoying')
else:
- self.text.extend([(self.h + x, self.v + y, f, g, w)
- for x, y, f, g, w in font.vf[char].text])
- self.boxes.extend([(self.h + x, self.v + y, a, b)
- for x, y, a, b in font.vf[char].boxes])
+ scale = font._scale
+ for x, y, f, g, w in font._vf[char].text:
+ newf = DviFont(scale=_mul2012(scale, f._scale),
+ tfm=f._tfm, texname=f.texname, vf=f._vf)
+ self.text.append((self.h + _mul2012(x, scale),
+ self.v + _mul2012(y, scale),
+ newf, g, newf._width_of(g)))
+ self.boxes.extend([(self.h + _mul2012(x, scale),
+ self.v + _mul2012(y, scale),
+ _mul2012(a, scale), _mul2012(b, scale))
+ for x, y, a, b in font._vf[char].boxes])
def _put_rule(self, a, b):
if self.state != _dvistate.inpage:
raise ValueError, "misplaced put_rule in dvi file"
if a > 0 and b > 0:
self.boxes.append((self.h, self.v, a, b))
+# matplotlib.verbose.report(
+# 'Dvi._put_rule: %d,%d %d,%d' % (self.h, self.v, a, b),
+# 'debug-annoying')
def _nop(self):
pass
@@ -357,7 +366,7 @@
vf = _vffile(n[-l:])
- self.fonts[k] = mpl_cbook.Bunch(scale=s, tfm=tfm, name=n, vf=vf)
+ self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf)
def _post(self):
if self.state != _dvistate.outer:
@@ -370,17 +379,20 @@
raise NotImplementedError
class DviFont(object):
- __slots__ = ('texname', 'size')
+ """
+ Object that holds a font's texname and size and supports comparison.
+ There are also internal attributes (for use by dviread.py) that
+ are _not_ used for comparison.
- def __init__(self, f):
- """
- Object that holds a font's texname and size and supports comparison.
+ The size is in Adobe points (converted from TeX points).
+ """
+ __slots__ = ('texname', 'size', '_scale', '_vf', '_tfm')
- The size is in Adobe points (converted from TeX points).
- """
+ def __init__(self, scale, tfm, texname, vf):
+ self._scale, self._tfm, self.texname, self._vf = \
+ scale, tfm, texname, vf
# TODO: would it make more sense to have the size in dpi units?
- self.texname = f.name
- self.size = f.scale * (72.0 / (72.27 * 2**16))
+ self.size = scale * (72.0 / (72.27 * 2**16))
def __eq__(self, other):
return self.__class__ == other.__class__ and \
@@ -389,6 +401,16 @@
def __ne__(self, other):
return not self.__eq__(other)
+ def _width_of(self, char):
+ width = self._tfm.width.get(char, None)
+ if width is not None:
+ return _mul2012(width, self._scale)
+
+ matplotlib.verbose.report(
+ 'No width for char %d in font %s' % (char, self.texname),
+ 'debug')
+ return 0
+
class Vf(Dvi):
"""
A virtual font (*.vf file) containing subroutines for dvi files.
@@ -465,7 +487,8 @@
raise ValueError, "pre command in middle of vf file"
if i != 202:
raise ValueError, "Unknown vf format %d" % i
- matplotlib.verbose.report('vf file comment: ' + x, 'debug')
+ if len(x):
+ matplotlib.verbose.report('vf file comment: ' + x, 'debug')
self.state = _dvistate.outer
# cs = checksum, ds = design size
@@ -474,7 +497,7 @@
if self._first_font is None:
self._first_font = k
-def fix2comp(num):
+def _fix2comp(num):
"""
Convert from two's complement to negative.
"""
@@ -484,6 +507,13 @@
else:
return num
+def _mul2012(num1, num2):
+ """
+ Multiply two numbers in 20.12 fixed point format.
+ """
+ # Separated into a function because >> has surprising precedence
+ return (num1*num2) >> 20
+
class Tfm(object):
"""
A TeX Font Metric file. This implementation covers only the bare
@@ -497,6 +527,7 @@
(this is a dict because indexing may not start from 0)
height[i], depth[i]: height and depth of character #i
"""
+ __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth')
def __init__(self, filename):
matplotlib.verbose.report('opening tfm file ' + filename, 'debug')
@@ -525,9 +556,9 @@
[ struct.unpack('!%dI' % (len(x)/4), x)
for x in (widths, heights, depths) ]
for i in range(ec-bc):
- self.width[bc+i] = fix2comp(widths[ord(char_info[4*i])])
- self.height[bc+i] = fix2comp(heights[ord(char_info[4*i+1]) >> 4])
- self.depth[bc+i] = fix2comp(depths[ord(char_info[4*i+1]) & 0xf])
+ self.width[bc+i] = _fix2comp(widths[ord(char_info[4*i])])
+ self.height[bc+i] = _fix2comp(heights[ord(char_info[4*i+1]) >> 4])
+ self.depth[bc+i] = _fix2comp(depths[ord(char_info[4*i+1]) & 0xf])
class PsfontsMap(object):
@@ -552,6 +583,7 @@
the pdf-related files perhaps only avoid the "Base 14" pdf fonts.
But the user may have configured these files differently.
"""
+ __slots__ = ('_font',)
def __init__(self, filename):
self._font = {}
@@ -627,7 +659,17 @@
encoding=encoding, filename=filename)
class Encoding(object):
+ """
+ Parses a *.enc file referenced from a psfonts.map style file.
+ The format this class understands is a very limited subset of
+ PostScript.
+ Usage (subject to change):
+ for name in Encoding(filename):
+ whatever(name)
+ """
+ __slots__ = ('encoding',)
+
def __init__(self, filename):
file = open(filename, 'rt')
try:
@@ -694,6 +736,10 @@
return result
+# With multiple text objects per figure (e.g. tick labels) we may end
+# up reading the same tfm and vf files many times, so we implement a
+# simple cache. TODO: is this worth making persistent?
+
_tfmcache = {}
_vfcache = {}
@@ -721,19 +767,22 @@
if __name__ == '__main__':
- matplotlib.verbose.set_level('debug')
- dvi = Dvi('foo.dvi', 72)
+ import sys
+ matplotlib.verbose.set_level('debug-annoying')
+ fname = sys.argv[1]
+ try: dpi = float(sys.argv[2])
+ except IndexError: dpi = None
+ dvi = Dvi(fname, dpi)
fontmap = PsfontsMap(find_tex_file('pdftex.map'))
- for text,boxes in dvi:
+ for page in dvi:
print '=== new page ==='
fPrev = None
- for x,y,f,c in text:
- texname = dvi.fonts[f].name
- print x,y,c,chr(c),texname
+ for x,y,f,c,w in page.text:
if f != fPrev:
- print 'font', texname, '=', fontmap[texname].__dict__
+ print 'font', f.texname, 'scaled', f._scale/pow(2.0,20)
fPrev = f
- for x,y,w,h in boxes:
+ print x,y,c, 32 <= c < 128 and chr(c) or '.', w
+ for x,y,w,h in page.boxes:
print x,y,'BOX',w,h
Modified: branches/transforms/lib/matplotlib/mlab.py
===================================================================
--- branches/transforms/lib/matplotlib/mlab.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/mlab.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -1253,9 +1253,9 @@
if r==1 or c==1:
X.shape = max(r,c),
if unpack: return X.transpose()
- return X
+ else: return X
-def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',',
+def csv2rec(fname, comments='#', skiprows=1, checkrows=5, delimiter=',',
converterd=None, names=None, missing=None):
"""
Load data from comma/space/tab delimited file in fname into a
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-09-13 12:44:16 UTC (rev 3846)
+++ branches/transforms/lib/matplotlib/patches.py 2007-09-13 12:50:05 UTC (rev 3847)
@@ -77,6 +77,8 @@
if len(kwargs): artist.setp(self, **kwargs)
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
+
+
def contains(self, mouseevent):
"""Test whether the mouse event occurred in the patch.
@@ -352,7 +354,6 @@
Return the vertices of the rectangle
"""
x, y = self.xy
-
left, right = self.convert_xunits((x, x + self.width))
bottom, top = self.convert_yunits((y, y + self.height))
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-13 12:44:23
|
Revision: 3846
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3846&view=rev
Author: mdboom
Date: 2007-09-13 05:44:16 -0700 (Thu, 13 Sep 2007)
Log Message:
-----------
Minor changes -- committing so I can merge again.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/lib/matplotlib/patches.py
branches/transforms/lib/matplotlib/text.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-13 06:29:14 UTC (rev 3845)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-13 12:44:16 UTC (rev 3846)
@@ -4,7 +4,7 @@
2007 Michael Droettboom
"""
-import numpy as N
+import numpy as npy
from numpy.linalg import inv
from sets import Set
@@ -37,7 +37,7 @@
class Bbox(TransformNode):
def __init__(self, points):
TransformNode.__init__(self)
- self._points = N.asarray(points, N.float_)
+ self._points = npy.asarray(points, npy.float_)
self.track = False
#@staticmethod
@@ -52,7 +52,7 @@
#@staticmethod
def from_lbrt(*args):
- points = N.array(args, dtype=N.float_).reshape(2, 2)
+ points = npy.array(args, dtype=npy.float_).reshape(2, 2)
return Bbox(points)
from_lbrt = staticmethod(from_lbrt)
@@ -72,12 +72,15 @@
return 'Bbox(%s)' % repr(self._points)
__str__ = __repr__
+ def __array__(self):
+ return self._points
+
# JDH: the update method will update the box limits from the
# existing limits and the new data; it appears here you are just
# using the new data. We use an "ignore" flag to specify whether
# you want to include the existing data or not in the update
def update_from_data(self, x, y, ignore=True):
- self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_)
+ self._points = npy.array([[x.min(), y.min()], [x.max(), y.max()]], npy.float_)
self.invalidate()
# MGDTODO: Probably a more efficient ways to do this...
@@ -150,7 +153,7 @@
self.xmax - self.xmin, self.ymax - self.ymin)
def _set_bounds(self, bounds):
l,b,w,h = bounds
- self._points = N.array([[l, b], [l+w, b+h]], N.float_)
+ self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
self.invalidate()
bounds = property(_get_bounds, _set_bounds)
@@ -165,7 +168,7 @@
height = self.height
deltaw = (sw * width - width) / 2.0
deltah = (sh * height - height) / 2.0
- a = N.array([[-deltaw, -deltah], [deltaw, deltah]])
+ a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
return Bbox(self._points + a)
def contains(self, x, y):
@@ -215,7 +218,7 @@
raise TypeError("Can not add Transform to object of type '%s'" % type(other))
def transform_point(self, point):
- return self.__call__([point])[0]
+ return self.__call__(npy.asarray([point]))[0]
def has_inverse(self):
raise NotImplementedError()
@@ -229,8 +232,6 @@
def is_affine(self):
return False
-# MGDTODO: Separate out Affine2DBase / Affine2DConcrete so BlendedAffine and CompositeAffine don't have translate/scale/rotate members
-
class Affine2DBase(Transform):
input_dims = 2
output_dims = 2
@@ -246,7 +247,7 @@
#@staticmethod
def _concat(a, b):
- return N.dot(b, a)
+ return npy.dot(b, a)
_concat = staticmethod(_concat)
def to_values(self):
@@ -255,7 +256,7 @@
#@staticmethod
def matrix_from_values(a, b, c, d, e, f):
- affine = N.zeros((3,3), N.float_)
+ affine = npy.zeros((3,3), npy.float_)
affine[0,] = a, c, e
affine[1,] = b, d, f
affine[2,2] = 1
@@ -267,7 +268,7 @@
def __call__(self, points):
"""
- Applies the transformation to a set of 2D points and
+ Applies the transformation to an array of 2D points and
returns the result.
points must be a numpy array of shape (N, 2), where N is the
@@ -277,9 +278,9 @@
# the points to an array in the first place. If we can use
# more arrays upstream, that should help here.
mtx = self.get_matrix()
- points = N.asarray(points, N.float_)
+ points = npy.asarray(points, npy.float_)
points = points.transpose()
- points = N.dot(mtx[0:2, 0:2], points)
+ points = npy.dot(mtx[0:2, 0:2], points)
points = points + mtx[0:2, 2:]
return points.transpose()
@@ -311,7 +312,7 @@
"""
Affine2DBase.__init__(self)
if matrix is None:
- matrix = N.identity(3)
+ matrix = npy.identity(3)
else:
assert matrix.shape == (3, 3)
self._mtx = matrix
@@ -348,19 +349,19 @@
#@staticmethod
def identity():
- return Affine2D(N.identity(3))
+ return Affine2D(npy.identity(3))
identity = staticmethod(identity)
def rotate(self, theta):
- a = N.cos(theta)
- b = N.sin(theta)
+ a = npy.cos(theta)
+ b = npy.sin(theta)
rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0)
self._mtx = self._concat(self._mtx, rotate_mtx)
self.invalidate()
return self
def rotate_deg(self, degrees):
- return self.rotate(degrees*N.pi/180.)
+ return self.rotate(degrees*npy.pi/180.)
def translate(self, tx, ty):
translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty)
@@ -420,7 +421,7 @@
# This works because we already know the transforms are
# separable, though normally one would want to set b and
# c to zero.
- self._mtx = N.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
+ self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
def is_separable(self):
return True
@@ -444,7 +445,7 @@
y_points = self._y(points)
# This works because we already know the transforms are
# separable
- return N.hstack((x_points[:, 0:1], y_points[:, 1:2]))
+ return npy.hstack((x_points[:, 0:1], y_points[:, 1:2]))
class CompositeAffine2D(Affine2DBase):
def __init__(self, a, b):
@@ -579,6 +580,7 @@
return interval[0] < val and interval[1] > val
if __name__ == '__main__':
+ import copy
from random import random
import timeit
@@ -588,36 +590,38 @@
assert bbox.xmax == 20
assert bbox.ymax == 25
- assert N.all(bbox.min == [10, 15])
- assert N.all(bbox.max == [20, 25])
- assert N.all(bbox.intervalx == (10, 20))
- assert N.all(bbox.intervaly == (15, 25))
+ assert npy.all(bbox.min == [10, 15])
+ assert npy.all(bbox.max == [20, 25])
+ assert npy.all(bbox.intervalx == (10, 20))
+ assert npy.all(bbox.intervaly == (15, 25))
assert bbox.width == 10
assert bbox.height == 10
- assert bbox.get_bounds() == (10, 15, 10, 10)
+ assert bbox.bounds == (10, 15, 10, 10)
+ print npy.asarray(bbox)
+
bbox.intervalx = (11, 21)
bbox.intervaly = (16, 26)
- assert bbox.get_bounds() == (11, 16, 10, 10)
+ assert bbox.bounds == (11, 16, 10, 10)
bbox.xmin = 12
bbox.ymin = 17
bbox.xmax = 22
bbox.ymax = 27
- assert bbox.get_bounds() == (12, 17, 10, 10)
+ assert bbox.bounds == (12, 17, 10, 10)
bbox = Bbox.from_lbwh(10, 11, 12, 13)
- assert bbox.get_bounds() == (10, 11, 12, 13)
+ assert bbox.bounds == (10, 11, 12, 13)
- bbox_copy = bbox.copy()
+ bbox_copy = copy.copy(bbox)
assert bbox == bbox_copy
bbox_copy.max = (14, 15)
- assert bbox.get_bounds() == (10, 11, 12, 13)
- assert bbox_copy.get_bounds() == (10, 11, 4, 4)
+ assert bbox.bounds == (10, 11, 12, 13)
+ assert bbox_copy.bounds == (10, 11, 4, 4)
bbox1 = Bbox([[10., 15.], [20., 25.]])
bbox2 = Bbox([[30., 35.], [40., 45.]])
@@ -634,7 +638,7 @@
-0.49999999999999994, 0.86602540378443871,
0.0, 0.0)
- points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_)
+ points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_)
translated_points = translation(points)
assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all()
scaled_points = scale(points)
@@ -655,7 +659,7 @@
t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points")
print "Time to transform 10000 x 10 points as tuples:", t.timeit(10)
- points2 = N.asarray(points)
+ points2 = npy.asarray(points)
t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, points2")
print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10)
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-09-13 06:29:14 UTC (rev 3845)
+++ branches/transforms/lib/matplotlib/patches.py 2007-09-13 12:44:16 UTC (rev 3846)
@@ -356,9 +356,9 @@
left, right = self.convert_xunits((x, x + self.width))
bottom, top = self.convert_yunits((y, y + self.height))
- return ( (left, bottom), (left, top),
- (right, top), (right, bottom),
- )
+ return npy.array([[left, bottom], [left, top],
+ [right, top], [right, bottom]],
+ npy.float_)
def get_x(self):
"Return the left coord of the rectangle"
Modified: branches/transforms/lib/matplotlib/text.py
===================================================================
--- branches/transforms/lib/matplotlib/text.py 2007-09-13 06:29:14 UTC (rev 3845)
+++ branches/transforms/lib/matplotlib/text.py 2007-09-13 12:44:16 UTC (rev 3846)
@@ -149,7 +149,7 @@
def _get_xy_display(self):
'get the (possibly unit converted) transformed x,y in display coords'
x, y = self.get_position()
- return self.get_transform()([[x,y]])[0]
+ return self.get_transform().transform_point((x,y))
def _get_multialignment(self):
if self._multialignment is not None: return self._multialignment
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <jo...@us...> - 2007-09-13 06:29:33
|
Revision: 3845
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3845&view=rev
Author: jouni
Date: 2007-09-12 23:29:14 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
More work on dviread and usetex in pdf. It is more usable now,
so I am renaming the method from _draw_tex to draw_tex.
Modified Paths:
--------------
trunk/matplotlib/CHANGELOG
trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
trunk/matplotlib/lib/matplotlib/dviread.py
Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG 2007-09-12 20:37:41 UTC (rev 3844)
+++ trunk/matplotlib/CHANGELOG 2007-09-13 06:29:14 UTC (rev 3845)
@@ -1,3 +1,6 @@
+2007-09-13 The usetex support in the pdf backend is more usable now,
+ so I am enabling it. - JKS
+
2007-09-12 Fixed a Axes.bar unit bug - JDH
2007-09-10 Made skiprows=1 the default on csv2rec - JDH
Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-12 20:37:41 UTC (rev 3844)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-13 06:29:14 UTC (rev 3845)
@@ -527,7 +527,7 @@
widths.append(afmdata.get_width_from_char_name(ch))
except KeyError:
matplotlib.verbose.report(
- 'No width for %s in %s' % (ch, fullname), 'debug')
+ 'No width for %s in %s' % (ch, fullname), 'debug-annoying')
widths.append(0)
differencesArray = [ Name(ch) for ch in enc ]
@@ -561,7 +561,7 @@
except KeyError:
matplotlib.verbose.report(
'No name for glyph %d in %s' % (ch, fullname),
- 'debug')
+ 'debug-annoying')
need_idx = True
@@ -1449,9 +1449,7 @@
# Pop off the global transformation
self.file.output(Op.grestore)
- def _draw_tex(self, gc, x, y, s, prop, angle):
- # Rename to draw_tex to enable
-
+ def draw_tex(self, gc, x, y, s, prop, angle):
texmanager = self.get_texmanager()
fontsize = prop.get_size_in_points()
dvifile = texmanager.make_dvi(s, fontsize)
@@ -1494,7 +1492,7 @@
elt[3][-1] += next[3][0]
elt[4] += next[4]-next[1]
else:
- elt[3] += [offset, next[3][0]]
+ elt[3] += [offset*1000.0/dvifont.size, next[3][0]]
elt[4] = next[4]
del seq[i+1]
continue
Modified: trunk/matplotlib/lib/matplotlib/dviread.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/dviread.py 2007-09-12 20:37:41 UTC (rev 3844)
+++ trunk/matplotlib/lib/matplotlib/dviread.py 2007-09-13 06:29:14 UTC (rev 3845)
@@ -84,16 +84,22 @@
e = 0 # zero depth
else: # glyph
x,y,font,g,w = elt
- h = (font.scale * font.tfm.height[g]) >> 20
- e = (font.scale * font.tfm.depth[g]) >> 20
+ h = _mul2012(font._scale, font._tfm.height[g])
+ e = _mul2012(font._scale, font._tfm.depth[g])
minx = min(minx, x)
miny = min(miny, y - h)
maxx = max(maxx, x + w)
maxy = max(maxy, y + e)
maxy_pure = max(maxy_pure, y)
+ if self.dpi is None:
+ # special case for ease of debugging: output raw dvi coordinates
+ return mpl_cbook.Bunch(text=self.text, boxes=self.boxes,
+ width=maxx-minx, height=maxy_pure-miny,
+ descent=maxy-maxy_pure)
+
d = self.dpi / (72.27 * 2**16) # from TeX's "scaled points" to dpi units
- text = [ ((x-minx)*d, (maxy-y)*d, DviFont(f), g, w*d)
+ text = [ ((x-minx)*d, (maxy-y)*d, f, g, w*d)
for (x,y,f,g,w) in self.text ]
boxes = [ ((x-minx)*d, (maxy-y)*d, h*d, w*d) for (x,y,h,w) in self.boxes ]
@@ -110,11 +116,11 @@
while True:
byte = ord(self.file.read(1))
self._dispatch(byte)
- if self.state == _dvistate.inpage:
- matplotlib.verbose.report(
- 'Dvi._read: after %d at %f,%f' %
- (byte, self.h, self.v),
- 'debug-annoying')
+# if self.state == _dvistate.inpage:
+# matplotlib.verbose.report(
+# 'Dvi._read: after %d at %f,%f' %
+# (byte, self.h, self.v),
+# 'debug-annoying')
if byte == 140: # end of page
return True
if self.state == _dvistate.post_post: # end of file
@@ -225,21 +231,11 @@
# I think we can assume this is constant
self.state = _dvistate.outer
- def _width_of(self, char, font):
- width = font.tfm.width.get(char, None)
- if width is not None:
- return (width * font.scale) >> 20
-
- matplotlib.verbose.report(
- 'No width for char %d in font %s' % (char, font.name),
- 'debug')
- return 0
-
def _set_char(self, char):
if self.state != _dvistate.inpage:
raise ValueError, "misplaced set_char in dvi file"
self._put_char(char)
- self.h += self._width_of(char, self.fonts[self.f])
+ self.h += self.fonts[self.f]._width_of(char)
def _set_rule(self, a, b):
if self.state != _dvistate.inpage:
@@ -251,20 +247,33 @@
if self.state != _dvistate.inpage:
raise ValueError, "misplaced put_char in dvi file"
font = self.fonts[self.f]
- if font.vf is None:
+ if font._vf is None:
self.text.append((self.h, self.v, font, char,
- self._width_of(char, font)))
+ font._width_of(char)))
+# matplotlib.verbose.report(
+# 'Dvi._put_char: %d,%d %d' %(self.h, self.v, char),
+# 'debug-annoying')
else:
- self.text.extend([(self.h + x, self.v + y, f, g, w)
- for x, y, f, g, w in font.vf[char].text])
- self.boxes.extend([(self.h + x, self.v + y, a, b)
- for x, y, a, b in font.vf[char].boxes])
+ scale = font._scale
+ for x, y, f, g, w in font._vf[char].text:
+ newf = DviFont(scale=_mul2012(scale, f._scale),
+ tfm=f._tfm, texname=f.texname, vf=f._vf)
+ self.text.append((self.h + _mul2012(x, scale),
+ self.v + _mul2012(y, scale),
+ newf, g, newf._width_of(g)))
+ self.boxes.extend([(self.h + _mul2012(x, scale),
+ self.v + _mul2012(y, scale),
+ _mul2012(a, scale), _mul2012(b, scale))
+ for x, y, a, b in font._vf[char].boxes])
def _put_rule(self, a, b):
if self.state != _dvistate.inpage:
raise ValueError, "misplaced put_rule in dvi file"
if a > 0 and b > 0:
self.boxes.append((self.h, self.v, a, b))
+# matplotlib.verbose.report(
+# 'Dvi._put_rule: %d,%d %d,%d' % (self.h, self.v, a, b),
+# 'debug-annoying')
def _nop(self):
pass
@@ -357,7 +366,7 @@
vf = _vffile(n[-l:])
- self.fonts[k] = mpl_cbook.Bunch(scale=s, tfm=tfm, name=n, vf=vf)
+ self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf)
def _post(self):
if self.state != _dvistate.outer:
@@ -370,17 +379,20 @@
raise NotImplementedError
class DviFont(object):
- __slots__ = ('texname', 'size')
+ """
+ Object that holds a font's texname and size and supports comparison.
+ There are also internal attributes (for use by dviread.py) that
+ are _not_ used for comparison.
- def __init__(self, f):
- """
- Object that holds a font's texname and size and supports comparison.
+ The size is in Adobe points (converted from TeX points).
+ """
+ __slots__ = ('texname', 'size', '_scale', '_vf', '_tfm')
- The size is in Adobe points (converted from TeX points).
- """
+ def __init__(self, scale, tfm, texname, vf):
+ self._scale, self._tfm, self.texname, self._vf = \
+ scale, tfm, texname, vf
# TODO: would it make more sense to have the size in dpi units?
- self.texname = f.name
- self.size = f.scale * (72.0 / (72.27 * 2**16))
+ self.size = scale * (72.0 / (72.27 * 2**16))
def __eq__(self, other):
return self.__class__ == other.__class__ and \
@@ -389,6 +401,16 @@
def __ne__(self, other):
return not self.__eq__(other)
+ def _width_of(self, char):
+ width = self._tfm.width.get(char, None)
+ if width is not None:
+ return _mul2012(width, self._scale)
+
+ matplotlib.verbose.report(
+ 'No width for char %d in font %s' % (char, self.texname),
+ 'debug')
+ return 0
+
class Vf(Dvi):
"""
A virtual font (*.vf file) containing subroutines for dvi files.
@@ -465,7 +487,8 @@
raise ValueError, "pre command in middle of vf file"
if i != 202:
raise ValueError, "Unknown vf format %d" % i
- matplotlib.verbose.report('vf file comment: ' + x, 'debug')
+ if len(x):
+ matplotlib.verbose.report('vf file comment: ' + x, 'debug')
self.state = _dvistate.outer
# cs = checksum, ds = design size
@@ -474,7 +497,7 @@
if self._first_font is None:
self._first_font = k
-def fix2comp(num):
+def _fix2comp(num):
"""
Convert from two's complement to negative.
"""
@@ -484,6 +507,13 @@
else:
return num
+def _mul2012(num1, num2):
+ """
+ Multiply two numbers in 20.12 fixed point format.
+ """
+ # Separated into a function because >> has surprising precedence
+ return (num1*num2) >> 20
+
class Tfm(object):
"""
A TeX Font Metric file. This implementation covers only the bare
@@ -497,6 +527,7 @@
(this is a dict because indexing may not start from 0)
height[i], depth[i]: height and depth of character #i
"""
+ __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth')
def __init__(self, filename):
matplotlib.verbose.report('opening tfm file ' + filename, 'debug')
@@ -525,9 +556,9 @@
[ struct.unpack('!%dI' % (len(x)/4), x)
for x in (widths, heights, depths) ]
for i in range(ec-bc):
- self.width[bc+i] = fix2comp(widths[ord(char_info[4*i])])
- self.height[bc+i] = fix2comp(heights[ord(char_info[4*i+1]) >> 4])
- self.depth[bc+i] = fix2comp(depths[ord(char_info[4*i+1]) & 0xf])
+ self.width[bc+i] = _fix2comp(widths[ord(char_info[4*i])])
+ self.height[bc+i] = _fix2comp(heights[ord(char_info[4*i+1]) >> 4])
+ self.depth[bc+i] = _fix2comp(depths[ord(char_info[4*i+1]) & 0xf])
class PsfontsMap(object):
@@ -552,6 +583,7 @@
the pdf-related files perhaps only avoid the "Base 14" pdf fonts.
But the user may have configured these files differently.
"""
+ __slots__ = ('_font',)
def __init__(self, filename):
self._font = {}
@@ -627,7 +659,17 @@
encoding=encoding, filename=filename)
class Encoding(object):
+ """
+ Parses a *.enc file referenced from a psfonts.map style file.
+ The format this class understands is a very limited subset of
+ PostScript.
+ Usage (subject to change):
+ for name in Encoding(filename):
+ whatever(name)
+ """
+ __slots__ = ('encoding',)
+
def __init__(self, filename):
file = open(filename, 'rt')
try:
@@ -694,6 +736,10 @@
return result
+# With multiple text objects per figure (e.g. tick labels) we may end
+# up reading the same tfm and vf files many times, so we implement a
+# simple cache. TODO: is this worth making persistent?
+
_tfmcache = {}
_vfcache = {}
@@ -721,19 +767,22 @@
if __name__ == '__main__':
- matplotlib.verbose.set_level('debug')
- dvi = Dvi('foo.dvi', 72)
+ import sys
+ matplotlib.verbose.set_level('debug-annoying')
+ fname = sys.argv[1]
+ try: dpi = float(sys.argv[2])
+ except IndexError: dpi = None
+ dvi = Dvi(fname, dpi)
fontmap = PsfontsMap(find_tex_file('pdftex.map'))
- for text,boxes in dvi:
+ for page in dvi:
print '=== new page ==='
fPrev = None
- for x,y,f,c in text:
- texname = dvi.fonts[f].name
- print x,y,c,chr(c),texname
+ for x,y,f,c,w in page.text:
if f != fPrev:
- print 'font', texname, '=', fontmap[texname].__dict__
+ print 'font', f.texname, 'scaled', f._scale/pow(2.0,20)
fPrev = f
- for x,y,w,h in boxes:
+ print x,y,c, 32 <= c < 128 and chr(c) or '.', w
+ for x,y,w,h in page.boxes:
print x,y,'BOX',w,h
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <jd...@us...> - 2007-09-12 20:37:47
|
Revision: 3844
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3844&view=rev
Author: jdh2358
Date: 2007-09-12 13:37:41 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
fixed a bar units bug
Modified Paths:
--------------
trunk/matplotlib/API_CHANGES
trunk/matplotlib/CHANGELOG
trunk/matplotlib/lib/matplotlib/artist.py
trunk/matplotlib/lib/matplotlib/axes.py
trunk/matplotlib/lib/matplotlib/axis.py
trunk/matplotlib/lib/matplotlib/mlab.py
trunk/matplotlib/lib/matplotlib/patches.py
Added Paths:
-----------
trunk/matplotlib/examples/units/bar_demo2.py
Modified: trunk/matplotlib/API_CHANGES
===================================================================
--- trunk/matplotlib/API_CHANGES 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/API_CHANGES 2007-09-12 20:37:41 UTC (rev 3844)
@@ -1,3 +1,5 @@
+ Made skiprows=1 the default on csv2rec
+
The gd and paint backends have been deleted.
The errorbar method and function now accept additional kwargs
Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/CHANGELOG 2007-09-12 20:37:41 UTC (rev 3844)
@@ -1,3 +1,7 @@
+2007-09-12 Fixed a Axes.bar unit bug - JDH
+
+2007-09-10 Made skiprows=1 the default on csv2rec - JDH
+
2007-09-09 Split out the plotting part of pylab and put it in
pyplot.py; removed numerix from the remaining pylab.py,
which imports everything from pyplot.py. The intention
Added: trunk/matplotlib/examples/units/bar_demo2.py
===================================================================
--- trunk/matplotlib/examples/units/bar_demo2.py (rev 0)
+++ trunk/matplotlib/examples/units/bar_demo2.py 2007-09-12 20:37:41 UTC (rev 3844)
@@ -0,0 +1,34 @@
+"""
+plot using a variety of cm vs inches conversions. The example shows
+how default unit instrospection works (ax1), how various keywords can
+be used to set the x and y units to override the defaults (ax2, ax3,
+ax4) and how one can set the xlimits using scalars (ax3, current units
+assumed) or units (conversions applied to get the numbers to current
+units)
+
+"""
+from basic_units import cm, inch
+from pylab import figure, show, nx
+
+cms = cm *nx.arange(0, 10, 2)
+bottom=0*cm
+width=0.8*cm
+
+fig = figure()
+
+ax1 = fig.add_subplot(2,2,1)
+ax1.bar(cms, cms, bottom=bottom)
+
+ax2 = fig.add_subplot(2,2,2)
+ax2.bar(cms, cms, bottom=bottom, width=width, xunits=cm, yunits=inch)
+
+ax3 = fig.add_subplot(2,2,3)
+ax3.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm)
+ax3.set_xlim(3, 6) # scalars are interpreted in current units
+
+ax4 = fig.add_subplot(2,2,4)
+ax4.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=inch)
+#fig.savefig('simple_conversion_plot.png')
+ax4.set_xlim(3*cm, 6*cm) # cm are converted to inches
+
+show()
Modified: trunk/matplotlib/lib/matplotlib/artist.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/artist.py 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/lib/matplotlib/artist.py 2007-09-12 20:37:41 UTC (rev 3844)
@@ -51,7 +51,7 @@
self._remove_method = None
def remove(self):
- '''
+ """
Remove the artist from the figure if possible. The effect will not
be visible until the figure is redrawn, e.g., with ax.draw_idle().
Call ax.relim() to update the axes limits if desired.
@@ -60,7 +60,7 @@
was added to axes with autolim=True.
Note: there is no support for removing the artist's legend entry.
- '''
+ """
# There is no method to set the callback. Instead the parent should set
# the _remove_method attribute directly. This would be a protected
Modified: trunk/matplotlib/lib/matplotlib/axes.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/axes.py 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/lib/matplotlib/axes.py 2007-09-12 20:37:41 UTC (rev 3844)
@@ -1198,23 +1198,27 @@
def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
'look for unit kwargs and update the axis instances as necessary'
- if self.xaxis is None or self.xaxis is None: return
+ if self.xaxis is None or self.yaxis is None: return
-
+ #print 'processing', self.get_geometry()
if xdata is not None:
self.xaxis.update_units(xdata)
+ #print '\tset from xdata', self.xaxis.units
if ydata is not None:
self.yaxis.update_units(ydata)
+ #print '\tset from ydata', self.yaxis.units
# process kwargs 2nd since these will override default units
if kwargs is not None:
xunits = kwargs.pop( 'xunits', self.xaxis.units)
if xunits!=self.xaxis.units:
+ #print '\tkw setting xunits', xunits
self.xaxis.set_units(xunits)
yunits = kwargs.pop('yunits', self.yaxis.units)
if yunits!=self.yaxis.units:
+ #print '\tkw setting yunits', yunits
self.yaxis.set_units(yunits)
def in_axes(self, xwin, ywin):
@@ -3114,11 +3118,13 @@
else:
raise ValueError, 'invalid orientation: %s' % orientation
- left = npy.asarray(left)
- height = npy.asarray(height)
- width = npy.asarray(width)
- bottom = npy.asarray(bottom)
+ # do not convert to array here as unit info is lost
+ #left = npy.asarray(left)
+ #height = npy.asarray(height)
+ #width = npy.asarray(width)
+ #bottom = npy.asarray(bottom)
+
if len(linewidth) == 1: linewidth = linewidth * nbars
# if color looks like a color string, an RGB tuple or a
@@ -3161,14 +3167,14 @@
# lets do some conversions now
if self.xaxis is not None:
xconv = self.xaxis.converter
- if ( xconv ):
+ if xconv is not None:
units = self.xaxis.get_units()
left = xconv.convert( left, units )
width = xconv.convert( width, units )
if self.yaxis is not None:
yconv = self.yaxis.converter
- if ( yconv ):
+ if yconv is not None :
units = self.yaxis.get_units()
bottom = yconv.convert( bottom, units )
height = yconv.convert( height, units )
@@ -3208,12 +3214,14 @@
if xerr is not None or yerr is not None:
if orientation == 'vertical':
- x = left + 0.5*width
- y = bottom + height
+ # using list comps rather than arrays to preserve unit info
+ x = [l+0.5*w for l, w in zip(left, width)]
+ y = [b+h for b,h in zip(bottom, height)]
elif orientation == 'horizontal':
- x = left + width
- y = bottom + 0.5*height
+ # using list comps rather than arrays to preserve unit info
+ x = [l+w for l,w in zip(left, width)]
+ y = [b+0.5*h for b,h in zip(bottom, height)]
self.errorbar(
x, y,
Modified: trunk/matplotlib/lib/matplotlib/axis.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/axis.py 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/lib/matplotlib/axis.py 2007-09-12 20:37:41 UTC (rev 3844)
@@ -828,7 +828,7 @@
return x
ret = self.converter.convert(x, self.units)
- #print 'convert_units converting: units=%s, converter=%s, in=%s, out=%s'%(self.units, self.converter, x, ret)
+ #print 'convert_units converting: axis=%s, units=%s, converter=%s, in=%s, out=%s'%(self, self.units, self.converter, x, ret)
return ret
def set_units(self, u):
Modified: trunk/matplotlib/lib/matplotlib/mlab.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/mlab.py 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/lib/matplotlib/mlab.py 2007-09-12 20:37:41 UTC (rev 3844)
@@ -1253,9 +1253,9 @@
if r==1 or c==1:
X.shape = max(r,c),
if unpack: return X.transpose()
- return X
+ else: return X
-def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',',
+def csv2rec(fname, comments='#', skiprows=1, checkrows=5, delimiter=',',
converterd=None, names=None, missing=None):
"""
Load data from comma/space/tab delimited file in fname into a
Modified: trunk/matplotlib/lib/matplotlib/patches.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/patches.py 2007-09-12 20:25:17 UTC (rev 3843)
+++ trunk/matplotlib/lib/matplotlib/patches.py 2007-09-12 20:37:41 UTC (rev 3844)
@@ -77,6 +77,8 @@
if len(kwargs): artist.setp(self, **kwargs)
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
+
+
def contains(self, mouseevent):
"""Test whether the mouse event occurred in the patch.
@@ -347,7 +349,6 @@
Return the vertices of the rectangle
"""
x, y = self.xy
-
left, right = self.convert_xunits((x, x + self.width))
bottom, top = self.convert_yunits((y, y + self.height))
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <jd...@us...> - 2007-09-12 20:25:19
|
Revision: 3843
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3843&view=rev
Author: jdh2358
Date: 2007-09-12 13:25:17 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
minor changes for gtk navigation
Modified Paths:
--------------
branches/transforms/lib/matplotlib/backends/backend_gtk.py
branches/transforms/lib/matplotlib/backends/backend_gtkagg.py
Modified: branches/transforms/lib/matplotlib/backends/backend_gtk.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_gtk.py 2007-09-12 19:47:56 UTC (rev 3842)
+++ branches/transforms/lib/matplotlib/backends/backend_gtk.py 2007-09-12 20:25:17 UTC (rev 3843)
@@ -405,8 +405,8 @@
self.toolbar = self._get_toolbar(canvas)
# calculate size for window
- w = int (self.canvas.figure.bbox.width())
- h = int (self.canvas.figure.bbox.height())
+ w = int (self.canvas.figure.bbox.width)
+ h = int (self.canvas.figure.bbox.height)
if self.toolbar is not None:
self.toolbar.show()
@@ -518,7 +518,7 @@
gc = drawable.new_gc()
- height = self.canvas.figure.bbox.height()
+ height = self.canvas.figure.bbox.height
y1 = height - y1
y0 = height - y0
@@ -639,8 +639,8 @@
toolfig.subplots_adjust(top=0.9)
tool = SubplotTool(self.canvas.figure, toolfig)
- w = int (toolfig.bbox.width())
- h = int (toolfig.bbox.height())
+ w = int (toolfig.bbox.width)
+ h = int (toolfig.bbox.height)
window = gtk.Window()
Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-12 19:47:56 UTC (rev 3842)
+++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-12 20:25:17 UTC (rev 3843)
@@ -60,8 +60,9 @@
w,h = widget.window.get_size()
if w==1 or h==1: return # empty fig
+ # dpival = self.figure.dpi.get() MGDTODO
# compute desired figure size in inches
- dpival = self.figure.dpi.get()
+ dpival = self.figure.dpi
winch = w/dpival
hinch = h/dpival
self.figure.set_size_inches(winch, hinch)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-12 19:48:00
|
Revision: 3842
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3842&view=rev
Author: mdboom
Date: 2007-09-12 12:47:56 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
More progress. Zooming and panning working thanks to John's patch.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/lib/matplotlib/artist.py
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/backends/backend_tkagg.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-12 19:47:56 UTC (rev 3842)
@@ -8,9 +8,16 @@
from numpy.linalg import inv
from sets import Set
+# MGDTODO: The name of this module is bad, since it deals with
+# non-affine transformations as well. It should probably just be
+# "transforms", but we already had one of those... ;)
+
# MGDTODO: This creates a ton of cyclical references. We may want to
# consider using weak references
+# MGDTODO: deep copying is probably incorrect wrt the parent/child
+# relationships
+
class TransformNode(object):
def __init__(self):
self._parents = Set()
@@ -48,29 +55,31 @@
points = N.array(args, dtype=N.float_).reshape(2, 2)
return Bbox(points)
from_lbrt = staticmethod(from_lbrt)
+
+ def __copy__(self):
+ return Bbox(self._points.copy())
+ def __deepcopy__(self, memo):
+ return Bbox(self._points.copy())
+
def __cmp__(self, other):
# MGDTODO: Totally suboptimal
- if isinstance(other, Bbox):
- if (self._points == other._points).all():
- return 0
+ if isinstance(other, Bbox) and (self._points == other._points).all():
+ return 0
return -1
-
+
+ def __repr__(self):
+ return 'Bbox(%s)' % repr(self._points)
+ __str__ = __repr__
+
# JDH: the update method will update the box limits from the
# existing limits and the new data; it appears here you are just
# using the new data. We use an "ignore" flag to specify whether
# you want to include the existing data or not in the update
- def update_from_data(self, x, y):
+ def update_from_data(self, x, y, ignore=True):
self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_)
self.invalidate()
-
- def copy(self):
- return Bbox(self._points.copy())
- def __repr__(self):
- return 'Bbox(%s)' % repr(self._points)
- __str__ = __repr__
-
# MGDTODO: Probably a more efficient ways to do this...
def _get_xmin(self):
return self._points[0, 0]
@@ -136,19 +145,24 @@
return self.ymax - self.ymin
height = property(_get_height)
+ def _get_bounds(self):
+ return (self.xmin, self.ymin,
+ self.xmax - self.xmin, self.ymax - self.ymin)
+ def _set_bounds(self, bounds):
+ l,b,w,h = bounds
+ self._points = N.array([[l, b], [l+w, b+h]], N.float_)
+ self.invalidate()
+ bounds = property(_get_bounds, _set_bounds)
+
def transformed(self, transform):
return Bbox(transform(self._points))
def inverse_transformed(self, transform):
return Bbox(transform.inverted()(self._points))
- def get_bounds(self):
- return (self.xmin, self.ymin,
- self.xmax - self.xmin, self.ymax - self.ymin)
-
def expanded(self, sw, sh):
- width = self.width()
- height = self.height()
+ width = self.width
+ height = self.height
deltaw = (sw * width - width) / 2.0
deltah = (sh * height - height) / 2.0
a = N.array([[-deltaw, -deltah], [deltaw, deltah]])
@@ -199,6 +213,9 @@
if isinstance(other, Transform):
return composite_transform_factory(other, self)
raise TypeError("Can not add Transform to object of type '%s'" % type(other))
+
+ def transform_point(self, point):
+ return self.__call__([point])[0]
def has_inverse(self):
raise NotImplementedError()
@@ -211,49 +228,27 @@
def is_affine(self):
return False
-
-class Affine2D(Transform):
+
+# MGDTODO: Separate out Affine2DBase / Affine2DConcrete so BlendedAffine and CompositeAffine don't have translate/scale/rotate members
+
+class Affine2DBase(Transform):
input_dims = 2
output_dims = 2
-
- def __init__(self, matrix = None):
- """
- Initialize an Affine transform from a 3x3 numpy float array.
- a c e
- b d f
- 0 0 1
- """
+ def __init__(self):
Transform.__init__(self)
- if matrix is None:
- matrix = N.identity(3)
- else:
- assert matrix.shape == (3, 3)
- self._mtx = matrix
self._inverted = None
- def __repr__(self):
- return "Affine2D(%s)" % repr(self._mtx)
- __str__ = __repr__
-
- def __cmp__(self, other):
- # MGDTODO: We need to decide if we want deferred transforms
- # to be equal to this one
- if isinstance(other, Affine2D):
- if (self.get_matrix() == other.get_matrix()).all():
- return 0
- return -1
-
def _do_invalidation(self):
result = self._inverted is None
self._inverted = None
return result
+
+ #@staticmethod
+ def _concat(a, b):
+ return N.dot(b, a)
+ _concat = staticmethod(_concat)
- #@staticmethod
- def from_values(a, b, c, d, e, f):
- return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f))
- from_values = staticmethod(from_values)
-
def to_values(self):
mtx = self.get_matrix()
return tuple(mtx[:2].swapaxes(0, 1).flatten())
@@ -268,7 +263,7 @@
matrix_from_values = staticmethod(matrix_from_values)
def get_matrix(self):
- return self._mtx
+ raise NotImplementedError()
def __call__(self, points):
"""
@@ -278,26 +273,74 @@
points must be a numpy array of shape (N, 2), where N is the
number of points.
"""
- # MGDTODO: This involves a copy. We may need to do something like
- # http://neuroimaging.scipy.org/svn/ni/ni/trunk/neuroimaging/core/reference/mapping.py
- # to separate the matrix out into the translation and scale components
- # and apply each separately (which is still sub-optimal)
-
- # This is easier for now, however, since we can just keep a
- # regular affine matrix around
- # MGDTODO: Trap cases where this isn't an array and fix there
+ # MGDTODO: The major speed trap here is just converting to
+ # the points to an array in the first place. If we can use
+ # more arrays upstream, that should help here.
mtx = self.get_matrix()
points = N.asarray(points, N.float_)
- new_points = points.swapaxes(0, 1)
- new_points = N.vstack((new_points, N.ones((1, points.shape[0]))))
- result = N.dot(mtx, new_points)[:2]
- return result.swapaxes(0, 1)
+ points = points.transpose()
+ points = N.dot(mtx[0:2, 0:2], points)
+ points = points + mtx[0:2, 2:]
+ return points.transpose()
+ def inverted(self):
+ if self._inverted is None:
+ mtx = self.get_matrix()
+ self._inverted = Affine2D(inv(mtx))
+ return self._inverted
+
+ def is_separable(self):
+ mtx = self.get_matrix()
+ return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
+
+ def is_affine(self):
+ return True
+
+
+class Affine2D(Affine2DBase):
+ input_dims = 2
+ output_dims = 2
+
+ def __init__(self, matrix = None):
+ """
+ Initialize an Affine transform from a 3x3 numpy float array.
+
+ a c e
+ b d f
+ 0 0 1
+ """
+ Affine2DBase.__init__(self)
+ if matrix is None:
+ matrix = N.identity(3)
+ else:
+ assert matrix.shape == (3, 3)
+ self._mtx = matrix
+ self._inverted = None
+
+ def __repr__(self):
+ return "Affine2D(%s)" % repr(self._mtx)
+ __str__ = __repr__
+
+ def __cmp__(self, other):
+ if (isinstance(other, Affine2D) and
+ (self.get_matrix() == other.get_matrix()).all()):
+ return 0
+ return -1
+
+ def __copy__(self):
+ return Affine2D(self._mtx.copy())
+
+ def __deepcopy__(self, memo):
+ return Affine2D(self._mtx.copy())
+
#@staticmethod
- def _concat(a, b):
- return N.dot(b, a)
- _concat = staticmethod(_concat)
+ def from_values(a, b, c, d, e, f):
+ return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f))
+ from_values = staticmethod(from_values)
+ def get_matrix(self):
+ return self._mtx
+
#@staticmethod
def concat(a, b):
return Affine2D(Affine2D._concat(a._mtx, b._mtx))
@@ -346,19 +389,18 @@
def is_affine(self):
return True
-class BlendedAffine2D(Affine2D):
+class BlendedAffine2D(Affine2DBase):
def __init__(self, x_transform, y_transform):
assert x_transform.is_affine()
assert y_transform.is_affine()
assert x_transform.is_separable()
assert y_transform.is_separable()
- Transform.__init__(self)
+ Affine2DBase.__init__(self)
self.add_children([x_transform, y_transform])
self._x = x_transform
self._y = y_transform
self._mtx = None
- self._inverted = None
def __repr__(self):
return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
@@ -367,7 +409,7 @@
def _do_invalidation(self):
if self._mtx is not None:
self._mtx = None
- Affine2D._do_invalidation(self)
+ Affine2DBase._do_invalidation(self)
return False
return True
@@ -376,8 +418,9 @@
x_mtx = self._x.get_matrix()
y_mtx = self._y.get_matrix()
# This works because we already know the transforms are
- # separable
- self._mtx = N.vstack([x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]])
+ # separable, though normally one would want to set b and
+ # c to zero.
+ self._mtx = N.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
def is_separable(self):
return True
@@ -397,20 +440,22 @@
self._y = y_transform
def __call__(self, points):
- # MGDTODO: Implement me
- pass
+ x_points = self._x(points)
+ y_points = self._y(points)
+ # This works because we already know the transforms are
+ # separable
+ return N.hstack((x_points[:, 0:1], y_points[:, 1:2]))
-class CompositeAffine2D(Affine2D):
+class CompositeAffine2D(Affine2DBase):
def __init__(self, a, b):
assert a.is_affine()
assert b.is_affine()
- Transform.__init__(self)
+ Affine2DBase.__init__(self)
self.add_children([a, b])
self._a = a
self._b = b
self._mtx = None
- self._inverted = None
def __repr__(self):
return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
@@ -418,7 +463,7 @@
def _do_invalidation(self):
self._mtx = None
- Affine2D._do_invalidation(self)
+ Affine2DBase._do_invalidation(self)
def _make__mtx(self):
if self._mtx is None:
@@ -433,22 +478,23 @@
class CompositeTransform(Transform):
def __init__(self, a, b):
assert a.output_dims == b.input_dims
-
+ self.input_dims = a.input_dims
+ self.output_dims = b.output_dims
+
Transform.__init__(self)
self.add_children([a, b])
self._a = a
self._b = b
def __call__(self, points):
- # MGDTODO: Optimize here by concatenating affines if possible
return self._b(self._a(points))
-class BboxTransform(Affine2D):
+class BboxTransform(Affine2DBase):
def __init__(self, boxin, boxout):
assert isinstance(boxin, Bbox)
assert isinstance(boxout, Bbox)
- Transform.__init__(self)
+ Affine2DBase.__init__(self)
self.add_children([boxin, boxout])
self._boxin = boxin
self._boxout = boxout
@@ -462,7 +508,7 @@
def _do_invalidation(self):
if self._mtx is not None:
self._mtx = None
- Affine2D._do_invalidation(self)
+ Affine2DBase._do_invalidation(self)
return False
return True
@@ -481,14 +527,6 @@
self._mtx = affine._mtx
- def __call__(self, points):
- self._make__mtx()
- return Affine2D.__call__(self, points)
-
- def inverted(self):
- self._make__mtx()
- return Affine2D.inverted(self)
-
def is_separable(self):
return True
@@ -541,6 +579,9 @@
return interval[0] < val and interval[1] > val
if __name__ == '__main__':
+ from random import random
+ import timeit
+
bbox = Bbox.from_lbrt(10., 15., 20., 25.)
assert bbox.xmin == 10
assert bbox.ymin == 15
@@ -589,7 +630,9 @@
scale = Affine2D().scale(10, 20)
assert scale.to_values() == (10, 0, 0, 20, 0, 0)
rotation = Affine2D().rotate_deg(30)
- print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, -0.49999999999999994, 0.86602540378443871, 0.0, 0.0)
+ print rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
+ -0.49999999999999994, 0.86602540378443871,
+ 0.0, 0.0)
points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_)
translated_points = translation(points)
@@ -600,11 +643,20 @@
print rotated_points
tpoints1 = rotation(translation(scale(points)))
- trans_sum = rotation + translation + scale
+ trans_sum = scale + translation + rotation
tpoints2 = trans_sum(points)
print tpoints1, tpoints2
print tpoints1 == tpoints2
# Need to do some sort of fuzzy comparison here?
# assert (tpoints1 == tpoints2).all()
+
+ # Here are some timing tests
+ points = [(random(), random()) for i in xrange(10000)]
+ t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points")
+ print "Time to transform 10000 x 10 points as tuples:", t.timeit(10)
+
+ points2 = N.asarray(points)
+ t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, points2")
+ print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10)
__all__ = ['Transform', 'Affine2D']
Modified: branches/transforms/lib/matplotlib/artist.py
===================================================================
--- branches/transforms/lib/matplotlib/artist.py 2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/artist.py 2007-09-12 19:47:56 UTC (rev 3842)
@@ -337,7 +337,7 @@
def _set_gc_clip(self, gc):
'set the clip properly for the gc'
if self.clipbox is not None:
- gc.set_clip_rectangle(self.clipbox.get_bounds())
+ gc.set_clip_rectangle(self.clipbox.bounds)
gc.set_clip_path(self._clippath)
def draw(self, renderer, *args, **kwargs):
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-12 19:47:56 UTC (rev 3842)
@@ -1,5 +1,5 @@
from __future__ import division, generators
-import math, sys, warnings
+import math, sys, warnings, copy
import numpy as npy
@@ -483,7 +483,7 @@
"""
martist.Artist.__init__(self)
self._position = maffine.Bbox.from_lbwh(*rect)
- self._originalPosition = self._position.copy()
+ self._originalPosition = copy.deepcopy(self._position)
self.set_axes(self)
self.set_aspect('auto')
self.set_adjustable('box')
@@ -613,7 +613,7 @@
"""
martist.Artist.set_figure(self, fig)
- l, b, w, h = self._position.get_bounds()
+ l, b, w, h = self._position.bounds
xmin = fig.bbox.xmin
xmax = fig.bbox.xmax
ymin = fig.bbox.ymin
@@ -669,9 +669,9 @@
def get_position(self, original=False):
'Return the axes rectangle left, bottom, width, height'
if original:
- return self._originalPosition[:]
+ return self._originalPosition.bounds
else:
- return self._position[:]
+ return self._position.bounds
# return [val.get() for val in self._position]
def set_position(self, pos, which='both'):
@@ -694,10 +694,10 @@
# # Change values within self._position--don't replace it.
# for num,val in zip(pos, self._position):
# val.set(num)
- self._position = pos
+ self._position.bounds = pos.bounds
# MGDTODO: side-effects
if which in ('both', 'original'):
- self._originalPosition = pos
+ self._originalPosition.bounds = pos.bounds
def _set_artist_props(self, a):
@@ -1547,7 +1547,9 @@
def get_xscale(self):
'return the xaxis scale string: log or linear'
- return self.scaled[self.transData.get_funcx().get_type()]
+ # MGDTODO
+ # return self.scaled[self.transData.get_funcx().get_type()]
+ return 'linear'
def set_xscale(self, value, basex = 10, subsx=None):
"""
@@ -1671,7 +1673,8 @@
def get_yscale(self):
'return the yaxis scale string: log or linear'
- return self.scaled[self.transData.get_funcy().get_type()]
+ # return self.scaled[self.transData.get_funcy().get_type()]
+ return 'linear'
def set_yscale(self, value, basey=10, subsy=None):
"""
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-12 19:47:56 UTC (rev 3842)
@@ -4,7 +4,7 @@
"""
from __future__ import division
-import os, sys, warnings
+import os, sys, warnings, copy
import numpy as npy
import matplotlib.numerix.npyma as ma
@@ -1070,7 +1070,7 @@
def get_width_height(self):
"""return the figure width and height in points or pixels
(depending on the backend), truncated to integers"""
- return int(self.figure.bbox.width()), int(self.figure.bbox.height())
+ return int(self.figure.bbox.width), int(self.figure.bbox.height)
filetypes = {
'emf': 'Enhanced Metafile',
@@ -1544,7 +1544,7 @@
xmin, xmax = a.get_xlim()
ymin, ymax = a.get_ylim()
lim = xmin, xmax, ymin, ymax
- self._xypress.append((x, y, a, i, lim,a.transData.deepcopy()))
+ self._xypress.append((x, y, a, i, lim, copy.deepcopy(a.transData)))
self.canvas.mpl_disconnect(self._idDrag)
self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan)
@@ -1571,7 +1571,7 @@
xmin, xmax = a.get_xlim()
ymin, ymax = a.get_ylim()
lim = xmin, xmax, ymin, ymax
- self._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() ))
+ self._xypress.append(( x, y, a, i, lim, copy.deepcopy(a.transData) ))
self.press(event)
@@ -1637,8 +1637,9 @@
#safer to use the recorded button at the press than current button:
#multiple button can get pressed during motion...
if self._button_pressed==1:
- lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )
- x, y = trans.inverse_xy_tup( (event.x, event.y) )
+ inverse = trans.inverted()
+ lastx, lasty = inverse.transform_point((lastx, lasty))
+ x, y = inverse.transform_point( (event.x, event.y) )
if a.get_xscale()=='log':
dx=1-lastx/x
else:
@@ -1664,15 +1665,16 @@
ymax -= dy
elif self._button_pressed==3:
try:
- dx=(lastx-event.x)/float(a.bbox.width())
- dy=(lasty-event.y)/float(a.bbox.height())
+ dx=(lastx-event.x)/float(a.bbox.width)
+ dy=(lasty-event.y)/float(a.bbox.height)
dx,dy=format_deltas(event,dx,dy)
if a.get_aspect() != 'auto':
dx = 0.5*(dx + dy)
dy = dx
alphax = pow(10.0,dx)
alphay = pow(10.0,dy)#use logscaling, avoid singularities and smother scaling...
- lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )
+ inverse = trans.inverted()
+ lastx, lasty = inverse.transform_point( (lastx, lasty) )
if a.get_xscale()=='log':
xmin = lastx*(xmin/lastx)**alphax
xmax = lastx*(xmax/lastx)**alphax
@@ -1710,8 +1712,9 @@
xmin, ymin, xmax, ymax = lim
# zoom to rect
- lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) )
- x, y = a.transData.inverse_xy_tup( (x, y) )
+ inverse = a.transData.inverted()
+ lastx, lasty = inverse.transform_point( (lastx, lasty) )
+ x, y = inverse.transform_point( (x, y) )
Xmin,Xmax=a.get_xlim()
Ymin,Ymax=a.get_ylim()
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-12 19:47:56 UTC (rev 3842)
@@ -398,7 +398,7 @@
self.figure.draw(self.renderer)
def get_renderer(self):
- l,b,w,h = self.figure.bbox.get_bounds()
+ l,b,w,h = self.figure.bbox.bounds
# MGDTODO
# key = w, h, self.figure.dpi.get()
key = w, h, self.figure.dpi
Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-12 19:47:56 UTC (rev 3842)
@@ -147,7 +147,7 @@
def __init__(self, figure, master=None, resize_callback=None):
FigureCanvasAgg.__init__(self, figure)
self._idle = True
- t1,t2,w,h = self.figure.bbox.get_bounds()
+ t1,t2,w,h = self.figure.bbox.bounds
w, h = int(w), int(h)
self._tkcanvas = Tk.Canvas(
master=master, width=w, height=h, borderwidth=4)
@@ -288,7 +288,7 @@
self.window.wm_title("Figure %d" % num)
self.canvas = canvas
self._num = num
- t1,t2,w,h = canvas.figure.bbox.get_bounds()
+ t1,t2,w,h = canvas.figure.bbox.bounds
w, h = int(w), int(h)
self.window.minsize(int(w*3/4),int(h*3/4))
if matplotlib.rcParams['toolbar']=='classic':
@@ -436,7 +436,7 @@
self.canvas = canvas
self.window = window
- xmin, xmax = canvas.figure.bbox.intervalx().get_bounds()
+ xmin, xmax = canvas.figure.bbox.intervalx
height, width = 50, xmax-xmin
Tk.Frame.__init__(self, master=self.window,
width=width, height=height,
@@ -582,7 +582,7 @@
self.message.set(s)
def draw_rubberband(self, event, x0, y0, x1, y1):
- height = self.canvas.figure.bbox.height()
+ height = self.canvas.figure.bbox.height
y0 = height-y0
y1 = height-y1
try: self.lastrect
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <js...@us...> - 2007-09-12 18:22:26
|
Revision: 3841
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3841&view=rev
Author: jswhit
Date: 2007-09-12 11:22:24 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
regenerate with Cython 0.9.6.6
Modified Paths:
--------------
trunk/toolkits/basemap/src/_geod.c
trunk/toolkits/basemap/src/_proj.c
Modified: trunk/toolkits/basemap/src/_geod.c
===================================================================
--- trunk/toolkits/basemap/src/_geod.c 2007-09-12 18:14:38 UTC (rev 3840)
+++ trunk/toolkits/basemap/src/_geod.c 2007-09-12 18:22:24 UTC (rev 3841)
@@ -1,4 +1,4 @@
-/* Generated by Pyrex 0.9.6.5 on Wed Sep 12 12:12:33 2007 */
+/* Generated by Cython 0.9.6.6 on Wed Sep 12 12:20:59 2007 */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
@@ -50,8 +50,14 @@
#ifdef __GNUC__
+/* Test for GCC > 2.95 */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
+#else /* __GNUC__ > 2 ... */
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif /* __GNUC__ > 2 ... */
#else /* __GNUC__ */
#define likely(x) (x)
#define unlikely(x) (x)
Modified: trunk/toolkits/basemap/src/_proj.c
===================================================================
--- trunk/toolkits/basemap/src/_proj.c 2007-09-12 18:14:38 UTC (rev 3840)
+++ trunk/toolkits/basemap/src/_proj.c 2007-09-12 18:22:24 UTC (rev 3841)
@@ -1,4 +1,4 @@
-/* Generated by Pyrex 0.9.6.3 on Fri Aug 31 08:42:47 2007 */
+/* Generated by Cython 0.9.6.6 on Wed Sep 12 12:21:02 2007 */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
@@ -12,6 +12,8 @@
#define PY_SSIZE_T_MIN INT_MIN
#define PyInt_FromSsize_t(z) PyInt_FromLong(z)
#define PyInt_AsSsize_t(o) PyInt_AsLong(o)
+ #define PyNumber_Index(o) PyNumber_Int(o)
+ #define PyIndex_Check(o) PyNumber_Check(o)
#endif
#ifdef __cplusplus
#define __PYX_EXTERN_C extern "C"
@@ -37,6 +39,8 @@
typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/
typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/
+#define __pyx_PyIndex_AsSsize_t(b) PyInt_AsSsize_t(PyNumber_Index(b))
+
#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False))
static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
if (x == Py_True) return 1;
@@ -46,8 +50,14 @@
#ifdef __GNUC__
+/* Test for GCC > 2.95 */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
+#else /* __GNUC__ > 2 ... */
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif /* __GNUC__ > 2 ... */
#else /* __GNUC__ */
#define likely(x) (x)
#define unlikely(x) (x)
@@ -111,13 +121,10 @@
static PyObject *__pyx_k2p;
-static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
-static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
- PyObject *__pyx_v_datapath = 0;
+static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_v_datapath); /*proto*/
+static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_v_datapath) {
char (*__pyx_v_searchpath);
PyObject *__pyx_r;
- static char *__pyx_argnames[] = {"datapath",0};
- if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "O", __pyx_argnames, &__pyx_v_datapath))) return 0;
Py_INCREF(__pyx_v_datapath);
/* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":7
@@ -401,15 +408,13 @@
static PyObject *__pyx_n___class__;
-static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *unused); /*proto*/
static char __pyx_doc_5_proj_4Proj___reduce__[] = "special method that allows pyproj.Proj instance to be pickled";
-static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *unused) {
PyObject *__pyx_r;
PyObject *__pyx_1 = 0;
PyObject *__pyx_2 = 0;
PyObject *__pyx_3 = 0;
- static char *__pyx_argnames[] = {0};
- if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0;
Py_INCREF(__pyx_v_self);
/* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":37
@@ -570,7 +575,7 @@
__pyx_4 = PyNumber_Divide(__pyx_2, __pyx_3); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; goto __pyx_L1;}
Py_DECREF(__pyx_2); __pyx_2 = 0;
Py_DECREF(__pyx_3); __pyx_3 = 0;
- __pyx_5 = PyInt_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; goto __pyx_L1;}
+ __pyx_5 = __pyx_PyIndex_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; goto __pyx_L1;}
Py_DECREF(__pyx_4); __pyx_4 = 0;
__pyx_v_ndim = __pyx_5;
@@ -922,7 +927,7 @@
__pyx_4 = PyNumber_Divide(__pyx_2, __pyx_3); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; goto __pyx_L1;}
Py_DECREF(__pyx_2); __pyx_2 = 0;
Py_DECREF(__pyx_3); __pyx_3 = 0;
- __pyx_5 = PyInt_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; goto __pyx_L1;}
+ __pyx_5 = __pyx_PyIndex_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; goto __pyx_L1;}
Py_DECREF(__pyx_4); __pyx_4 = 0;
__pyx_v_ndim = __pyx_5;
@@ -1142,13 +1147,11 @@
return __pyx_r;
}
-static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
-static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *unused); /*proto*/
+static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *unused) {
int __pyx_v_i;
PyObject *__pyx_r;
int __pyx_1;
- static char *__pyx_argnames[] = {0};
- if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0;
Py_INCREF(__pyx_v_self);
/* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":133
@@ -1203,13 +1206,11 @@
return __pyx_r;
}
-static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
-static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *unused); /*proto*/
+static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *unused) {
int __pyx_v_i;
PyObject *__pyx_r;
int __pyx_1;
- static char *__pyx_argnames[] = {0};
- if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0;
Py_INCREF(__pyx_v_self);
/* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":142
@@ -1609,7 +1610,7 @@
* if not radians and p2.is_latlong():
* for i from 0 <= i < npts:
*/
- __pyx_7 = PyInt_AsLong(__pyx_v_ierr); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;}
+ __pyx_7 = PyInt_AsLong(__pyx_v_ierr); if (unlikely((__pyx_7 == -1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;}
__pyx_4 = PyString_FromString(pj_strerrno(__pyx_7)); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;}
__pyx_3 = PyTuple_New(1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;}
PyTuple_SET_ITEM(__pyx_3, 0, __pyx_4);
@@ -1791,11 +1792,11 @@
}
static struct PyMethodDef __pyx_methods_5_proj_Proj[] = {
- {"__reduce__", (PyCFunction)__pyx_f_5_proj_4Proj___reduce__, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_proj_4Proj___reduce__},
+ {"__reduce__", (PyCFunction)__pyx_f_5_proj_4Proj___reduce__, METH_NOARGS, __pyx_doc_5_proj_4Proj___reduce__},
{"_fwd", (PyCFunction)__pyx_f_5_proj_4Proj__fwd, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_proj_4Proj__fwd},
{"_inv", (PyCFunction)__pyx_f_5_proj_4Proj__inv, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_proj_4Proj__inv},
- {"is_latlong", (PyCFunction)__pyx_f_5_proj_4Proj_is_latlong, METH_VARARGS|METH_KEYWORDS, 0},
- {"is_geocent", (PyCFunction)__pyx_f_5_proj_4Proj_is_geocent, METH_VARARGS|METH_KEYWORDS, 0},
+ {"is_latlong", (PyCFunction)__pyx_f_5_proj_4Proj_is_latlong, METH_NOARGS, 0},
+ {"is_geocent", (PyCFunction)__pyx_f_5_proj_4Proj_is_geocent, METH_NOARGS, 0},
{0, 0, 0, 0}
};
@@ -1924,7 +1925,7 @@
};
static struct PyMethodDef __pyx_methods[] = {
- {"set_datapath", (PyCFunction)__pyx_f_5_proj_set_datapath, METH_VARARGS|METH_KEYWORDS, 0},
+ {"set_datapath", (PyCFunction)__pyx_f_5_proj_set_datapath, METH_O, 0},
{"_transform", (PyCFunction)__pyx_f_5_proj__transform, METH_VARARGS|METH_KEYWORDS, 0},
{0, 0, 0, 0}
};
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <js...@us...> - 2007-09-12 18:14:42
|
Revision: 3840
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3840&view=rev
Author: jswhit
Date: 2007-09-12 11:14:38 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
fix isnan check
Modified Paths:
--------------
trunk/toolkits/basemap/src/_geod.c
trunk/toolkits/basemap/src/_geod.pyx
Modified: trunk/toolkits/basemap/src/_geod.c
===================================================================
--- trunk/toolkits/basemap/src/_geod.c 2007-09-12 17:25:19 UTC (rev 3839)
+++ trunk/toolkits/basemap/src/_geod.c 2007-09-12 18:14:38 UTC (rev 3840)
@@ -1,4 +1,4 @@
-/* Generated by Pyrex 0.9.6.3 on Fri Aug 31 08:42:50 2007 */
+/* Generated by Pyrex 0.9.6.5 on Wed Sep 12 12:12:33 2007 */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
@@ -12,6 +12,8 @@
#define PY_SSIZE_T_MIN INT_MIN
#define PyInt_FromSsize_t(z) PyInt_FromLong(z)
#define PyInt_AsSsize_t(o) PyInt_AsLong(o)
+ #define PyNumber_Index(o) PyNumber_Int(o)
+ #define PyIndex_Check(o) PyNumber_Check(o)
#endif
#ifdef __cplusplus
#define __PYX_EXTERN_C extern "C"
@@ -37,6 +39,8 @@
typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/
typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/
+#define __pyx_PyIndex_AsSsize_t(b) PyInt_AsSsize_t(PyNumber_Index(b))
+
#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False))
static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
if (x == Py_True) return 1;
@@ -337,15 +341,13 @@
static PyObject *__pyx_n___class__;
-static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *unused); /*proto*/
static char __pyx_doc_5_geod_4Geod___reduce__[] = "special method that allows pyproj.Geod instance to be pickled";
-static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *unused) {
PyObject *__pyx_r;
PyObject *__pyx_1 = 0;
PyObject *__pyx_2 = 0;
PyObject *__pyx_3 = 0;
- static char *__pyx_argnames[] = {0};
- if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0;
Py_INCREF(__pyx_v_self);
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":27
@@ -421,6 +423,7 @@
PyObject *__pyx_5 = 0;
Py_ssize_t __pyx_6;
double __pyx_7;
+ int __pyx_8;
static char *__pyx_argnames[] = {"lons","lats","az","dist","radians",0};
__pyx_v_radians = __pyx_k3;
if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOOO|O", __pyx_argnames, &__pyx_v_lons, &__pyx_v_lats, &__pyx_v_az, &__pyx_v_dist, &__pyx_v_radians))) return 0;
@@ -571,7 +574,7 @@
__pyx_5 = PyNumber_Divide(__pyx_3, __pyx_4); if (unlikely(!__pyx_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; goto __pyx_L1;}
Py_DECREF(__pyx_3); __pyx_3 = 0;
Py_DECREF(__pyx_4); __pyx_4 = 0;
- __pyx_6 = PyInt_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; goto __pyx_L1;}
+ __pyx_6 = __pyx_PyIndex_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; goto __pyx_L1;}
Py_DECREF(__pyx_5); __pyx_5 = 0;
__pyx_v_ndim = __pyx_6;
@@ -781,7 +784,7 @@
* geod_for(&self.geodesic_t)
* if pj_errno != 0: # <<<<<<<<<<<<<<
* raise RuntimeError(pj_strerrno(pj_errno))
- * if isnan(self.geodesic_t.ALPHA21) == FP_NAN:
+ * if isnan(self.geodesic_t.ALPHA21):
*/
__pyx_1 = (pj_errno != 0);
if (__pyx_1) {
@@ -790,7 +793,7 @@
* geod_for(&self.geodesic_t)
* if pj_errno != 0:
* raise RuntimeError(pj_strerrno(pj_errno)) # <<<<<<<<<<<<<<
- * if isnan(self.geodesic_t.ALPHA21) == FP_NAN:
+ * if isnan(self.geodesic_t.ALPHA21):
* raise ValueError('undefined forward geodesic (may be an equatorial arc)')
*/
__pyx_3 = PyString_FromString(pj_strerrno(pj_errno)); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 72; goto __pyx_L1;}
@@ -809,16 +812,16 @@
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":73
* if pj_errno != 0:
* raise RuntimeError(pj_strerrno(pj_errno))
- * if isnan(self.geodesic_t.ALPHA21) == FP_NAN: # <<<<<<<<<<<<<<
+ * if isnan(self.geodesic_t.ALPHA21): # <<<<<<<<<<<<<<
* raise ValueError('undefined forward geodesic (may be an equatorial arc)')
* if radians:
*/
- __pyx_2 = (isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.ALPHA21) == FP_NAN);
- if (__pyx_2) {
+ __pyx_8 = isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.ALPHA21);
+ if (__pyx_8) {
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":74
* raise RuntimeError(pj_strerrno(pj_errno))
- * if isnan(self.geodesic_t.ALPHA21) == FP_NAN:
+ * if isnan(self.geodesic_t.ALPHA21):
* raise ValueError('undefined forward geodesic (may be an equatorial arc)') # <<<<<<<<<<<<<<
* if radians:
* lonsdata[i] = self.geodesic_t.p2.v
@@ -836,14 +839,14 @@
__pyx_L12:;
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":75
- * if isnan(self.geodesic_t.ALPHA21) == FP_NAN:
+ * if isnan(self.geodesic_t.ALPHA21):
* raise ValueError('undefined forward geodesic (may be an equatorial arc)')
* if radians: # <<<<<<<<<<<<<<
* lonsdata[i] = self.geodesic_t.p2.v
* latsdata[i] = self.geodesic_t.p2.u
*/
- __pyx_1 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 75; goto __pyx_L1;}
- if (__pyx_1) {
+ __pyx_2 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_2 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 75; goto __pyx_L1;}
+ if (__pyx_2) {
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":76
* raise ValueError('undefined forward geodesic (may be an equatorial arc)')
@@ -980,6 +983,7 @@
PyObject *__pyx_5 = 0;
Py_ssize_t __pyx_6;
double __pyx_7;
+ int __pyx_8;
static char *__pyx_argnames[] = {"lons1","lats1","lons2","lats2","radians",0};
__pyx_v_radians = __pyx_k4;
if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOOO|O", __pyx_argnames, &__pyx_v_lons1, &__pyx_v_lats1, &__pyx_v_lons2, &__pyx_v_lats2, &__pyx_v_radians))) return 0;
@@ -1130,7 +1134,7 @@
__pyx_5 = PyNumber_Divide(__pyx_3, __pyx_4); if (unlikely(!__pyx_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; goto __pyx_L1;}
Py_DECREF(__pyx_3); __pyx_3 = 0;
Py_DECREF(__pyx_4); __pyx_4 = 0;
- __pyx_6 = PyInt_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; goto __pyx_L1;}
+ __pyx_6 = __pyx_PyIndex_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; goto __pyx_L1;}
Py_DECREF(__pyx_5); __pyx_5 = 0;
__pyx_v_ndim = __pyx_6;
@@ -1281,7 +1285,7 @@
* self.geodesic_t.p2.v = _dg2rad*azdata[i]
* self.geodesic_t.p2.u = _dg2rad*distdata[i] # <<<<<<<<<<<<<<
* geod_inv(&self.geodesic_t)
- * if isnan(self.geodesic_t.DIST) == FP_NAN:
+ * if isnan(self.geodesic_t.DIST):
*/
__pyx_3 = __Pyx_GetName(__pyx_m, __pyx_n__dg2rad); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; goto __pyx_L1;}
__pyx_4 = PyFloat_FromDouble((__pyx_v_distdata[__pyx_v_i])); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; goto __pyx_L1;}
@@ -1298,7 +1302,7 @@
* self.geodesic_t.p2.v = _dg2rad*azdata[i]
* self.geodesic_t.p2.u = _dg2rad*distdata[i]
* geod_inv(&self.geodesic_t) # <<<<<<<<<<<<<<
- * if isnan(self.geodesic_t.DIST) == FP_NAN:
+ * if isnan(self.geodesic_t.DIST):
* raise ValueError('undefined inverse geodesic (may be an antipodal point)')
*/
geod_inv((&((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t));
@@ -1306,16 +1310,16 @@
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":122
* self.geodesic_t.p2.u = _dg2rad*distdata[i]
* geod_inv(&self.geodesic_t)
- * if isnan(self.geodesic_t.DIST) == FP_NAN: # <<<<<<<<<<<<<<
+ * if isnan(self.geodesic_t.DIST): # <<<<<<<<<<<<<<
* raise ValueError('undefined inverse geodesic (may be an antipodal point)')
* if pj_errno != 0:
*/
- __pyx_2 = (isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.DIST) == FP_NAN);
- if (__pyx_2) {
+ __pyx_8 = isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.DIST);
+ if (__pyx_8) {
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":123
* geod_inv(&self.geodesic_t)
- * if isnan(self.geodesic_t.DIST) == FP_NAN:
+ * if isnan(self.geodesic_t.DIST):
* raise ValueError('undefined inverse geodesic (may be an antipodal point)') # <<<<<<<<<<<<<<
* if pj_errno != 0:
* raise RuntimeError(pj_strerrno(pj_errno))
@@ -1333,14 +1337,14 @@
__pyx_L10:;
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":124
- * if isnan(self.geodesic_t.DIST) == FP_NAN:
+ * if isnan(self.geodesic_t.DIST):
* raise ValueError('undefined inverse geodesic (may be an antipodal point)')
* if pj_errno != 0: # <<<<<<<<<<<<<<
* raise RuntimeError(pj_strerrno(pj_errno))
* if radians:
*/
- __pyx_1 = (pj_errno != 0);
- if (__pyx_1) {
+ __pyx_2 = (pj_errno != 0);
+ if (__pyx_2) {
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":125
* raise ValueError('undefined inverse geodesic (may be an antipodal point)')
@@ -1369,8 +1373,8 @@
* lonsdata[i] = self.geodesic_t.ALPHA12
* latsdata[i] = self.geodesic_t.ALPHA21
*/
- __pyx_2 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_2 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 126; goto __pyx_L1;}
- if (__pyx_2) {
+ __pyx_1 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 126; goto __pyx_L1;}
+ if (__pyx_1) {
/* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":127
* raise RuntimeError(pj_strerrno(pj_errno))
@@ -1868,7 +1872,7 @@
}
static struct PyMethodDef __pyx_methods_5_geod_Geod[] = {
- {"__reduce__", (PyCFunction)__pyx_f_5_geod_4Geod___reduce__, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod___reduce__},
+ {"__reduce__", (PyCFunction)__pyx_f_5_geod_4Geod___reduce__, METH_NOARGS, __pyx_doc_5_geod_4Geod___reduce__},
{"_fwd", (PyCFunction)__pyx_f_5_geod_4Geod__fwd, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod__fwd},
{"_inv", (PyCFunction)__pyx_f_5_geod_4Geod__inv, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod__inv},
{"_npts", (PyCFunction)__pyx_f_5_geod_4Geod__npts, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod__npts},
Modified: trunk/toolkits/basemap/src/_geod.pyx
===================================================================
--- trunk/toolkits/basemap/src/_geod.pyx 2007-09-12 17:25:19 UTC (rev 3839)
+++ trunk/toolkits/basemap/src/_geod.pyx 2007-09-12 18:14:38 UTC (rev 3840)
@@ -70,7 +70,7 @@
geod_for(&self.geodesic_t)
if pj_errno != 0:
raise RuntimeError(pj_strerrno(pj_errno))
- if isnan(self.geodesic_t.ALPHA21) == FP_NAN:
+ if isnan(self.geodesic_t.ALPHA21):
raise ValueError('undefined forward geodesic (may be an equatorial arc)')
if radians:
lonsdata[i] = self.geodesic_t.p2.v
@@ -119,7 +119,7 @@
self.geodesic_t.p2.v = _dg2rad*azdata[i]
self.geodesic_t.p2.u = _dg2rad*distdata[i]
geod_inv(&self.geodesic_t)
- if isnan(self.geodesic_t.DIST) == FP_NAN:
+ if isnan(self.geodesic_t.DIST):
raise ValueError('undefined inverse geodesic (may be an antipodal point)')
if pj_errno != 0:
raise RuntimeError(pj_strerrno(pj_errno))
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-12 17:25:26
|
Revision: 3839
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3839&view=rev
Author: mdboom
Date: 2007-09-12 10:25:19 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
Milestone -- simple_plot.py working with new affine framework (with
the exception of dpi propagation)
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/figure.py
branches/transforms/lib/matplotlib/lines.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-12 15:41:22 UTC (rev 3838)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-12 17:25:19 UTC (rev 3839)
@@ -33,29 +33,29 @@
self._points = N.asarray(points, N.float_)
self.track = False
- # JDH: if you define a del method, the garbage collector won't
- # destory cyclic references, so make sure you either manage these
- # yourself or remove the __del__ after testing
- def __del__(self):
- if self.track:
- print "Bbox::__del__"
-
#@staticmethod
def unit():
- return Bbox([[0,0], [1,1]])
+ return Bbox.from_lbrt(0., 0., 1., 1.)
unit = staticmethod(unit)
#@staticmethod
def from_lbwh(left, bottom, width, height):
- return Bbox([[left, bottom], [left + width, bottom + height]])
+ return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
from_lbwh = staticmethod(from_lbwh)
#@staticmethod
- def from_lbrt(left, bottom, right, top):
- return Bbox([[left, bottom], [right, top]])
+ def from_lbrt(*args):
+ points = N.array(args, dtype=N.float_).reshape(2, 2)
+ return Bbox(points)
from_lbrt = staticmethod(from_lbrt)
-
+ def __cmp__(self, other):
+ # MGDTODO: Totally suboptimal
+ if isinstance(other, Bbox):
+ if (self._points == other._points).all():
+ return 0
+ return -1
+
# JDH: the update method will update the box limits from the
# existing limits and the new data; it appears here you are just
# using the new data. We use an "ignore" flag to specify whether
@@ -63,31 +63,18 @@
def update_from_data(self, x, y):
self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_)
self.invalidate()
- if self.track:
- print "Bbox::update_from_data", self._points
def copy(self):
- if self.track:
- print "Bbox::copy"
return Bbox(self._points.copy())
def __repr__(self):
return 'Bbox(%s)' % repr(self._points)
__str__ = __repr__
- def __cmp__(self, other):
- # MGDTODO: Totally suboptimal
- if isinstance(other, Bbox):
- return (self._points == other._points).all()
- return -1
-
# MGDTODO: Probably a more efficient ways to do this...
def _get_xmin(self):
- if self.track:
- print "Bbox::_get_xmin"
return self._points[0, 0]
def _set_xmin(self, val):
- print "Bbox::_set_xmin"
self._points[0, 0] = val
self.invalidate()
xmin = property(_get_xmin, _set_xmin)
@@ -150,10 +137,10 @@
height = property(_get_height)
def transformed(self, transform):
- return Bbox(self.transform(self._points))
+ return Bbox(transform(self._points))
def inverse_transformed(self, transform):
- return Bbox(self.transform.inverted()(self._points))
+ return Bbox(transform.inverted()(self._points))
def get_bounds(self):
return (self.xmin, self.ymin,
@@ -249,6 +236,14 @@
return "Affine2D(%s)" % repr(self._mtx)
__str__ = __repr__
+ def __cmp__(self, other):
+ # MGDTODO: We need to decide if we want deferred transforms
+ # to be equal to this one
+ if isinstance(other, Affine2D):
+ if (self.get_matrix() == other.get_matrix()).all():
+ return 0
+ return -1
+
def _do_invalidation(self):
result = self._inverted is None
self._inverted = None
@@ -380,10 +375,9 @@
if self._mtx is None:
x_mtx = self._x.get_matrix()
y_mtx = self._y.get_matrix()
+ # This works because we already know the transforms are
+ # separable
self._mtx = N.vstack([x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]])
-# self._mtx = self.matrix_from_values(
-# x_mtx[0,0], 0.0, 0.0, y_mtx[1,1], x_mtx[0,2], y_mtx[1,2])
- print "Blended", x_mtx, y_mtx, self._mtx
def is_separable(self):
return True
@@ -429,8 +423,8 @@
def _make__mtx(self):
if self._mtx is None:
self._mtx = self._concat(
- self._b.get_matrix(),
- self._a.get_matrix())
+ self._a.get_matrix(),
+ self._b.get_matrix())
def get_matrix(self):
self._make__mtx()
@@ -547,12 +541,70 @@
return interval[0] < val and interval[1] > val
if __name__ == '__main__':
+ bbox = Bbox.from_lbrt(10., 15., 20., 25.)
+ assert bbox.xmin == 10
+ assert bbox.ymin == 15
+ assert bbox.xmax == 20
+ assert bbox.ymax == 25
+
+ assert N.all(bbox.min == [10, 15])
+ assert N.all(bbox.max == [20, 25])
+ assert N.all(bbox.intervalx == (10, 20))
+ assert N.all(bbox.intervaly == (15, 25))
+
+ assert bbox.width == 10
+ assert bbox.height == 10
+
+ assert bbox.get_bounds() == (10, 15, 10, 10)
+
+ bbox.intervalx = (11, 21)
+ bbox.intervaly = (16, 26)
+
+ assert bbox.get_bounds() == (11, 16, 10, 10)
+
+ bbox.xmin = 12
+ bbox.ymin = 17
+ bbox.xmax = 22
+ bbox.ymax = 27
+
+ assert bbox.get_bounds() == (12, 17, 10, 10)
+
+ bbox = Bbox.from_lbwh(10, 11, 12, 13)
+ assert bbox.get_bounds() == (10, 11, 12, 13)
+
+ bbox_copy = bbox.copy()
+ assert bbox == bbox_copy
+ bbox_copy.max = (14, 15)
+ assert bbox.get_bounds() == (10, 11, 12, 13)
+ assert bbox_copy.get_bounds() == (10, 11, 4, 4)
+
bbox1 = Bbox([[10., 15.], [20., 25.]])
bbox2 = Bbox([[30., 35.], [40., 45.]])
trans = BboxTransform(bbox1, bbox2)
- print trans(bbox1._points)
+ bbox3 = bbox1.transformed(trans)
+ assert bbox3 == bbox2
- bbox2.intervalx = 50, 55
- print trans(bbox1._points)
+ translation = Affine2D().translate(10, 20)
+ assert translation.to_values() == (1, 0, 0, 1, 10, 20)
+ scale = Affine2D().scale(10, 20)
+ assert scale.to_values() == (10, 0, 0, 20, 0, 0)
+ rotation = Affine2D().rotate_deg(30)
+ print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, -0.49999999999999994, 0.86602540378443871, 0.0, 0.0)
+ points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_)
+ translated_points = translation(points)
+ assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all()
+ scaled_points = scale(points)
+ print scaled_points
+ rotated_points = rotation(points)
+ print rotated_points
+
+ tpoints1 = rotation(translation(scale(points)))
+ trans_sum = rotation + translation + scale
+ tpoints2 = trans_sum(points)
+ print tpoints1, tpoints2
+ print tpoints1 == tpoints2
+ # Need to do some sort of fuzzy comparison here?
+ # assert (tpoints1 == tpoints2).all()
+
__all__ = ['Transform', 'Affine2D']
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-12 15:41:22 UTC (rev 3838)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-12 17:25:19 UTC (rev 3839)
@@ -653,15 +653,12 @@
self.viewLim = Bbox.from_lbrt(left, bottom, right, top)
self.dataLim = Bbox.unit()
- self.dataLim.track = True
self.transData = maffine.BboxTransform(
self.viewLim, self.bbox)
self.transAxes = maffine.BboxTransform(
Bbox.unit(), self.bbox)
- print "_set_lim_and_transforms", self.viewLim, self.transData, self.dataLim, self.transAxes, self.bbox
-
# MGDTODO
# if self._sharex:
# self.transData.set_funcx(self._sharex.transData.get_funcx())
@@ -697,7 +694,6 @@
# # Change values within self._position--don't replace it.
# for num,val in zip(pos, self._position):
# val.set(num)
- print "set_position", self._position, pos
self._position = pos
# MGDTODO: side-effects
if which in ('both', 'original'):
@@ -1182,9 +1178,7 @@
#print type(x), type(y)
# MGDTODO
## self.dataLim.update_numerix(x, y, -1)
- print "update_datalim_numerix", self.dataLim,
self.dataLim.update_from_data(x, y)
- print self.dataLim
def _get_verts_in_data_coords(self, trans, xys):
if trans == self.transData:
@@ -1245,8 +1239,6 @@
axis direction reversal that has already been done.
"""
# if image data only just use the datalim
- print "autoscale_view", self._autoscaleon, scalex, scaley
-
if not self._autoscaleon: return
if (tight or (len(self.images)>0 and
len(self.lines)==0 and
@@ -1274,7 +1266,7 @@
def draw(self, renderer=None, inframe=False):
"Draw everything (plot lines, axes, labels)"
- if renderer is None:
+ if renderer is None:
renderer = self._cachedRenderer
if renderer is None:
@@ -1550,7 +1542,6 @@
xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False)
self.viewLim.intervalx = (xmin, xmax)
- print 'set_xlim', self.viewLim, xmin, xmax
return xmin, xmax
@@ -1654,7 +1645,6 @@
ACCEPTS: len(2) sequence of floats
"""
- print "set_ylim", ymin, ymax, emit
if ymax is None and iterable(ymin):
ymin,ymax = ymin
@@ -1676,7 +1666,6 @@
ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False)
self.viewLim.intervaly = (ymin, ymax)
if emit: self.callbacks.process('ylim_changed', self)
- print "set_ylim", self.viewLim
return ymin, ymax
Modified: branches/transforms/lib/matplotlib/figure.py
===================================================================
--- branches/transforms/lib/matplotlib/figure.py 2007-09-12 15:41:22 UTC (rev 3838)
+++ branches/transforms/lib/matplotlib/figure.py 2007-09-12 17:25:19 UTC (rev 3839)
@@ -326,7 +326,6 @@
dpival = self.dpi
self.bbox.max = w * dpival, h * dpival
- print self.bbox
# self.figwidth.set(w) MGDTODO
# self.figheight.set(h)
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-12 15:41:22 UTC (rev 3838)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-12 17:25:19 UTC (rev 3839)
@@ -389,7 +389,6 @@
def set_axes(self, ax):
- print "set_axes"
Artist.set_axes(self, ax)
if ax.xaxis is not None:
self._xcid = ax.xaxis.callbacks.connect('units', self.recache)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <jd...@us...> - 2007-09-12 15:41:28
|
Revision: 3838
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3838&view=rev
Author: jdh2358
Date: 2007-09-12 08:41:22 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
removed Interval from canonical tickers and formatters
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/ticker.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-12 14:46:03 UTC (rev 3837)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-12 15:41:22 UTC (rev 3838)
@@ -33,6 +33,9 @@
self._points = N.asarray(points, N.float_)
self.track = False
+ # JDH: if you define a del method, the garbage collector won't
+ # destory cyclic references, so make sure you either manage these
+ # yourself or remove the __del__ after testing
def __del__(self):
if self.track:
print "Bbox::__del__"
@@ -52,6 +55,11 @@
return Bbox([[left, bottom], [right, top]])
from_lbrt = staticmethod(from_lbrt)
+
+ # JDH: the update method will update the box limits from the
+ # existing limits and the new data; it appears here you are just
+ # using the new data. We use an "ignore" flag to specify whether
+ # you want to include the existing data or not in the update
def update_from_data(self, x, y):
self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_)
self.invalidate()
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-12 14:46:03 UTC (rev 3837)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-12 15:41:22 UTC (rev 3838)
@@ -855,9 +855,9 @@
ACCEPTS: A Formatter instance
"""
self.major.formatter = formatter
- self.major.formatter.set_view_interval( self.get_view_interval() )
- self.major.formatter.set_data_interval( self.get_data_interval() )
+ self.major.formatter.set_axis(self)
+
def set_minor_formatter(self, formatter):
"""
Set the formatter of the minor ticker
@@ -865,8 +865,7 @@
ACCEPTS: A Formatter instance
"""
self.minor.formatter = formatter
- self.minor.formatter.set_view_interval( self.get_view_interval() )
- self.minor.formatter.set_data_interval( self.get_data_interval() )
+ self.minor.formatter.set_axis(self)
def set_major_locator(self, locator):
@@ -876,8 +875,7 @@
ACCEPTS: a Locator instance
"""
self.major.locator = locator
- self.major.locator.set_view_interval( self.get_view_interval() )
- self.major.locator.set_data_interval( self.get_data_interval() )
+ self.major.locator.set_axis(self)
def set_minor_locator(self, locator):
@@ -887,8 +885,7 @@
ACCEPTS: a Locator instance
"""
self.minor.locator = locator
- self.minor.locator.set_view_interval( self.get_view_interval() )
- self.minor.locator.set_data_interval( self.get_data_interval() )
+ self.minor.locator.set_axis(self)
def set_pickradius(self, pickradius):
"""
Modified: branches/transforms/lib/matplotlib/ticker.py
===================================================================
--- branches/transforms/lib/matplotlib/ticker.py 2007-09-12 14:46:03 UTC (rev 3837)
+++ branches/transforms/lib/matplotlib/ticker.py 2007-09-12 15:41:22 UTC (rev 3838)
@@ -99,13 +99,7 @@
minor ticks. See the matplotlib.dates module for more information and
examples of using date locators and formatters.
-DEVELOPERS NOTE
-If you are implementing your own class or modifying one of these, it
-is critical that you use viewlim and dataInterval READ ONLY MODE so
-multiple axes can share the same locator w/o side effects!
-
-
"""
@@ -121,37 +115,11 @@
class TickHelper:
+ axis = None
+ def set_axis(self, axis):
+ self.axis = axis
- viewInterval = None
- dataInterval = None
- def verify_intervals(self):
- if self.dataInterval is None:
- raise RuntimeError("You must set the data interval to use this function")
-
- if self.viewInterval is None:
- raise RuntimeError("You must set the view interval to use this function")
-
-
- def set_view_interval(self, interval):
- self.viewInterval = interval
-
- def set_data_interval(self, interval):
- self.dataInterval = interval
-
- def set_bounds(self, vmin, vmax):
- '''
- Set dataInterval and viewInterval from numeric vmin, vmax.
-
- This is for stand-alone use of Formatters and/or
- Locators that require these intervals; that is, for
- cases where the Intervals do not need to be updated
- automatically.
- '''
- # MGDTODO: Interval no longer exists
- self.dataInterval = maffine.Interval([vmin, vmax])
- self.viewInterval = maffine.Interval([vmin, vmax])
-
class Formatter(TickHelper):
"""
Convert the tick location to a string
@@ -341,9 +309,8 @@
'set the locations of the ticks'
self.locs = locs
if len(self.locs) > 0:
- self.verify_intervals()
- vmin, vmax = self.viewInterval
- d = abs(vmax - vmin)
+ vmin, vmax = self.axis.get_view_interval()
+ d = abs(vmax-vmin)
if self._useOffset: self._set_offset(d)
self._set_orderOfMagnitude(d)
self._set_format()
@@ -865,14 +832,12 @@
def __call__(self):
- self.verify_intervals()
- vmin, vmax = self.viewInterval
+ vmin, vmax = self.axis.get_view_interval()
vmin, vmax = maffine.nonsingular(vmin, vmax, expander = 0.05)
return self.bin_boundaries(vmin, vmax)
def autoscale(self):
- self.verify_intervals()
- dmin, dmax = self.dataInterval
+ dmin, dmax = self.axis.get_data_interval()
dmin, dmax = maffine.nonsingular(dmin, dmax, expander = 0.05)
return npy.take(self.bin_boundaries(dmin, dmax), [0,-1])
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <md...@us...> - 2007-09-12 14:46:06
|
Revision: 3837
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3837&view=rev
Author: mdboom
Date: 2007-09-12 07:46:03 -0700 (Wed, 12 Sep 2007)
Log Message:
-----------
Adding pbox.py
Added Paths:
-----------
branches/transforms/lib/matplotlib/pbox.py
Added: branches/transforms/lib/matplotlib/pbox.py
===================================================================
--- branches/transforms/lib/matplotlib/pbox.py (rev 0)
+++ branches/transforms/lib/matplotlib/pbox.py 2007-09-12 14:46:03 UTC (rev 3837)
@@ -0,0 +1,126 @@
+# MGDTODO: Just included verbatim for now
+
+class PBox(list):
+ '''
+ A left-bottom-width-height (lbwh) specification of a bounding box,
+ such as is used to specify the position of an Axes object within
+ a Figure.
+ It is a 4-element list with methods for changing the size, shape,
+ and position relative to its container.
+ '''
+ coefs = {'C': (0.5, 0.5),
+ 'SW': (0,0),
+ 'S': (0.5, 0),
+ 'SE': (1.0, 0),
+ 'E': (1.0, 0.5),
+ 'NE': (1.0, 1.0),
+ 'N': (0.5, 1.0),
+ 'NW': (0, 1.0),
+ 'W': (0, 0.5)}
+ def __init__(self, box, container=None, llur=False):
+ if len(box) != 4:
+ raise ValueError("Argument must be iterable of length 4")
+ if llur:
+ box = [box[0], box[1], box[2]-box[0], box[3]-box[1]]
+ list.__init__(self, box)
+ self.set_container(container)
+
+ def as_llur(self):
+ return [self[0], self[1], self[0]+self[2], self[1]+self[3]]
+
+ def set_container(self, box=None):
+ if box is None:
+ box = self
+ if len(box) != 4:
+ raise ValueError("Argument must be iterable of length 4")
+ self._container = list(box)
+
+ def get_container(self, box):
+ return self._container
+
+ def anchor(self, c, container=None):
+ '''
+ Shift to position c within its container.
+
+ c can be a sequence (cx, cy) where cx, cy range from 0 to 1,
+ where 0 is left or bottom and 1 is right or top.
+
+ Alternatively, c can be a string: C for centered,
+ S for bottom-center, SE for bottom-left, E for left, etc.
+
+ Optional arg container is the lbwh box within which the
+ PBox is positioned; it defaults to the initial
+ PBox.
+ '''
+ if container is None:
+ container = self._container
+ l,b,w,h = container
+ if isinstance(c, str):
+ cx, cy = self.coefs[c]
+ else:
+ cx, cy = c
+ W,H = self[2:]
+ self[:2] = l + cx * (w-W), b + cy * (h-H)
+ return self
+
+ def shrink(self, mx, my):
+ '''
+ Shrink the box by mx in the x direction and my in the y direction.
+ The lower left corner of the box remains unchanged.
+ Normally mx and my will be <= 1, but this is not enforced.
+ '''
+ assert mx >= 0 and my >= 0
+ self[2:] = mx * self[2], my * self[3]
+ return self
+
+ def shrink_to_aspect(self, box_aspect, fig_aspect = 1):
+ '''
+ Shrink the box so that it is as large as it can be while
+ having the desired aspect ratio, box_aspect.
+ If the box coordinates are relative--that is, fractions of
+ a larger box such as a figure--then the physical aspect
+ ratio of that figure is specified with fig_aspect, so
+ that box_aspect can also be given as a ratio of the
+ absolute dimensions, not the relative dimensions.
+ '''
+ assert box_aspect > 0 and fig_aspect > 0
+ l,b,w,h = self._container
+ H = w * box_aspect/fig_aspect
+ if H <= h:
+ W = w
+ else:
+ W = h * fig_aspect/box_aspect
+ H = h
+ self[2:] = W,H
+ return self
+
+ def splitx(self, *args):
+ '''
+ e.g., PB.splitx(f1, f2, ...)
+
+ Returns a list of new PBoxes formed by
+ splitting the original one (PB) with vertical lines
+ at fractional positions f1, f2, ...
+ '''
+ boxes = []
+ xf = [0] + list(args) + [1]
+ l,b,w,h = self[:]
+ for xf0, xf1 in zip(xf[:-1], xf[1:]):
+ boxes.append(PBox([l+xf0*w, b, (xf1-xf0)*w, h]))
+ return boxes
+
+ def splity(self, *args):
+ '''
+ e.g., PB.splity(f1, f2, ...)
+
+ Returns a list of new PBoxes formed by
+ splitting the original one (PB) with horizontal lines
+ at fractional positions f1, f2, ..., with y measured
+ positive up.
+ '''
+ boxes = []
+ yf = [0] + list(args) + [1]
+ l,b,w,h = self[:]
+ for yf0, yf1 in zip(yf[:-1], yf[1:]):
+ boxes.append(PBox([l, b+yf0*h, w, (yf1-yf0)*h]))
+ return boxes
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|