NAME
App::Chart::Gtk2::Ex::LineClipper -- accumulate and/or draw connected line segments
SYNOPSIS
use App::Chart::Gtk2::Ex::LineClipper;
# direct draw
App::Chart::Gtk2::Ex::LineClipper::clipped_draw_lines
($drawable, $gc, $line_width, $points_list);
# OOP accumulator
my $linedrawer = App::Chart::Gtk2::Ex::LineClipper->new (drawable => $drawable);
$linedrawer->add ($gc, $x1, $y1);
$linedrawer->add ($gc, $x2, $y2);
$linedrawer->end;
DESCRIPTION
App::Chart::Gtk2::Ex::LineClipper helps you draw connected line segments like Gtk2::Drawable->draw_lines does, but with benefits of clipping wild coordinates (and not even sent to the server if not visible), and an accumulator mechanism to build line sequences.
Clipping wild X,Y values on the client side is important because Gdk silently takes just the low 16 bits of each. For example if you draw a line from 100,50 to 65736,50 you'll be unpleasantly surprised to find X=65736 comes out as X=200 (its low 16 bits), instead of extending to the far right hand end of the window.
The object-oriented accumulator is helpful if you've got a tricky loop generating points and colours for each segment and want someone else to keep track of what, if anything, you build up to draw.
DIRECT DRAWING
App::Chart::Gtk2::Ex::LineClipper::clipped_draw_lines ($drawable, $gc, $line_width, $points)-
Draw lines connecting the X,Y points in
$points, likeGtk2::Drawable::draw_linesdoes, but clipping to the size of the drawable so huge coordinate values don't wrap around, and indeed are not sent to the server at all if completely off-screen.$pointsis a reference to an array of X,Y values, one after the other.$drawableis aGtk2::Gdk::Drawable(window, pixmap, etc) and$gcis aGtk2::Gdk::GC. Eg.App::Chart::Gtk2::Ex::LineClipper::clipped_draw_lines ($drawable, $gc, 1, [ 100,100, 200,200, 300,100 ]);$line_widthshould be the width in pixels of the lines$gcwill draw. This is used to know how far off the drawable the clipping must extend so thecap_styledoesn't show.clipped_draw_linesdoesn't simply read$line_widthfrom$gc->get_valuesbecause that call does a round-trip to the X server. A width bigger than actually in use is fine; for instance you might just pass 20 pixels if you know you never draw lines wider than that. A width 0 is interpreted as a 1 pixel "thin line", the same as happens in the GC.If
$pointsis an empty array, or an array of just one X,Y point, then nothing at all is drawn. This handling of one point adds certainty to what plain$win->draw_linesdoes; for it a single X,Y of a 0-width "thin line" might or might not be drawn, depending on the server.
ACCUMULATOR OBJECT
App::Chart::Gtk2::Ex::LineClipper->new (drawable => $drawable, ...)-
Create and return a new LineClipper object to accumulate and draw connected line segments on
$drawableusingclipped_draw_linesabove.$drawableis aGtk2::Gdk::Drawableobject as above, and aline_widthparameter can be passed to set that for the clipping (again as above).my $linedrawer = App::Chart::Gtk2::Ex::LineClipper->new (drawable => $drawable, line_width => 5); # pixelsBy default a single solitary X,Y point is not drawn, on the principle that it's not a line segment, but the
draw_pointoption can be set true to have it shown as a circle of the givenline_width. Eg.my $linedrawer = App::Chart::Gtk2::Ex::LineClipper->new (drawable => $drawable, line_width => 5, # pixels draw_point => 1); # trueIf you're wondering that
line_widthused as a diameter won't come out circular if the pixels aren't square, well, yes, but the same is true of the line drawing. The single width used vertically and horizontally for the line segments makes them appear wider or narrower according to their angle. $linedrawer->add ($gc, $x, $y)-
Add a point to
$linedrawer. The points accumulated will have connected line segments drawn from each to the next, in the order they're added.$gcis aGtk2::Gdk::GCfor the new segment, ie. from the current endpoint to the new$x,$y. Different GCs can be used for different segments and the LineClipper takes care of passing runs of the same GC toclipped_draw_lines. $linedrawer->end()-
Draw the line segments accumulated, if any, and set
$linedrawerback to empty, ready to start a new completely separate sequence of points.You can use this to force a gap between points, ie. draw everything accumulated so far and make subsequent points a new run.
endis called automatically when$linedraweris destroyed, which means that if you've got a complicated loop generating the points then you can justreturnor jump out from multiple places, confident that letting the LineClipper go out of scope will flush what you've accumulated.
OTHER NOTES
The choice between the direct clipped_draw_lines and the accumulator object is purely a matter of convenience. If you've got all your values in an array and want to draw just one colour (one GC) then the best idea is to map or whatever to scale and build an X,Y array, then call clipped_draw_lines. But if you're going to make decisions about skipping some points or changing colours or leaving gaps while passing over the data then you may find the accumulator easier.
When switching to a different GC with add, the join between the lines in the old and new GCs doesn't use the join style (Gtk2::Gdk::JoinStyle) of either GC, but rather merely gets the cap style (Gtk2::Gdk::CapStyle) of each where the draw_lines from one ends and the other begins. This means you can't get a nice mitre or bevel when changing colours etc. For now it seems far too much trouble to try to do anything about that. The suggestion would be to use a cap style of round which comes out looking nice at any angle. Or if you only do GC switching with lines a few pixels wide then one or two slightly off where they meet will be hardly noticeable.