File Coverage

blib/lib/Tk/Canvas/Draw.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1              
2             package Tk::Canvas::Draw;
3              
4             our $VERSION = '0.05';
5              
6             =head1 NAME
7              
8             Tk::Canvas::Draw - Simplifies drawing with a mouse in a perl/Tk Canvas
9              
10             =head1 DESCRIPTION
11              
12             This module simplifies the drawing of perl/Tk shapes in a Canvas, using
13             a mouse. Once the first event is detected for the given mouse
14             button N, and in the specified Canvas, the Motion and ButtonRelease events
15             are bound to the same mouse button and Canvas. All subsequent points are
16             captured until the final ButtonRelease event occurs. Finally, any previous
17             set bindings for the Canvas and mouse button are reinstated, and the
18             registered callback is invoked to handle any necessary final processing.
19              
20             =head1 VERSION
21              
22             Version 0.05
23              
24             =head1 SYNOPSIS
25              
26             use Tk::Canvas::Draw;
27              
28             Tk::Canvas::Draw->new($canvas, \&final_callback, $h_args);
29              
30             =head1 REQUIRED PARAMETERS
31              
32             =over 4
33              
34             =item $canvas
35              
36             The Tk::Canvas object where the mouse events will be captured.
37              
38             =item \&final_callback
39              
40             A callback to invoked when the event occurs. The argument
41             is required, but may be a non-blank string (eg. 'none') if the user is
42             certain that no final processing is necessary.
43             (See the section FINAL CALLBACK below)
44              
45             =item $h_args
46              
47             An optional reference to a hash containing any of the following arguments:
48              
49             =over 4
50              
51             =item 'style'
52              
53             The style of drawing to be done. Must be one of:
54              
55             =over 4
56              
57             =item 'none'
58              
59             Does not draw anything, just collects the (x,y) points generated by
60             moving the mouse over the canvas.
61              
62             =item 'free'
63              
64             Joins all points drawn to create freehand lines (this is the default).
65              
66             =item 'line'
67              
68             Joins the first point with the most recent point, to create a straight line.
69              
70             =item 'oval'
71              
72             Joins the first point with the most recent point to create an oval.
73              
74             =item 'circle'
75              
76             Joins the first point with the most recent point to create a circle.
77              
78             =item 'rectangle'
79              
80             Joins the first point with the most recent point to create a rectangle.
81              
82             =item 'mouse'
83              
84             The mouse button to bind the drawing to; one of {'1', '2' or '3'}.
85             The default is '1'.
86              
87             =back
88              
89             =item 'color'
90              
91             The color of the object being drawn. (Do not confuse this with the
92             'fill' argument). The default color is 'black'.
93              
94             =item 'fill'
95              
96             The color with which to fill the drawn shape (does not apply to styles
97             'free' or 'line'). The default fill is '0' (ie. no fill).
98              
99             =item 'width'
100              
101             The width of the shape being draw. In the case of lines (style 'free'
102             or 'line'), this referes to the line width; in all other shapes it is
103             the width of the shape's outline. The default width is '1'.
104              
105             =item 'action'
106              
107             A callback to invoke each time a new point is detected. It will be passed
108             a reference to an array containing the most recent (x, y) point detected,
109             eg. [ 123, 45 ].
110              
111             =back
112              
113             =back
114              
115             =head1 FINAL CALLBACK
116              
117             The final callback parameter names a subroutine to be invoked
118             when the mouse button is released. This subroutine is passed the
119             following 3 arguments:
120              
121             =over 4
122              
123             $o_obj -- The Tk::Canvas::Draw object
124              
125             $a_points -- A reference to an array containing the captured coordinate
126             points, each of which is an array reference in the form [ x, y ]
127              
128             $a_ids -- A reference to an array containing the ID(s) of the drawn shape
129              
130             =back
131              
132             =head1 METHODS
133              
134             restart($obj, $h_args)
135              
136             =over 4
137              
138             Lets the user reuse the Tk::Canvas::Draw object, optionally resetting any
139             of the same arguments as allowed to the new() method. This method takes
140             the following 2 arguments:
141              
142             =over 4
143              
144             $obj
145              
146             =over 4
147              
148             The Tk::Canvas::Draw object
149              
150             =back
151              
152             $h_args
153              
154             =over 4
155              
156             An optional hash, with the same values as allowed in the I
157             constructor. (See the I<$h_args> parameter in the REQUIRED PARAMETERS
158             section above)
159              
160             =back
161              
162             =back
163              
164             =back
165              
166             transform($obj, $a_points, $xoff, $yoff, $canvas)
167              
168             =over 4
169              
170             Allows the recreation of the shape given by the points in $a_points to
171             an alternate location in the canvas (or in a separate canvas), and returns
172             the ID(s) associated with the new shape. The following arguments are
173             required:
174              
175             =over 4
176              
177             $obj
178              
179             =over 4
180              
181             The Tk::Canvas::Draw object. The following accessor methods allow retrieval
182             of the corresponding member data:
183              
184             =over 4
185              
186             $obj->canvas
187             $obj->mouse
188             $obj->color
189             $obj->fill
190             $obj->width
191             $obj->style
192              
193             =back
194              
195             =back
196              
197             $a_points
198              
199             =over 4
200              
201             A reference to an array containing the (x, y) points generated by an
202             initial call to Tk::Canvas::Draw::new. For example:
203              
204             [ [10, 25], [12, 27], [13, 29], ... ]
205              
206             =back
207              
208             $xoff
209              
210             =over 4
211              
212             The x-offset by which to vary the new shape from the original
213              
214             =back
215              
216             $yoff
217              
218             =over 4
219              
220             The y-offset by which to vary the new shape from the original
221              
222             =back
223              
224             $canvas
225              
226             =over 4
227              
228             An optional Canvas on which to draw the new shape (it defaults to the
229             current Canvas used by $obj)
230              
231             =back
232              
233             =back
234              
235             =back
236              
237             =head1 EXAMPLE 1
238              
239             #!/usr/bin/perl -w
240             #
241             # Here's a quick example to stimulate your immediate excitement.
242             # The following program 'doodle' lets you draw colorful, freehand lines
243             # in a Tk Canvas!
244             ##
245              
246             use strict;
247             use warnings;
248             use Tk;
249             use Tk::Canvas::Draw;
250              
251             my $help = qq[
252             Click and move the mouse anywhere in the white box to begin drawing.
253             Type 'Escape' to clear everything.
254             ];
255              
256             my $a_all_ids = [ ];
257              
258             my $mw = new MainWindow(-title => 'Doodle -- Tk::Canvas::Draw example');
259             my $cv = $mw->Canvas(-bg => 'white', -width => 512, -height => 512)->pack;
260             $cv->createText(0, 0, -anch => 'nw', -text => $help);
261             Tk::Canvas::Draw->new($cv, \&done_drawing, { width => 5 });
262             $mw->bind("" => sub { map { $cv->delete($_) } @$a_all_ids });
263             MainLoop;
264              
265             # Tk::Canvas::Draw callback -- reinstall callback with a new, random color
266             sub done_drawing {
267             my ($o_obj, $a_points, $a_ids) = @_;
268             push @$a_all_ids, @$a_ids;
269             my $color = sprintf "#%02x%02x%02x", rand(256), rand(256), rand(256);
270             $o_obj->restart( { color => $color });
271             }
272              
273              
274             =head1 EXAMPLE 2
275              
276             #!/usr/bin/perl -w
277             #
278             # Another very simple example of Tk::Canvas::Draw, this time using
279             # each of the various allowable styles.
280             ##
281              
282             use strict;
283             use warnings;
284             use Tk;
285             use Tk::Canvas::Draw;
286              
287             my $a_style = [qw[ free line oval circle rectangle ]];
288             my $a_color = [qw[ black red blue purple orange ]];
289             my $stylenum = 0;
290             my $colornum = 0;
291              
292             my $mw = new MainWindow(-title => 'Tk::Canvas::Draw example');
293             my $cv = $mw->Canvas(-width => 512, -height => 512)->pack;
294             Tk::Canvas::Draw->new($cv, \&done, { width => 5, fill => 'white' });
295             MainLoop;
296              
297             # Tk::Canvas::Draw final callback - change style, reinstall callback
298             sub done {
299             my ($o_obj, $a_points, $a_ids) = @_;
300             my $style = $a_style->[++$stylenum % @$a_style];
301             my $color = $a_color->[++$colornum % @$a_color];
302             $o_obj->restart( { style => $style, color => $color });
303             }
304              
305              
306             =head1 EXAMPLE 3
307              
308             #!/usr/bin/perl -w
309             #
310             # A more complicated example of Tk::Canvas::Draw, this program gives
311             # the user more flexibility in choosing options to the constructor
312             # (although the color and width are randomized). It also demonstrates
313             # how to use the -action => \&callback argument, to track points while
314             # they are drawn, as well as showing the transform() method which can
315             # be used to make copies of the drawn object.
316             ##
317              
318             use strict;
319             use warnings;
320             use Tk;
321             use Tk::Canvas::Draw;
322              
323             #############
324             ## Globals ##
325             #############
326             my $a_styles = [qw[ free line oval circle rectangle ]];
327             my $a_font = [qw[ tahoma 12 ]];
328             my @all_id1 = ( );
329             my @all_id2 = ( );
330             my $b_fill = 0;
331             my $lastxy = "";
332             my $style;
333              
334             ##################
335             ## Main program ##
336             ##################
337             my $mw = new MainWindow(-title => 'Tk::Canvas::Draw example');
338             my $f1 = $mw->Frame()->pack(-fill => 'x');
339             my $f2 = $mw->Frame()->pack(-fill => 'both');
340             my $c1 = $f2->Canvas(-wi => 512,-he => 512, -bg => 'white');
341             my $c2 = $f2->Canvas(-wi => 512,-he => 512, -bg => '#ffffdf');
342             $c1->pack($c2, -side => 'left');
343              
344             button($f1, '>Quit (^Q)', sub { exit }, 'Control-q');
345             button($f1, '
346             button($f1, '
347              
348             choose_style($f1);
349             choose_fill($f1);
350             last_point($f1);
351             start_drawing($c1);
352             MainLoop;
353              
354             #################
355             ## Subroutines ##
356             #################
357             sub button {
358             my ($w, $text, $c_cmd, $bind) = @_;
359             my $side = ($text =~ s/^([<>])//)? $1: '<';
360             my $bt = $w->Button(-bg => '#ffafef', -text => $text);
361             $bt->configure(-comm => $c_cmd, -font => $a_font);
362             if ($bind || 0) {
363             $w->toplevel->bind("<$bind>" => sub { $bt->invoke });
364             }
365             $bt->pack(-side => ($side eq '<')? 'left': 'right');
366             }
367              
368             sub random_color {
369             sprintf "#%02x%02x%02x", rand(256), rand(256), rand(256);
370             }
371              
372             sub clear_last {
373             my $a_id1 = pop @all_id1;
374             my $a_id2 = pop @all_id2;
375             map { $c1->delete($_) } @$a_id1;
376             map { $c2->delete($_) } @$a_id2;
377             }
378              
379             sub clear_all {
380             while (@all_id1 > 0) {
381             clear_last();
382             }
383             }
384              
385             sub labeled_frame {
386             my ($w, $text) = @_;
387             my $fr = $w->Frame(-relief => 'ridge', -borderwidth => 4);
388             my $lb = $fr->Label(-text => $text, -font => $a_font);
389             $fr->pack(-side => 'left');
390             $lb->pack(-side => 'left');
391             return $fr;
392             }
393              
394             sub choose_style {
395             my ($w) = @_;
396             my $fr = labeled_frame($w, "Style");
397             my @args = (
398             -bg => '#7fcfff',
399             -variable => \$style,
400             -command => \&start_drawing,
401             -font => $a_font,
402             );
403             my $opt = $fr->Optionmenu(@args);
404             map { $opt->addOptions($_) } @$a_styles;
405             $style = 'free';
406             $opt->pack(-side => 'left');
407             }
408              
409             sub choose_fill {
410             my ($w) = @_;
411             my $fr = labeled_frame($w, "Fill Shapes");
412             my $a_comm = [
413             -font => $a_font,
414             -variable => \$b_fill,
415             -command => \&start_drawing,
416             ];
417             my $a_no = [ -text => "No", -value => 0 ];
418             my $a_yes = [ -text => "Yes", -value => 1 ];
419             my $r_no = $fr->Radiobutton(@$a_no, @$a_comm);
420             my $r_yes = $fr->Radiobutton(@$a_yes, @$a_comm);
421             $r_no->pack($r_yes, -side => 'left');
422             }
423              
424             sub last_point {
425             my ($w) = @_;
426             my $fr = labeled_frame($w, "Last Point");
427             my $lbl = $fr->Label(-textvar => \$lastxy, -font => $a_font);
428             $lbl->pack(-side => 'left');
429             }
430              
431             #==============================#
432             ## Tk::Canvas::Draw interface ##
433             #==============================#
434             sub start_drawing {
435             my $width = int(1 + rand(32));
436             my $color = random_color();
437             my $fill = $b_fill? random_color: 0;
438              
439             my $h_opts = {
440             'width' => $width,
441             'color' => $color,
442             'fill' => $fill,
443             'style' => $style,
444             'action' => \&show_last,
445             };
446              
447             new Tk::Canvas::Draw($c1, \&done_drawing, $h_opts);
448             }
449              
450             sub show_last {
451             my ($a_point) = @_;
452             my ($x, $y) = @$a_point;
453             $lastxy = sprintf "($x, $y)";
454             }
455              
456             sub done_drawing {
457             my ($o_obj, $a_pts, $a_ids) = @_;
458             push @all_id1, $a_ids;
459             push @all_id2, Tk::Canvas::Draw::transform($o_obj, $a_pts, 0, 0, $c2);
460             start_drawing();
461             }
462            
463              
464             =head1 AUTHOR
465              
466             John C. Norton
467              
468             =head1 COPYRIGHT & LICENSE
469              
470             Copyright 2009-2010 John C. Norton.
471              
472             This program is free software; you can redistribute it and/or modify it
473             under the terms of either: the GNU General Public License as published
474             by the Free Software Foundation; or the Artistic License.
475              
476             See http://dev.perl.org/licenses/ for more information.
477              
478             =cut
479              
480             ###############
481             ## Libraries ##
482             ###############
483 1     1   27179 use strict;
  1         2  
  1         35  
484 1     1   5 use warnings;
  1         1  
  1         28  
485 1     1   5 use Carp;
  1         6  
  1         71  
486 1     1   419 use Tk;
  0            
  0            
487              
488              
489             #############
490             ## Globals ##
491             #############
492             my $b_drawing = 0; # Disallow multiple simultaneous invocations
493              
494             my $h_defaults = {
495             'mouse' => 1,
496             'color' => 'black',
497             'fill' => 0,
498             'width' => 1,
499             'action' => 0,
500             'style' => 'free',
501             };
502              
503             my $h_aliases = {
504             'a' => 'action',
505             'c' => 'color',
506             'f' => 'fill',
507             'm' => 'mouse',
508             'w' => 'width',
509             's' => 'style',
510             };
511              
512             my $h_styles = {
513             'none' => 'Do not draw anything, just collect points',
514             'free' => 'Join all points to create freehand lines (default)',
515             'line' => 'Join first/last points to create straight lines',
516             'oval' => 'Join first/last points to create ovals',
517             'circle' => 'Join first/last points to create circles',
518             'rectangle' => 'Join first/last points to create rectangles',
519             };
520              
521              
522             ###############
523             ## Libraries ##
524             ###############
525             sub new {
526             my ($proto, $canvas, $c_result, $h_args) = @_;
527              
528             my $self = { 'points' => [ ], 'ids' => [ ], 'bindings' => { } };
529             bless $self, $proto;
530              
531             $self->assign_args($h_args);
532              
533             ($canvas || 0) or $self->fatal("Missing Canvas argument (arg \$1)");
534             ($c_result || 0) or $self->fatal("Missing result callback (arg \$2)");
535              
536             $self->{'canvas'} = $canvas;
537             $self->{'result'} = $c_result;
538              
539             my $mouse = $self->{'mouse'};
540             my $event = $self->{'start_event'};
541             my $a_draw = [ sub { $self->start_drawing(@_) }, Ev('x'), Ev('y') ];
542             $self->new_binding($canvas, $event, $a_draw);
543              
544             return $self;
545             }
546              
547              
548             ###############
549             ## Accessors ##
550             ###############
551             sub canvas { my ($self) = @_; $self->{'canvas'} }
552             sub mouse { my ($self) = @_; $self->{'mouse'} }
553             sub color { my ($self) = @_; $self->{'color'} }
554             sub fill { my ($self) = @_; $self->{'fill'} }
555             sub width { my ($self) = @_; $self->{'width'} }
556             sub style { my ($self) = @_; $self->{'style'} }
557              
558              
559             ##########################
560             ## User-visible Methods ##
561             ##########################
562             sub restart {
563             my ($self, $h_args) = @_;
564             $self->assign_args($h_args);
565             my $canvas = $self->{'canvas'};
566             my $event = $self->{'start_event'};
567             my $a_draw = [ sub { $self->start_drawing(@_) }, Ev('x'), Ev('y') ];
568             $self->new_binding($canvas, $event, $a_draw);
569             }
570              
571              
572             sub transform {
573             my ($self, $a_points, $xoff, $yoff, $canvas) = @_;
574             my $a_ids = [ ];
575             my ($x0, $y0);
576              
577             $canvas ||= $self->canvas;
578             my $color = $self->color;
579             my $fill = $self->fill;
580             my $width = $self->width;
581             my $style = $self->style;
582              
583             for (my $i = 0; $i < @$a_points; $i++) {
584             my $a_point = $a_points->[$i];
585             my ($x1, $y1) = ( $a_point->[0] + $xoff, $a_point->[1] + $yoff );
586              
587             if ($i > 0) {
588             my @args = ($x0, $y0, $x1, $y1);
589             if ($style eq 'free' or $style eq 'line') {
590             push @args, -width => $width;
591             $color and push @args, -fill => $color;
592             push @$a_ids, $canvas->createLine(@args);
593             } elsif ($style eq 'oval') {
594             push @args, -width => $width, -outline => $color;
595             $fill and push @args, -fill => $fill;
596             push @$a_ids, $canvas->createOval(@args);
597             } elsif ($style eq 'rectangle') {
598             push @args, -width => $width, -outline => $color;
599             $fill and push @args, -fill => $fill;
600             push @$a_ids, $canvas->createRectangle(@args);
601             } elsif ($style eq 'circle') {
602             my $rad = sqrt(($x1 - $x0) ** 2 + ($y1 - $y0) ** 2);
603             @args = ($x0 - $rad, $y0 - $rad, $x0 + $rad, $y0 + $rad);
604             push @args, -width => $width, -outline => $color;
605             $fill and push @args, -fill => $fill;
606             push @$a_ids, $canvas->createOval(@args);
607             }
608             }
609              
610             ($x0, $y0) = ($x1, $y1);
611             }
612              
613             return $a_ids;
614             }
615              
616              
617             ######################
618             ## Internal Methods ##
619             ######################
620             sub fatal {
621             my ($self, $errmsg) = @_;
622             carp "$errmsg\n";
623             exit;
624             }
625              
626              
627             sub new_binding {
628             my ($self, $canvas, $binding, $a_args) = @_;
629             my $c_prev = $canvas->Tk::bind($binding);
630             $self->{'bindings'}->{$binding} = $c_prev;
631             $canvas->Tk::bind($binding => $a_args);
632             }
633              
634              
635             sub restore_bindings {
636             my ($self, $canvas, $binding) = @_;
637             my $c_prev = delete $self->{'bindings'}->{$binding};
638             $canvas->Tk::bind($binding => $c_prev);
639             }
640              
641              
642             sub styles {
643             print "[Available Tk::Canvas::Draw styles]\n";
644             foreach my $key (sort keys %$h_styles) {
645             my $desc = $h_styles->{$key};
646             printf "%10.10s .... %s\n", $key, $desc;
647             }
648             }
649              
650              
651             sub assign_args {
652             my ($self, $h_args) = @_;
653              
654             # Make a copy of user-supplied args, resolving aliases
655             $h_args ||= { };
656             my $h_copy = { };
657             foreach my $k (keys %$h_args) {
658             my $key = $h_aliases->{$k} || $k;
659             $h_copy->{$key} = $h_args->{$k};
660             }
661              
662             # Resolve all arguments
663             foreach my $key (keys %$h_defaults) {
664             my $val = delete $h_copy->{$key};
665             if (defined($val)) {
666             $self->{$key} = $val;
667             } elsif (!defined($self->{$key})) {
668             $self->{$key} = $h_defaults->{$key};
669             }
670             }
671              
672             # Give an error if any arguments were invalid
673             my @leftover = keys %$h_copy;
674             if (@leftover > 0) {
675             my $s = (1 == @leftover)? "": "s";
676             my $errstr = "Unknown Tk::Canvas::Draw arg$s: ";
677             for (my $i = 0; $i < @leftover; $i++) {
678             my $arg = $leftover[$i];
679             $errstr .= ($i > 0)? ", '$arg'": "'$arg'";
680             }
681             $self->fatal($errstr);
682             }
683              
684             # Determine action when mouse button is clicked/moving/released
685             my $style = $self->{'style'};
686             if (!exists($h_styles->{$style})) {
687             my $styles = join(', ', keys %$h_styles);
688             $self->fatal("Unknown style '$style' (must be one of {$styles})");
689             }
690              
691             # Validate the mouse button
692             my $mouse = $self->{'mouse'};
693             if ($mouse !~ /^[123]$/) {
694             $self->fatal("Unknown mouse '$mouse' (must be one of {1, 2, 3})");
695             }
696              
697             # Assign events
698             $self->{'start_event'} = "";
699             $self->{'stop_event'} = "";
700             }
701              
702              
703             sub start_drawing {
704             my ($self, $canvas, $x, $y) = @_;
705              
706             $b_drawing++ and return;
707              
708             push @{$self->{'points'}}, [ $self->{'x'} = $x, $self->{'y'} = $y ];
709              
710             my $a_move = [ sub { $self->keep_drawing(@_) }, Ev('x'), Ev('y') ];
711             $self->new_binding($canvas, '', $a_move);
712              
713             my $a_stop = [ sub { $self->stop_drawing(@_) }, Ev('x'), Ev('y') ];
714             $self->new_binding($canvas, $self->{'stop_event'}, $a_stop);
715              
716             my $c_action = $self->{'action'};
717             ($c_action || 0) and $c_action->([ $x, $y ]);
718             }
719              
720              
721             sub keep_drawing {
722             my ($self, $canvas, $x1, $y1) = @_;
723              
724             my ($x0, $y0) = ($self->{'x'}, $self->{'y'});
725              
726             my $style = $self->{'style'};
727             if ($style ne 'none') {
728             my $method = "style_$style";
729             $self->$method($canvas, [ $x0, $y0, $x1, $y1 ]);
730             }
731              
732             my $c_action = $self->{'action'};
733             ($c_action || 0) and $c_action->([ $x1, $y1 ]);
734             }
735              
736              
737             sub stop_drawing {
738             my ($self, $canvas, $x, $y) = @_;
739              
740             my $h_bindings = $self->{'bindings'};
741             foreach my $binding (keys %$h_bindings) {
742             $self->restore_bindings($canvas, $binding);
743             }
744              
745             my $c_result = $self->{'result'};
746             my $a_points = $self->{'points'};
747             my $a_ids = $self->{'ids'};
748              
749             $self->{'points'} = [ ];
750             $self->{'ids'} = [ ];
751              
752             if (ref $c_result eq 'CODE') {
753             $c_result->($self, $a_points, $a_ids, $canvas);
754             }
755             $b_drawing = 0;
756             }
757              
758              
759             #==========#
760             ## Styles ##
761             #==========#
762             sub style_free {
763             my ($self, $canvas, $a_points) = @_;
764              
765             my ($x0, $y0, $x1, $y1) = @$a_points;
766              
767             my $width = $self->{'width'};
768             my $color = $self->{'color'};
769             my $a_ids = $self->{'ids'};
770              
771             my @args = ( -width => $width, -fill => $color );
772             push @$a_ids, $canvas->createLine($x0, $y0, $x1, $y1, @args);
773             push @{$self->{'points'}}, [ $self->{'x'} = $x1, $self->{'y'} = $y1 ];
774             }
775              
776              
777             sub style_line {
778             my ($self, $canvas, $a_points) = @_;
779              
780             my ($x1, $y1) = ($a_points->[-2], $a_points->[-1]);
781             my ($x0, $y0) = @{$self->{'points'}->[0]};
782              
783             my $width = $self->{'width'};
784             my $color = $self->{'color'};
785             my $a_ids = $self->{'ids'};
786              
787             if (@$a_ids > 0) {
788             pop @{$self->{'points'}};
789             my $id = pop @{$self->{'ids'}};
790             $canvas->delete($id);
791             }
792              
793             my @args = ( -width => $width, -fill => $color );
794             push @$a_ids, $canvas->createLine($x0, $y0, $x1, $y1, @args);
795             push @{$self->{'points'}}, [ $self->{'x'} = $x1, $self->{'y'} = $y1 ];
796             }
797              
798              
799             sub style_oval {
800             my ($self, $canvas, $a_points) = @_;
801              
802             my ($x1, $y1) = ($a_points->[-2], $a_points->[-1]);
803             my ($x0, $y0) = @{$self->{'points'}->[0]};
804              
805             my $width = $self->{'width'};
806             my $color = $self->{'color'};
807             my $fill = $self->{'fill'};
808             my $a_ids = $self->{'ids'};
809              
810             if (@$a_ids > 0) {
811             pop @{$self->{'points'}};
812             my $id = pop @{$self->{'ids'}};
813             $canvas->delete($id);
814             }
815              
816             my @args = ( -width => $width, -outline => $color );
817             $fill and push @args, -fill => $fill;
818             push @$a_ids, $canvas->createOval($x0, $y0, $x1, $y1, @args);
819             push @{$self->{'points'}}, [ $self->{'x'} = $x1, $self->{'y'} = $y1 ];
820             }
821              
822              
823             sub style_rectangle {
824             my ($self, $canvas, $a_points) = @_;
825              
826             my ($x1, $y1) = ($a_points->[-2], $a_points->[-1]);
827             my ($x0, $y0) = @{$self->{'points'}->[0]};
828              
829             my $width = $self->{'width'};
830             my $color = $self->{'color'};
831             my $fill = $self->{'fill'};
832             my $a_ids = $self->{'ids'};
833              
834             if (@$a_ids > 0) {
835             pop @{$self->{'points'}};
836             my $id = pop @{$self->{'ids'}};
837             $canvas->delete($id);
838             }
839              
840             my @args = ( -width => $width, -outline => $color );
841             $fill and push @args, -fill => $fill;
842             push @$a_ids, $canvas->createRectangle($x0, $y0, $x1, $y1, @args);
843             push @{$self->{'points'}}, [ $self->{'x'} = $x1, $self->{'y'} = $y1 ];
844             }
845              
846              
847             sub style_circle {
848             my ($self, $canvas, $a_points) = @_;
849              
850             my ($x1, $y1) = ($a_points->[-2], $a_points->[-1]);
851             my $a_center = $self->{'points'}->[0];
852             my ($x0, $y0) = @$a_center;
853             my $rad = sqrt(($x1 - $x0) ** 2 + ($y1 - $y0) ** 2);
854              
855             # Create the box surrounding the larger circle
856             my $a_corner1 = [ $x0 - $rad, $y0 - $rad ];
857             my $a_corner2 = [ $x0 + $rad, $y0 + $rad ];
858              
859             my $width = $self->{'width'};
860             my $color = $self->{'color'};
861             my $fill = $self->{'fill'};
862             my $a_ids = $self->{'ids'};
863              
864             if (@$a_ids > 0) {
865             pop @{$self->{'points'}};
866             my $id = pop @{$self->{'ids'}};
867             $canvas->delete($id);
868             }
869              
870             my @args = ( -width => $width, -outline => $color );
871             $fill and push @args, -fill => $fill;
872             push @$a_ids, $canvas->createOval(@$a_corner1, @$a_corner2, @args);
873             push @{$self->{'points'}}, [ $self->{'x'} = $x1, $self->{'y'} = $y1 ];
874             }
875              
876              
877             1;
878              
879              
880