From: <md...@us...> - 2007-11-30 15:07:00
|
Revision: 4521 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4521&view=rev Author: mdboom Date: 2007-11-30 07:06:56 -0800 (Fri, 30 Nov 2007) Log Message: ----------- Get Gtk backend working. Modified Paths: -------------- branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backends/backend_gdk.py branches/transforms/lib/matplotlib/path.py branches/transforms/src/_path.cpp Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-11-30 14:43:11 UTC (rev 4520) +++ branches/transforms/lib/matplotlib/axis.py 2007-11-30 15:06:56 UTC (rev 4521) @@ -87,7 +87,7 @@ self._size = size self._padPixels = self.figure.dpi * self._pad * (1/72.0) - + self.tick1line = self._get_tick1line() self.tick2line = self._get_tick2line() self.gridline = self._get_gridline() @@ -97,7 +97,7 @@ self.label2 = self._get_text2() self.update_position(loc) - + self.gridOn = gridOn self.tick1On = tick1On self.tick2On = tick2On @@ -114,7 +114,7 @@ #self.tick2line.set_clip_path(clippath, transform) self.gridline.set_clip_path(clippath, transform) set_clip_path.__doc__ = Artist.set_clip_path.__doc__ - + def contains(self, mouseevent): """Test whether the mouse event occured in the Tick marks. @@ -189,7 +189,7 @@ """ self.label1.set_text(s) set_label = set_label1 - + def set_label2(self, s): """ Set the text of ticklabel2 @@ -209,7 +209,7 @@ def set_view_interval(self, vmin, vmax, ignore=False): raise NotImplementedError('Derived must override') - + class XTick(Tick): """ Contains all the Artists needed to make an x tick - the tick line, @@ -223,7 +223,7 @@ # x in data coords, y in axes coords #t = Text( trans, vert, horiz = self.axes.get_xaxis_text1_transform(self._padPixels) - + t = TextWithDash( x=0, y=0, fontproperties=FontProperties(size=rcParams['xtick.labelsize']), @@ -320,10 +320,10 @@ else: Vmin, Vmax = self.get_view_interval() self.axes.viewLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) - + def get_minpos(self): return self.axes.dataLim.minposx - + def get_data_interval(self): 'return the Interval instance for this axis data limits' return self.axes.dataLim.intervalx @@ -428,7 +428,7 @@ self._loc = loc - + def get_view_interval(self): 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervaly @@ -439,10 +439,10 @@ else: Vmin, Vmax = self.get_view_interval() self.axes.viewLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) - + def get_minpos(self): return self.axes.dataLim.minposy - + def get_data_interval(self): 'return the Interval instance for this axis data limits' return self.axes.dataLim.intervaly @@ -465,7 +465,7 @@ """ LABELPAD = 5 OFFSETTEXTPAD = 3 - + def __str__(self): return str(self.__class__).split('.')[-1] \ + "(%d,%d)"%self.axes.transAxes.xy_tup((0,0)) @@ -493,23 +493,23 @@ self.majorTicks = [] self.minorTicks = [] self.pickradius = pickradius - + self.cla() self.set_scale('linear') - + def get_transform(self): return self._scale.get_transform() def get_scale(self): return self._scale.name - + def set_scale(self, value, **kwargs): self._scale = scale_factory(value, self, **kwargs) self._scale.set_default_locators_and_formatters(self) def limit_range_for_scale(self, vmin, vmax): return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos()) - + def get_children(self): children = [self.label] majorticks = self.get_major_ticks() @@ -547,7 +547,7 @@ self.minorTicks.extend([self._get_tick(major=False)]) self._lastNumMajorTicks = 1 self._lastNumMinorTicks = 1 - + self.converter = None self.units = None self.set_units(None) @@ -555,17 +555,17 @@ def set_clip_path(self, clippath, transform=None): Artist.set_clip_path(self, clippath, transform) majorticks = self.get_major_ticks() - minorticks = self.get_minor_ticks() + minorticks = self.get_minor_ticks() for child in self.majorTicks + self.minorTicks: child.set_clip_path(clippath, transform) - + def get_view_interval(self): 'return the Interval instance for this axis view limits' raise NotImplementedError('Derived must override') def set_view_interval(self, vmin, vmax, ignore=False): raise NotImplementedError('Derived must override') - + def get_data_interval(self): 'return the Interval instance for this axis data limits' raise NotImplementedError('Derived must override') @@ -573,7 +573,7 @@ def set_data_interval(self): 'Set the axis data limits' raise NotImplementedError('Derived must override') - + def _set_artist_props(self, a): if a is None: return a.set_figure(self.figure) @@ -774,7 +774,7 @@ for i in range(numticks - len(self.majorTicks)): tick = self._get_tick(major=True) self.majorTicks.append(tick) - + if self._lastNumMajorTicks < numticks: protoTick = self.majorTicks[0] for i in range(self._lastNumMajorTicks, len(self.majorTicks)): @@ -798,7 +798,7 @@ for i in range(numticks - len(self.minorTicks)): tick = self._get_tick(major=False) self.minorTicks.append(tick) - + if self._lastNumMinorTicks < numticks: protoTick = self.minorTicks[0] for i in range(self._lastNumMinorTicks, len(self.minorTicks)): @@ -1039,7 +1039,7 @@ class XAxis(Axis): __name__ = 'xaxis' axis_name = 'x' - + def contains(self,mouseevent): """Test whether the mouse event occured in the x axis. """ @@ -1120,7 +1120,7 @@ bbox = Bbox.union(bboxes) bottom = bbox.y0 self.label.set_position( (x, bottom - self.LABELPAD*self.figure.dpi / 72.0)) - + else: if not len(bboxes2): top = self.axes.bbox.ymax @@ -1141,7 +1141,7 @@ bbox = Bbox.union(bboxes) bottom = bbox.y0 self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0)) - + def set_ticks_position(self, position): """ Set the ticks position (top, bottom, both, default or none) @@ -1224,10 +1224,10 @@ else: Vmin, Vmax = self.get_view_interval() self.axes.viewLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) - + def get_minpos(self): return self.axes.dataLim.minposx - + def get_data_interval(self): 'return the Interval instance for this axis data limits' return self.axes.dataLim.intervalx @@ -1244,7 +1244,7 @@ class YAxis(Axis): __name__ = 'yaxis' axis_name = 'y' - + def contains(self,mouseevent): """Test whether the mouse event occurred in the y axis. @@ -1331,7 +1331,7 @@ left = bbox.x0 self.label.set_position( (left-self.LABELPAD*self.figure.dpi/72.0, y)) - + else: if not len(bboxes2): right = self.axes.bbox.xmax @@ -1349,7 +1349,7 @@ x,y = self.offsetText.get_position() top = self.axes.bbox.ymax self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi/72.0)) - + def set_offset_position(self, position): assert position == 'left' or position == 'right' @@ -1445,10 +1445,10 @@ else: Vmin, Vmax = self.get_view_interval() self.axes.viewLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) - + def get_minpos(self): return self.axes.dataLim.minposy - + def get_data_interval(self): 'return the Interval instance for this axis data limits' return self.axes.dataLim.intervaly Modified: branches/transforms/lib/matplotlib/backends/backend_gdk.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gdk.py 2007-11-30 14:43:11 UTC (rev 4520) +++ branches/transforms/lib/matplotlib/backends/backend_gdk.py 2007-11-30 15:06:56 UTC (rev 4521) @@ -25,7 +25,7 @@ from matplotlib.cbook import is_string_like, enumerate from matplotlib.figure import Figure from matplotlib.mathtext import MathTextParser - +from matplotlib.transforms import Affine2D from matplotlib.backends._backend_gdk import pixbuf_get_pixels_array @@ -81,23 +81,25 @@ """ self.width, self.height = width, height - def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, rotation): - x, y = int(x-0.5*width), self.height-int(y+0.5*height) - w, h = int(width)+1, int(height)+1 - a1, a2 = int(angle1*64), int(angle2*64) + def draw_path(self, gc, path, transform, rgbFace=None): + transform = transform + Affine2D(). \ + scale(1.0, -1.0).translate(0, self.height) + polygons = path.to_polygons(transform) + for polygon in polygons: + # draw_polygon won't take an arbitrary sequence -- it must be a list + # of tuples + polygon = [(int(round(x)), int(round(y))) for x, y in polygon] + if rgbFace is not None: + saveColor = gc.gdkGC.foreground + gc.gdkGC.foreground = gc.rgb_to_gdk_color(rgbFace) + self.gdkDrawable.draw_polygon(gc.gdkGC, True, polygon) + gc.gdkGC.foreground = saveColor + if gc.gdkGC.line_width > 0: + self.gdkDrawable.draw_lines(gc.gdkGC, polygon) - if rgbFace: - saveColor = gc.gdkGC.foreground - gc.gdkGC.foreground = gc.rgb_to_gdk_color(rgbFace) - self.gdkDrawable.draw_arc(gc.gdkGC, True, x, y, w, h, a1, a2) - gc.gdkGC.foreground = saveColor - if gc.gdkGC.line_width > 0: - self.gdkDrawable.draw_arc(gc.gdkGC, False, x, y, w, h, a1, a2) - - - def draw_image(self, x, y, im, bbox): + def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None): if bbox != None: - l,b,w,h = bbox.get_bounds() + l,b,w,h = bbox.bounds #rectangle = (int(l), self.height-int(b+h), # int(w), int(h)) # set clip rect? @@ -135,48 +137,6 @@ im.flipud_out() - def draw_line(self, gc, x1, y1, x2, y2): - if gc.gdkGC.line_width > 0: - self.gdkDrawable.draw_line(gc.gdkGC, int(x1), self.height-int(y1), - int(x2), self.height-int(y2)) - - - def draw_lines(self, gc, x, y, transform=None): - if gc.gdkGC.line_width > 0: - x = x.astype(npy.int16) - y = self.height - y.astype(npy.int16) - self.gdkDrawable.draw_lines(gc.gdkGC, zip(x,y)) - - - def draw_point(self, gc, x, y): - self.gdkDrawable.draw_point(gc.gdkGC, int(x), self.height-int(y)) - - - def draw_polygon(self, gc, rgbFace, points): - points = [(int(x), self.height-int(y)) for x,y in points] - if rgbFace: - saveColor = gc.gdkGC.foreground - gc.gdkGC.foreground = gc.rgb_to_gdk_color(rgbFace) - self.gdkDrawable.draw_polygon(gc.gdkGC, True, points) - gc.gdkGC.foreground = saveColor - if gc.gdkGC.line_width > 0: - self.gdkDrawable.draw_polygon(gc.gdkGC, False, points) - - - def draw_rectangle(self, gc, rgbFace, x, y, width, height): - x, y = int(x), self.height-int(y+height) - #x, y = int(x), self.height-int(math.ceil(y+height)) - w, h = int(math.ceil(width)), int(math.ceil(height)) - - if rgbFace: - saveColor = gc.gdkGC.foreground - gc.gdkGC.foreground = gc.rgb_to_gdk_color(rgbFace) - self.gdkDrawable.draw_rectangle(gc.gdkGC, True, x, y, w, h) - gc.gdkGC.foreground = saveColor - if gc.gdkGC.line_width > 0: - self.gdkDrawable.draw_rectangle(gc.gdkGC, False, x, y, w, h) - - def draw_text(self, gc, x, y, s, prop, angle, ismath): x, y = int(x), int(y) @@ -200,7 +160,7 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle): ox, oy, width, height, descent, font_image, used_characters = \ - self.mathtext_parser.parse(s, self.dpi.get(), prop) + self.mathtext_parser.parse(s, self.dpi, prop) if angle==90: width, height = height, width @@ -213,7 +173,7 @@ # a numpixels by num fonts array Xall = npy.zeros((N,1), npy.uint8) - + image_str = font_image.as_str() Xall[:,0] = npy.fromstring(image_str, npy.uint8) @@ -310,12 +270,12 @@ # two (not one) layouts are created for every text item s (then they # are cached) - why? - key = self.dpi.get(), s, hash(prop) + key = self.dpi, s, hash(prop) value = self.layoutd.get(key) if value != None: return value - size = prop.get_size_in_points() * self.dpi.get() / 96.0 + size = prop.get_size_in_points() * self.dpi / 96.0 size = round(size) font_str = '%s, %s %i' % (prop.get_name(), prop.get_style(), size,) @@ -341,7 +301,7 @@ def get_text_width_height_descent(self, s, prop, ismath): if ismath: ox, oy, width, height, descent, font_image, used_characters = \ - self.mathtext_parser.parse(s, self.dpi.get(), prop) + self.mathtext_parser.parse(s, self.dpi, prop) return width, height, descent layout, inkRect, logicalRect = self._get_pango_layout(s, prop) @@ -386,9 +346,9 @@ return an allocated gtk.gdk.Color """ try: - return self._cached[rgb] + return self._cached[tuple(rgb)] except KeyError: - color = self._cached[rgb] = \ + color = self._cached[tuple(rgb)] = \ self._cmap.alloc_color( int(rgb[0]*65535),int(rgb[1]*65535),int(rgb[2]*65535)) return color @@ -404,14 +364,15 @@ def set_clip_rectangle(self, rectangle): GraphicsContextBase.set_clip_rectangle(self, rectangle) - l,b,w,h = rectangle + if rectangle is None: + return + l,b,w,h = rectangle.bounds rectangle = (int(l), self.renderer.height-int(b+h)+1, int(w), int(h)) #rectangle = (int(l), self.renderer.height-int(b+h), # int(w+1), int(h+2)) self.gdkGC.set_clip_rectangle(rectangle) - def set_dashes(self, dash_offset, dash_list): GraphicsContextBase.set_dashes(self, dash_offset, dash_list) @@ -486,7 +447,7 @@ def print_png(self, filename, *args, **kwargs): return self._print_image(filename, 'png') - + def _print_image(self, filename, format, *args, **kwargs): width, height = self.get_width_height() pixmap = gtk.gdk.Pixmap (None, width, height, depth=24) @@ -500,4 +461,6 @@ 0, 0, 0, 0, width, height) pixbuf.save(filename, format) - + + def get_default_filetype(self): + return 'png' Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-11-30 14:43:11 UTC (rev 4520) +++ branches/transforms/lib/matplotlib/path.py 2007-11-30 15:06:56 UTC (rev 4521) @@ -12,7 +12,7 @@ from matplotlib._path import point_in_path, get_path_extents, \ point_in_path_collection, get_path_collection_extents, \ - path_in_path, path_intersects_path + path_in_path, path_intersects_path, convert_path_to_polygons from matplotlib.cbook import simple_linear_interpolation KAPPA = 4.0 * (npy.sqrt(2) - 1) / 3.0 @@ -210,19 +210,17 @@ If transform is not None, the path will be transformed before performing the test. """ - if transform is None: - from transforms import IdentityTransform - transform = IdentityTransform() - return point_in_path(point[0], point[1], self, transform.frozen()) + if transform is not None: + transform = transform.frozen() + return point_in_path(point[0], point[1], self, transform) def contains_path(self, path, transform=None): """ Returns True if this path completely contains the given path. """ - if transform is None: - from transforms import IdentityTransform - transform = IdentityTransform() - return path_in_path(self, IdentityTransform(), path, transform) + if transform is not None: + transform = transform.frozen() + return path_in_path(self, None, path, transform) def get_extents(self, transform=None): """ @@ -232,9 +230,9 @@ algorithm will take into account the curves and deal with control points appropriately. """ - from transforms import Affine2D, Bbox - if transform is None: - transform = Affine2D() + from transforms import Bbox + if transform is not None: + transform = transform.frozen() return Bbox(get_path_extents(self, transform)) def intersects_path(self, other): @@ -267,6 +265,23 @@ new_codes = None return Path(vertices, new_codes) + def to_polygons(self, transform=None): + """ + Convert this path to a list of polygons. Each polygon is an + Nx2 array of vertices. In other words, each polygon has no + "move to" instructions or curves. + """ + if transform is not None: + transform = transform.frozen() + # Deal with the common and simple case + if self.codes is None: + if len(self.vertices): + return [transform.transform(self.vertices)] + return [] + # Deal with the case where there are curves and/or multiple + # subpaths (using extension code) + return convert_path_to_polygons(self, transform) + _unit_rectangle = None #@classmethod def unit_rectangle(cls): Modified: branches/transforms/src/_path.cpp =================================================================== --- branches/transforms/src/_path.cpp 2007-11-30 14:43:11 UTC (rev 4520) +++ branches/transforms/src/_path.cpp 2007-11-30 15:06:56 UTC (rev 4521) @@ -48,6 +48,8 @@ "count_bboxes_overlapping_bbox(bbox, bboxes)"); add_varargs_method("path_intersects_path", &_path_module::path_intersects_path, "path_intersects_path(p1, p2)"); + add_varargs_method("convert_path_to_polygons", &_path_module::convert_path_to_polygons, + "convert_path_to_polygons(path, trans)"); initialize("Helper functions for paths"); } @@ -66,6 +68,7 @@ Py::Object affine_transform(const Py::Tuple& args); Py::Object count_bboxes_overlapping_bbox(const Py::Tuple& args); Py::Object path_intersects_path(const Py::Tuple& args); + Py::Object convert_path_to_polygons(const Py::Tuple& args); }; // @@ -137,7 +140,7 @@ code = path.vertex(&x, &y); // The following cases denote the beginning on a new subpath - if (code == agg::path_cmd_stop || + if (code == agg::path_cmd_stop || (code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { x = sx; @@ -234,7 +237,7 @@ double x = Py::Float(args[0]); double y = Py::Float(args[1]); PathIterator path(args[2]); - agg::trans_affine trans = py_to_agg_transformation_matrix(args[3]); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[3], false); if (::point_in_path(x, y, path, trans)) return Py::Int(1); @@ -623,9 +626,9 @@ args.verify_length(4); PathIterator a(args[0]); - agg::trans_affine atrans = py_to_agg_transformation_matrix(args[1]); + agg::trans_affine atrans = py_to_agg_transformation_matrix(args[1], false); PathIterator b(args[2]); - agg::trans_affine btrans = py_to_agg_transformation_matrix(args[3]); + agg::trans_affine btrans = py_to_agg_transformation_matrix(args[3], false); return Py::Int(::path_in_path(a, atrans, b, btrans)); } @@ -1091,6 +1094,68 @@ return Py::Int(1); } +void _add_polygon(Py::List& polygons, const std::vector<double>& polygon) { + if (polygon.size() == 0) + return; + npy_intp polygon_dims[] = { polygon.size() / 2, 2, 0 }; + double* polygon_data = new double[polygon.size()]; + memcpy(polygon_data, &polygon[0], polygon.size() * sizeof(double)); + PyArrayObject* polygon_array = NULL; + polygon_array = (PyArrayObject*)PyArray_SimpleNewFromData + (2, polygon_dims, PyArray_DOUBLE, polygon_data); + if (!polygon_array) + { + delete[] polygon_data; + throw Py::RuntimeError("Error creating polygon array"); + } + polygons.append(Py::Object((PyObject*)polygon_array)); +} + +Py::Object _path_module::convert_path_to_polygons(const Py::Tuple& args) +{ + typedef agg::conv_transform<PathIterator> transformed_path_t; + typedef agg::conv_curve<transformed_path_t> curve_t; + + typedef std::vector<double> vertices_t; + + args.verify_length(2); + + PathIterator path(args[0]); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[1], false); + + transformed_path_t tpath(path, trans); + curve_t curve(tpath); + + Py::List polygons; + vertices_t polygon; + double x, y; + unsigned code; + + while ((code = curve.vertex(&x, &y)) != agg::path_cmd_stop) + { + if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { + if (polygon.size() >= 2) + { + polygon.push_back(polygon[0]); + polygon.push_back(polygon[1]); + _add_polygon(polygons, polygon); + } + polygon.clear(); + } else { + if (code == agg::path_cmd_move_to) { + _add_polygon(polygons, polygon); + polygon.clear(); + } + polygon.push_back(x); + polygon.push_back(y); + } + } + + _add_polygon(polygons, polygon); + + return polygons; +} + extern "C" DL_EXPORT(void) init_path(void) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |