From: John H. <jdh...@ac...> - 2004-06-07 11:56:05
|
>>>>> "Mark" == Mark Engelhardt <ma...@st...> writes: Mark> For me, the plot generated by x2 (the second call of show) Mark> is dense in the middle and fades out at the edges. I assume Mark> this is somehow due to the line redrawing over itself as it Mark> moves from point to point. Mark> Is there a good way to clean it up, other than always Mark> 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 <jdh...@ac...> Subject: [AGG] line paths, subpixel, and aa To: Vec...@li... Date: Fri, 04 Jun 2004 15:39:44 -0500 Reply-To: vec...@li... 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)); } |