File Coverage

blib/lib/Wx/SimplePlotter.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             package Wx::SimplePlotter;
2              
3 1     1   25581 use 5.006;
  1         3  
  1         38  
4 1     1   6 use strict;
  1         1  
  1         32  
5 1     1   5 use warnings;
  1         6  
  1         40  
6              
7 1     1   497 use Wx qw(wxSOLID);
  0            
  0            
8             use Wx::Event qw(EVT_PAINT EVT_SIZE);
9              
10             our @ISA = qw(Wx::Control);
11              
12             our $VERSION = '0.03';
13              
14             our $Default_Colours = [[0, 0, 0], [255, 0, 0], [0, 0, 255], [0, 255, 0]];
15              
16             =head1 NAME
17              
18             Wx::SimplePlotter - Simple Plotter Control
19              
20             =head1 SYNOPSIS
21              
22             use Wx::SimplePlotter;
23              
24             # init app, window etc.
25              
26             my $plotter = Wx::SimplePlotter->new();
27            
28             $plotter->SetColours([0, 0, 0], [255, 0, 0], [0, 0, 255], [0, 255, 0]);
29             $plotter->SetPoints(...)
30            
31             # plot ten sinus functions on a logarithmic x scale
32             my (@a);
33             for (1..1000) {
34             for my $i (0..9) {
35             push @{$a[$i]}, [log($_), sin($_ / 10) * $i];
36             }
37             }
38             $plotter->SetPoints(@a);
39            
40             # add plotter control to sizer, start app etc.
41              
42             =head1 DESCRIPTION
43              
44             This wxWidgets control plots points (e.g. function results) with different
45             colors. It automatically scales its output according to the control size.
46              
47             The points are passed as pairs of coordinates. Points are connected by lines
48             when drawn.
49              
50             The control has been tested on Mac OS X, but should work on other operating
51             systems (including Windows and Linux) as well.
52              
53             =head1 METHODS
54              
55             =over 4
56              
57             =item C
58              
59             Passes its arguments to the constructor of wxControl.
60              
61             =cut
62              
63             sub new {
64             my $class = shift;
65             my $self = $class->SUPER::new(@_);
66            
67             $self->{POINTS} = [];
68             $self->{SCALED} = [];
69             $self->{PEN} = Wx::Pen->new(Wx::Colour->new(0, 0, 0), 1, wxSOLID);
70             $self->{COLOURS} = $Default_Colours;
71            
72             EVT_PAINT($self, \&OnPaint);
73             EVT_SIZE($self, \&OnSize);
74            
75             return $self;
76             }
77              
78             =item C
79              
80             Sets the point data. The array referenced by each parameter contains pairs
81             of coordinates (x, y).
82              
83             =cut
84              
85             sub SetPoints {
86             my $self = shift;
87              
88             $self->{POINTS} = [ @_ ];
89              
90             $self->_FindMinMax();
91             $self->ScalePoints();
92             }
93              
94             =item C
95              
96             Sets the colours used to draw the data arrays. Each colours is a reference to an
97             array that contains three colour values (red, green and blue). The default
98             colours are C< [[0, 0, 0], [255, 0, 0], [0, 0, 255], [0, 255, 0]]>.
99             Colours are cycled if you use more data sets than colours.
100              
101             =cut
102              
103             sub SetColours {
104             my $self = shift;
105            
106             if ($#_ > -1) {
107             $self->{COLOURS} = [ @_ ];
108             } else {
109             $self->{COLOURS} = $Default_Colours;
110             }
111             }
112              
113             =item C
114              
115             Scales the point data according to the control size. Is called automatically
116             whenever the control is resized and when C is called, so there should
117             be no need for you to call it directly.
118              
119             =cut
120              
121             sub ScalePoints {
122             my $self = shift;
123              
124             $self->{SCALED} = [];
125             return unless defined $self->{POINTS} && (ref($self->{POINTS}) eq 'ARRAY');
126              
127             my ($minX, $minY, $maxX, $maxY) =
128             ($self->{MINX}, $self->{MINY}, $self->{MAXX}, $self->{MAXY});
129              
130             return unless defined $minX && defined $minY &&
131             defined $maxX && defined $maxY;
132              
133             my $size = $self->GetClientSize();
134             my ($w, $h) = ($size->GetWidth() - 1, $size->GetHeight() - 1);
135              
136             my ($scaleX, $scaleY) = (1, 1);
137            
138             if ($maxX - $minX != 0) {
139             $scaleX = $w / ($maxX - $minX);
140             } else {
141             $scaleX = 1;
142             }
143             if ($maxY - $minY != 0) {
144             $scaleY = $h / ($maxY - $minY);
145             } else {
146             $scaleY = 1;
147             }
148            
149             foreach my $p (@{$self->{POINTS}}) {
150             push @{$self->{SCALED}},
151             [ map { Wx::Point->new(
152             ($_->[0] - $minX) * $scaleX,
153             $h - ($_->[1] - $minY) * $scaleY) } @$p ];
154             }
155            
156             return $self->{SCALED};
157             }
158              
159             # Search for minimum and maximum values in the data. Called automatically by
160             # SetPoints
161              
162             sub _FindMinMax {
163             my $self = shift;
164            
165             my ($minX, $minY, $maxX, $maxY) =
166             ($self->{MINX}, $self->{MINY}, $self->{MAXX}, $self->{MAXY}) =
167             (undef, undef, undef, undef);
168              
169             foreach my $p (@{$self->{POINTS}}) {
170             for (0..$#$p) {
171             $minX = $p->[$_]->[0] if (!defined $minX || $minX > $p->[$_]->[0]);
172             $maxX = $p->[$_]->[0] if (!defined $maxX || $maxX < $p->[$_]->[0]);
173             $minY = $p->[$_]->[1] if (!defined $minY || $minY > $p->[$_]->[1]);
174             $maxY = $p->[$_]->[1] if (!defined $maxY || $maxY < $p->[$_]->[1]);
175             }
176             }
177              
178             ($self->{MINX}, $self->{MINY}, $self->{MAXX}, $self->{MAXY}) =
179             ($minX, $minY, $maxX, $maxY);
180             }
181              
182             =item C
183              
184             Paint handler that draws the points.
185              
186             =cut
187              
188             sub OnPaint {
189             my ($self, $event) = @_;
190              
191             my $dc = Wx::PaintDC->new($self);
192            
193             return unless defined $self->{SCALED} && ref($self->{SCALED});
194              
195             my $colidx = 0;
196              
197             foreach (@{$self->{SCALED}}) {
198             $self->{PEN}->SetColour(@{$self->{COLOURS}->[$colidx]});
199             $dc->SetPen($self->{PEN});
200            
201             $dc->DrawLines($_);
202              
203             $colidx++;
204             $colidx = 0
205             if ($colidx > $#{$self->{COLOURS}});
206             }
207             }
208              
209             =item C
210              
211             Resizing handler that re-scales the points.
212              
213             =cut
214              
215             sub OnSize {
216             my $self = shift;
217            
218             $self->ScalePoints();
219             }
220              
221             1;
222             __END__