Revision: 3965
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3965&view=rev
Author:   mdboom
Date:     2007-10-18 11:11:59 -0700 (Thu, 18 Oct 2007)

Log Message:
-----------
First pass at working PDF backend.

Modified Paths:
--------------
    branches/transforms/lib/matplotlib/backends/backend_pdf.py
    branches/transforms/lib/matplotlib/backends/backend_ps.py
    branches/transforms/lib/matplotlib/projections/polar.py
    branches/transforms/lib/matplotlib/transforms.py
    branches/transforms/src/_backend_agg.cpp

Modified: branches/transforms/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_pdf.py  2007-10-18 
18:07:06 UTC (rev 3964)
+++ branches/transforms/lib/matplotlib/backends/backend_pdf.py  2007-10-18 
18:11:59 UTC (rev 3965)
@@ -33,7 +33,10 @@
     LOAD_NO_HINTING, KERNING_UNFITTED
 from matplotlib.mathtext import MathTextParser
 from matplotlib.transforms import Bbox
+from matplotlib.path import Path
 from matplotlib import ttconv
+# MGDTODO: Move this stuff
+from matplotlib.backends._backend_agg import get_path_extents
 
 # Overview
 #
@@ -90,22 +93,26 @@
     """Make one string from sequence of strings, with whitespace
     in between. The whitespace is chosen to form lines of at most
     linelen characters, if possible."""
-
-    s, strings = [strings[0]], strings[1:]
-    while strings:
-        if len(s[-1]) + len(strings[0]) < linelen:
-            s[-1] += ' ' + strings[0]
+    currpos = 0
+    lasti = 0
+    result = []
+    for i, s in enumerate(strings):
+        length = len(s)
+        if currpos + length < linelen:
+            currpos += length + 1
         else:
-            s.append(strings[0])
-        strings = strings[1:]
-    return '\n'.join(s)
+            result.append(' '.join(strings[lasti:i]))
+            lasti = i
+            currpos = length
+    result.append(' '.join(strings[lasti:]))
+    return '\n'.join(result)
 
 
 def pdfRepr(obj):
     """Map Python objects to PDF syntax."""
 
     # Some objects defined later have their own pdfRepr method.
-    if 'pdfRepr' in dir(obj):
+    if hasattr(obj, 'pdfRepr'):
         return obj.pdfRepr()
 
     # Floats. PDF does not have exponential notation (1.0e-10) so we
@@ -164,6 +171,13 @@
         else: r += "-%02d'%02d'" % (z//3600, z%3600)
         return pdfRepr(r)
 
+    # A bounding box
+    elif isinstance(obj, Bbox):
+        r = ["["]
+        r.extend([pdfRepr(val) for val in obj.lbrt])
+        r.append("]")
+        return fill(r)
+    
     else:
         raise TypeError, \
             "Don't know a PDF representation for %s objects." \
@@ -379,7 +393,6 @@
 
         self.markers = {}
         self.two_byte_charprocs = {}
-        self.nextMarker = 1
 
         # The PDF spec recommends to include every procset
         procsets = [ Name(x)
@@ -409,8 +422,8 @@
                                for val in self.alphaStates.values()]))
         self.writeHatches()
         xobjects = dict(self.images.values())
-        for name, value in self.markers.items():
-            xobjects[name] = value[0]
+        for tup in self.markers.values():
+            xobjects[tup[0]] = tup[1]
         for name, value in self.two_byte_charprocs.items():
             xobjects[name] = value
         self.writeObject(self.XObjectObject, xobjects)
@@ -1009,71 +1022,64 @@
 
             img.flipud_out()
 
-    def markerObject(self, path, fillp, lw):
+    def markerObject(self, path, trans, fillp, lw):
         """Return name of a marker XObject representing the given path."""
-
-        name = Name('M%d' % self.nextMarker)
-        ob = self.reserveObject('marker %d' % self.nextMarker)
-        self.nextMarker += 1
-        self.markers[name] = (ob, path, fillp, lw)
+        key = (path, trans)
+        result = self.markers.get(key)
+        if result is None:
+            name = Name('M%d' % len(self.markers))
+            ob = self.reserveObject('marker %d' % len(self.markers))
+            self.markers[key] = (name, ob, path, trans, fillp, lw)
+        else:
+            name = result[0]
         return name
-
+    
     def writeMarkers(self):
-        for name, tuple in self.markers.items():
-            object, path, fillp, lw = tuple
+        for tup in self.markers.values():
+            name, object, path, trans, fillp, lw = tup
+            a, b, c, d = get_path_extents(path, trans)
+            bbox = Bbox.from_lbrt(*get_path_extents(path, trans))
+            bbox = bbox.padded(lw * 0.5)
             self.beginStream(
                 object.id, None,
                 {'Type': Name('XObject'), 'Subtype': Name('Form'),
-                 'BBox': self.pathBbox(path, lw) })
-            self.writePath(path, fillp)
+                 'BBox': bbox })
+            self.writePath(path, trans)
+            if fillp:
+                self.output(Op.fill_stroke)
+            else:
+                self.output(Op.stroke)
             self.endStream()
 
     [EMAIL PROTECTED]
-    def pathBbox(path, lw):
-        path.rewind(0)
-        x, y = [], []
-        while True:
-            code, xp, yp = path.vertex()
-            if code & agg.path_cmd_mask in \
-                    (agg.path_cmd_move_to, agg.path_cmd_line_to):
-                x.append(xp)
-                y.append(yp)
-            elif code == agg.path_cmd_stop:
-                break
-        return min(x)-lw, min(y)-lw, max(x)+lw, max(y)+lw
-    pathBbox = staticmethod(pathBbox)
-
-    [EMAIL PROTECTED]
-    def pathOperations(path):
-        path.rewind(0)
-        result = []
-        while True:
-            code, x, y = path.vertex()
-            code = code & agg.path_cmd_mask
-            if code == agg.path_cmd_stop:
-                break
-            elif code == agg.path_cmd_move_to:
-                result += (x, y, Op.moveto)
-            elif code == agg.path_cmd_line_to:
-                result += (x, y, Op.lineto)
-            elif code == agg.path_cmd_curve3:
-                pass # TODO
-            elif code == agg.path_cmd_curve4:
-                pass # TODO
-            elif code == agg.path_cmd_end_poly:
-                result += (Op.closepath,)
-            else:
-                print >>sys.stderr, "pathOperations", code, xp, yp
-        return result
+    def pathOperations(path, transform):
+        tpath = transform.transform_path(path)
+        
+        cmds = []
+        for points, code in tpath.iter_segments():
+            if code == Path.MOVETO:
+                cmds.extend(points)
+                cmds.append(Op.moveto)
+            elif code == Path.LINETO:
+                cmds.extend(points)
+                cmds.append(Op.lineto)
+            elif code == Path.CURVE3:
+                cmds.extend([points[0], points[1],
+                             points[0], points[1],
+                             points[2], points[3],
+                             Op.curveto])
+            elif code == Path.CURVE4:
+                cmds.extend(points)
+                cmds.append(Op.curveto)
+            elif code == Path.CLOSEPOLY:
+                cmds.append(Op.closepath)
+        return cmds
     pathOperations = staticmethod(pathOperations)
-
-    def writePath(self, path, fillp):
-        self.output(*self.pathOperations(path))
-        if fillp:
-            self.output(Op.fill_stroke)
-        else:
-            self.output(Op.stroke)
-
+            
+    def writePath(self, path, transform):
+        cmds = self.pathOperations(path, transform)
+        self.output(*cmds)
+        
     def reserveObject(self, name=''):
         """Reserve an ID for an indirect object.
         The name is used for debugging in case we forget to print out
@@ -1177,59 +1183,6 @@
                 stat_key, (realpath, Set()))
             used_characters[1].update(set)
 
-    def draw_arc(self, gcEdge, rgbFace, x, y, width, height,
-                 angle1, angle2, rotation):
-        """
-        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, rotated by `rotation` degrees
-        positive angles are anti-clockwise
-
-        If the color rgbFace is not None, fill the arc with it.
-        """
-        # source: agg_bezier_arc.cpp in agg23
-
-        def arc_to_bezier(cx, cy, rx, ry, angle1, sweep, rotation):
-            halfsweep = sweep / 2.0
-            x0, y0 = cos(halfsweep), sin(halfsweep)
-            tx = (1.0 - x0) * 4.0/3.0;
-            ty = y0 - tx * x0 / y0;
-            px =  x0, x0+tx, x0+tx, x0
-            py = -y0,   -ty,    ty, y0
-            sn, cs = sin(angle1 + halfsweep), cos(angle1 + halfsweep)
-            result = [ (rx * (pxi * cs - pyi * sn),
-                        ry * (pxi * sn + pyi * cs))
-                       for pxi, pyi in zip(px, py) ]
-            result = [ (cx + cos(rotation)*x - sin(rotation)*y,
-                        cy + sin(rotation)*x + cos(rotation)*y)
-                       for x, y in result ]
-            return reduce(lambda x, y: x + y, result)
-
-        epsilon = 0.01
-        angle1 *= pi/180.0
-        angle2 *= pi/180.0
-        rotation *= pi/180.0
-        sweep = angle2 - angle1
-        angle1 = angle1 % (2*pi)
-        sweep = min(max(-2*pi, sweep), 2*pi)
-
-        if sweep < 0.0:
-            sweep, angle1, angle2 = -sweep, angle2, angle1
-        bp = [ pi/2.0 * i
-               for i in range(4)
-               if pi/2.0 * i < sweep-epsilon ]
-        bp.append(sweep)
-        subarcs = [ arc_to_bezier(x, y, width/2.0, height/2.0,
-                                  bp[i], bp[i+1]-bp[i], rotation)
-                    for i in range(len(bp)-1) ]
-
-        self.check_gc(gcEdge, rgbFace)
-        self.file.output(subarcs[0][0], subarcs[0][1], Op.moveto)
-        for arc in subarcs:
-            self.file.output(*(arc[2:] + (Op.curveto,)))
-
-        self.file.output(self.gc.close_and_paint())
-
     def get_image_magnification(self):
         return self.image_magnification
 
@@ -1246,82 +1199,29 @@
         self.file.output(Op.gsave, w, 0, 0, h, x, y, Op.concat_matrix,
                          imob, Op.use_xobject, Op.grestore)
 
-    def draw_line(self, gc, x1, y1, x2, y2):
-        if npy.isnan(x1) or npy.isnan(x2) or npy.isnan(y1) or npy.isnan(y2):
-            return
-        self.check_gc(gc)
-        self.file.output(x1, y1, Op.moveto,
-                         x2, y2, Op.lineto, self.gc.paint())
-
-    def draw_lines(self, gc, x, y, transform=None):
-        self.check_gc(gc)
-        if transform is not None:
-            x, y = transform.seq_x_y(x, y)
-        nan_at = npy.isnan(x) | npy.isnan(y)
-        next_op = Op.moveto
-        for i in range(len(x)):
-            if nan_at[i]:
-                next_op = Op.moveto
-            else:
-                self.file.output(x[i], y[i], next_op)
-                next_op = Op.lineto
+    def draw_path(self, gc, path, transform, rgbFace=None):
+        self.check_gc(gc, rgbFace)
+        stream = self.file.writePath(path, transform)
         self.file.output(self.gc.paint())
 
-    def draw_point(self, gc, x, y):
-        print >>sys.stderr, "draw_point called"
-
-        self.check_gc(gc, gc._rgb)
-        self.file.output(x, y, 1, 1,
-                         Op.rectangle, Op.fill_stroke)
-
-    def draw_polygon(self, gcEdge, rgbFace, points):
-        # Optimization for axis-aligned rectangles
-        if len(points) == 4:
-            if points[0][0] == points[1][0] and points[1][1] == points[2][1] 
and \
-               points[2][0] == points[3][0] and points[3][1] == points[0][1]:
-                self.draw_rectangle(gcEdge, rgbFace,
-                                    min(points[0][0], points[2][0]),
-                                    min(points[1][1], points[3][1]),
-                                    abs(points[2][0] - points[0][0]),
-                                    abs(points[3][1] - points[1][1]))
-                return
-            elif points[0][1] == points[1][1] and points[1][0] == points[2][0] 
and \
-                 points[2][1] == points[3][1] and points[3][0] == points[0][0]:
-                self.draw_rectangle(gcEdge, rgbFace,
-                                    min(points[1][0], points[3][0]),
-                                    min(points[2][1], points[0][1]),
-                                    abs(points[1][0] - points[3][0]),
-                                    abs(points[2][1] - points[0][1]))
-                return
-
-        self.check_gc(gcEdge, rgbFace)
-        self.file.output(points[0][0], points[0][1], Op.moveto)
-        for x,y in points[1:]:
-            self.file.output(x, y, Op.lineto)
-        self.file.output(self.gc.close_and_paint())
-
-    def draw_rectangle(self, gcEdge, rgbFace, x, y, width, height):
-        self.check_gc(gcEdge, rgbFace)
-        self.file.output(x, y, width, height, Op.rectangle)
-        self.file.output(self.gc.paint())
-
-    def draw_markers(self, gc, path, rgbFace, x, y, trans):
+    def draw_markers(self, gc, marker_path, marker_trans, path, trans, 
rgbFace=None):
         self.check_gc(gc, rgbFace)
         fillp = rgbFace is not None
-        marker = self.file.markerObject(path, fillp, self.gc._linewidth)
-        x, y = trans.numerix_x_y(npy.asarray(x), npy.asarray(y))
-        nan_at = npy.isnan(x) | npy.isnan(y)
 
-        self.file.output(Op.gsave)
-        ox, oy = 0, 0
-        for i in range(len(x)):
-            if nan_at[i]: continue
-            dx, dy, ox, oy = x[i]-ox, y[i]-oy, x[i], y[i]
-            self.file.output(1, 0, 0, 1, dx, dy,
-                             Op.concat_matrix,
-                             marker, Op.use_xobject)
-        self.file.output(Op.grestore)
+        output = self.file.output
+        marker = self.file.markerObject(
+            marker_path, marker_trans, fillp, self.gc._linewidth)
+        tpath = trans.transform_path(path)
 
+        output(Op.gsave)
+        lastx, lasty = 0, 0
+        for x, y in tpath.vertices:
+            dx, dy = x - lastx, y - lasty
+            output(1, 0, 0, 1, dx, dy, Op.concat_matrix,
+                   marker, Op.use_xobject)
+            lastx, lasty = x, y
+        output(Op.grestore)
+        
     def _setup_textpos(self, x, y, angle, oldx=0, oldy=0, oldangle=0):
         if angle == oldangle == 0:
             self.file.output(x - oldx, y - oldy, Op.textpos)
@@ -1735,7 +1635,7 @@
 
     def hatch_cmd(self, hatch):
         if not hatch:
-            if self._fillcolor:
+            if self._fillcolor is not None:
                 return self.fillcolor_cmd(self._fillcolor)
             else:
                 return [Name('DeviceRGB'), Op.setcolorspace_nonstroke]
@@ -1757,7 +1657,7 @@
         if rgb[0] == rgb[1] == rgb[2]:
             return [rgb[0], Op.setgray_stroke]
         else:
-            return list(rgb) + [Op.setrgb_stroke]
+            return list(rgb[:3]) + [Op.setrgb_stroke]
 
     def fillcolor_cmd(self, rgb):
         if rgb is None or rcParams['pdf.inheritcolor']:
@@ -1765,7 +1665,7 @@
         elif rgb[0] == rgb[1] == rgb[2]:
             return [rgb[0], Op.setgray_nonstroke]
         else:
-            return list(rgb) + [Op.setrgb_nonstroke]
+            return list(rgb[:3]) + [Op.setrgb_nonstroke]
 
     def push(self):
         parent = GraphicsContextPdf(self.file)
@@ -1791,11 +1691,12 @@
         if (self._cliprect, self._clippath) != (cliprect, clippath):
             cmds.extend(self.push())
             if self._cliprect != cliprect:
-                cmds.extend([t for t in cliprect] + 
-                            [Op.rectangle, Op.clip, Op.endpath])
+                cmds.extend([cliprect, Op.rectangle, Op.clip, Op.endpath])
             if self._clippath != clippath:
-                cmds.extend(PdfFile.pathOperations(clippath) +
-                            [Op.clip, Op.endpath])
+                cmds.extend(
+                    PdfFile.pathOperations(
+                        *clippath.get_transformed_path_and_affine()) +
+                    [Op.clip, Op.endpath])
         return cmds
 
     commands = (
@@ -1821,7 +1722,11 @@
         for params, cmd in self.commands:
             ours = [ getattr(self, p) for p in params ] 
             theirs = [ getattr(other, p) for p in params ]
-            if ours != theirs:
+            try:
+                different = ours != theirs
+            except ValueError:
+                different = ours.shape != theirs.shape or npy.any(ours != 
theirs)
+            if ours is not theirs:
                 cmds.extend(cmd(self, *theirs))
                 for p in params:
                     setattr(self, p, getattr(other, p))

Modified: branches/transforms/lib/matplotlib/backends/backend_ps.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_ps.py   2007-10-18 
18:07:06 UTC (rev 3964)
+++ branches/transforms/lib/matplotlib/backends/backend_ps.py   2007-10-18 
18:11:59 UTC (rev 3965)
@@ -1433,14 +1433,4 @@
       clip
       newpath
     } bind def""",
-    """/unitcircle {
-    newpath
--1. 0. moveto
--1.0 0.552284749831 -0.552284749831 1.0 0.0 1.0 curveto
-0.552284749831 1.0 1.0 0.552284749831 1.0 0.0 curveto
-1.0 -0.552284749831 0.552284749831 -1.0 0.0 -1.0 curveto
--0.552284749831 -1.0 -1.0 -0.552284749831 -1.0 0.0 curveto
-closepath
-    } bind def""",
-    
 ]

Modified: branches/transforms/lib/matplotlib/projections/polar.py
===================================================================
--- branches/transforms/lib/matplotlib/projections/polar.py     2007-10-18 
18:07:06 UTC (rev 3964)
+++ branches/transforms/lib/matplotlib/projections/polar.py     2007-10-18 
18:11:59 UTC (rev 3965)
@@ -41,13 +41,13 @@
             self._resolution = resolution
 
         def transform(self, tr):
-            xy = npy.zeros(tr.shape, npy.float_)
-            t = tr[:, 0:1]
-            r = tr[:, 1:2]
-            x = xy[:, 0:1]
-            y = xy[:, 1:2]
-            x += r * npy.cos(t)
-            y += r * npy.sin(t)
+            xy   = npy.zeros(tr.shape, npy.float_)
+            t    = tr[:, 0:1]
+            r    = tr[:, 1:2]
+            x    = xy[:, 0:1]
+            y    = xy[:, 1:2]
+            x[:] = r * npy.cos(t)
+            y[:] = r * npy.sin(t)
             return xy
         transform.__doc__ = Transform.transform.__doc__
 

Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py    2007-10-18 18:07:06 UTC 
(rev 3964)
+++ branches/transforms/lib/matplotlib/transforms.py    2007-10-18 18:11:59 UTC 
(rev 3965)
@@ -490,6 +490,14 @@
         a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
         return Bbox(self._points + a)
 
+    def padded(self, p):
+        """
+        Return a new Bbox that is padded on all four sides by the
+        given value.
+        """
+        points = self._points
+        return Bbox(points + [[-p, -p], [p, p]])
+    
     def translated(self, tx, ty):
         """
         Return a copy of the Bbox, translated by tx and ty.

Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp    2007-10-18 18:07:06 UTC (rev 
3964)
+++ branches/transforms/src/_backend_agg.cpp    2007-10-18 18:11:59 UTC (rev 
3965)
@@ -1573,12 +1573,12 @@
   curved_path.rewind(0);
 
   while ((code = curved_path.vertex(&x, &y)) != agg::path_cmd_stop) {
-    if (code & agg::path_cmd_end_poly == agg::path_cmd_end_poly)
-      continue;
     if (x < *x0) *x0 = x;
     if (y < *y0) *y0 = y;
     if (x > *x1) *x1 = x;
     if (y > *y1) *y1 = y;
+    if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly)
+      continue;
   }
 }
 


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Matplotlib-checkins mailing list
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to