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_lines
does, 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.$points
is a reference to an array of X,Y values, one after the other.$drawable
is aGtk2::Gdk::Drawable
(window, pixmap, etc) and$gc
is aGtk2::Gdk::GC
. Eg.App::Chart::Gtk2::Ex::LineClipper::clipped_draw_lines ($drawable, $gc, 1, [ 100,100, 200,200, 300,100 ]);
$line_width
should be the width in pixels of the lines$gc
will draw. This is used to know how far off the drawable the clipping must extend so thecap_style
doesn't show.clipped_draw_lines
doesn't simply read$line_width
from$gc->get_values
because 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
$points
is 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_lines
does; 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
$drawable
usingclipped_draw_lines
above.$drawable
is aGtk2::Gdk::Drawable
object as above, and aline_width
parameter 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); # pixels
By default a single solitary X,Y point is not drawn, on the principle that it's not a line segment, but the
draw_point
option 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); # true
If you're wondering that
line_width
used 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.$gc
is aGtk2::Gdk::GC
for 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
$linedrawer
back 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.
end
is called automatically when$linedrawer
is destroyed, which means that if you've got a complicated loop generating the points then you can justreturn
or 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.