line fading

For me, the plot generated by x2 (the second call of show)

    > is dense in the middle and fades out at the edges. I assume
    > this is somehow due to the line redrawing over itself as it
    > moves from point to point.

    > Is there a good way to clean it up, other than always
    > sorting the x variable list?

Hi Mark,

The problem arises from a combination of when in the rendering
pipeline matplotlib does the transformation from data to display
coordinates, and how agg handles antialiasing with subpixel rendering.
The short answer is that matplotlib handles the transformation in the
front end and then passes the backend display coords to render. This
doesn't play nicely with agg, which does different things depending on
the fractional value of the display coordinate (aka subpixel
rendering). It's something I would like to change, but it is a fairly
substantial piece of work so I'm holding off on it for now. Drawing
with

  plot(x, y, antialiased=False)

will improve the situation, but then you won't have anitaliasing, and
it still won't be perfect.

I posted a variant of your question to the antigrain mailing list (see
below) in hopes of understanding this issue better myself, and Maxim,
the antigrain author, was kind enough to write this web page in reply

  http://antigrain.com/tips/line_alignment/line_alignment.agdoc.html

He also pointed out that SVG has the same behavior. The best
solution, as noted above, is to render in the following order

  set the path
  apply the affine transformation
  rasterize

whereas I am doing

  apply the affine transformation
  set the path
  rasterize

and there is no way to change this w/o significant changes to al lthe
drawing functions in all the backends.

Note that the following simpler example exposes the problem

    from matplotlib.matlab import *
    x = array([2,5,3,1,4])
    plot(x, x)
    show()

If I come up with something better I'll let you know; even after
reading Maxim's webpage I'm still confused on a couple of issues so
I'll follow-up. Note that this is a problem specific to the agg
backend, so if you need to produce something for distribution and want
to avoid this problem, you might be able to use another backend.

I'll include my post to the antigrain list below, where I have
translated your example into display coords, ie, what agg sees.

JDH

···

From: John Hunter <jdhunter@...4...>
Subject: [AGG] line paths, subpixel, and aa
To: Vector-agg-general@lists.sourceforge.net
Date: Fri, 04 Jun 2004 15:39:44 -0500
Reply-To: vector-agg-general@lists.sourceforge.net

I develop a plotting library (matplotlib) that has an antigrain
backend. The library is setup to do the transformations on the front
end and pass the backends drawing commands in display coordinates.
The library was designed before I started using antigrain as a
backend, and one unfortunate side-effect of this is that the backend
often gets coordinates with a variety of fractional values, eg 100.0,
100.25, 100.5. The front end does not understand agg's subpixel
rendering.

So occasionally we get "strange" results like uneven thickness of
lines. I have a few hacks to deal with this in common use cases, but
it still is a lingering problem.

Here is one particularly strange (to me) manifestation. I don't fully
understand how agg handles line widths with antialiasing and subpixel
locations, so I'm hoping to use this example to enlighten me.

If a path is drawn as (canvas is 640x480 - all these points more or
less fall on the same line)

  path.move_to(204, 332.4);
  path.line_to(576, 48);
  path.line_to(328, 237.6);
  path.line_to(80, 427.2);
  path.line_to(452, 142.8);

There is a strong effect of line width. For example, the line at (80,
427.2) is extremely (vanishingly) thin, but thick in the middle at
328, 237.6. raw rgba pixel dump is at
http://nitace.bsd.uchicago.edu:8080/files/share/line_aa.raw.

If the order the line is drawn is changed to (points ordered by
increasing x,y)

  path.move_to(80, 427.2);
  path.line_to(204, 332.4);
  path.line_to(328, 237.6);
  path.line_to(452, 142.8);
  path.line_to(576, 48);

The line width is more or less uniform.

Can someone enlighten me here? (Complete example below).

On another note, does anyone have advice on how to solve these kinds
of problems in general. Another example is in drawing of horizontal
or vertical tick lines for graphs. The tick locations are in data
coordinates and the front end transforms these into display coords as
above. Depending on the data location, these might end up having any
fractional display value, and thus the thickness of these rendered
lines varies. My solution for this case is to detect at the backend
(agg) whether the line is horizontal or vertical and "snap-to" the
nearest integer + 0.5 location. This works OK for horiz and vertical
lines.

But for arbitrary line paths, eg, sine waves, snapping all points to
the same fractional value introduces other kinds of visual problems,
so I don't do this - but then I get the kinds of problems in the
example above.

General question: if the transformations were handled in agg, eg the
frontend passed data coords and I set the transformation matrices in
agg accordingly, would I still face these problems?

Sorry for the somewhat vague and meanering post - hopefully someone
can understand my problem and enlighten me!

John Hunter

#include <fstream>

#include "agg_path_storage.h"
#include "agg_pixfmt_rgb24.h"
#include "agg_pixfmt_rgba32.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_bin.h"
#include "agg_scanline_p32.h"
#include "agg_conv_stroke.h"

typedef agg::pixel_formats_rgba32<agg::order_rgba32> pixfmt;
typedef agg::renderer_base<pixfmt> renderer_base;
typedef agg::rasterizer_scanline_aa<> rasterizer;

// antialiased
typedef agg::renderer_scanline_p_solid<renderer_base> renderer;
typedef agg::scanline_p8 scanline;

// aliased
//typedef agg::scanline_bin scanline;
//typedef agg::renderer_scanline_bin_solid<renderer_base> renderer;

int main(int argc, char* argv) {

  unsigned width(640), height(480);
  unsigned stride(width*4);

  size_t NUMBYTES(width*height*4);
  agg::int8u buffer[NUMBYTES];

  agg::rendering_buffer rbuf;
  rbuf.attach(buffer, width, height, stride);

  //draw_anti_aliased

  pixfmt pixf(rbuf);
  renderer_base rb(pixf);
  renderer ren(rb);
  rasterizer ras;
  scanline sline;

  agg::path_storage path;
  rb.clear(agg::rgba(1.0, 1.0, 1.0, 1.0));

  path.move_to(204, 332.4);
  path.line_to(576, 48);
  path.line_to(328, 237.6);
  path.line_to(80, 427.2);
  path.line_to(452, 142.8);
  /*
  path.move_to(80, 427.2);
  path.line_to(204, 332.4);
  path.line_to(328, 237.6);
  path.line_to(452, 142.8);
  path.line_to(576, 48);
  */

  agg::conv_stroke<agg::path_storage> stroke(path);
  stroke.width( 1);
  ras.add_path(stroke);

  ren.color(agg::rgba(0.0, 0.0, 0.0, 1.0));
  ras.render(sline, ren);

  size_t i;
  std::ofstream of2( "line_aa.raw", std::ios::binary|std::ios::out);
  for (i=0; i<NUMBYTES; ++i)
    of2.write((char*)&buffer[i], sizeof(char));
  
}