File Coverage

blib/lib/Text/NumericData/App/txdrecycle.pm
Criterion Covered Total %
statement 82 85 96.4
branch 21 32 65.6
condition 1 3 33.3
subroutine 10 10 100.0
pod 0 8 0.0
total 114 138 82.6


line stmt bran cond sub pod time code
1             package Text::NumericData::App::txdrecycle;
2              
3 1     1   71716 use Text::NumericData::App;
  1         3  
  1         29  
4              
5 1     1   6 use strict;
  1         2  
  1         726  
6              
7             # This is just a placeholder because of a past build system bug.
8             # The one and only version for Text::NumericData is kept in
9             # the Text::NumericData module itself.
10             our $VERSION = '1';
11             $VERSION = eval $VERSION;
12              
13             #the infostring says it all
14             my $infostring = 'Rearrange lines (records) in files in accordance to changing the viewport along a cyclic coordinate.
15              
16             This is experimental work, so no usage example yet. But one hint: I designed this one with moving the viewport of gnuplot plots of cyclic data. It assumest sorted data along the coordinate (ascending or descending). To make it work with 3D plots, it processes blocks of data (separated by blank line) as independent "scans" (for gnuplot pm3d mode, for example).';
17              
18             our @ISA = ('Text::NumericData::App');
19              
20             sub new
21             {
22 1     1 0 97 my $class = shift;
23 1         7 my @pars =
24             (
25             'column', 1, 'c',
26             'the column to use as coordiante'
27             , 'shift', 0.25, 's',
28             'shift the viewport by that value (nearest existing data point), direction is subject to misunderstandings'
29             );
30 1         22 return $class->SUPER::new
31             ({
32             parconf=>
33             {
34             info=>$infostring # default version,
35             # default author
36             # default copyright
37             }
38             ,pardef=>\@pars
39             ,filemode=>0
40             ,pipemode=>1
41             ,pipe_init=>\&preinit
42             ,pipe_begin=>\&begin
43             ,pipe_line=>\&line
44             ,pipe_end=>\&end
45             });
46             }
47              
48             sub preinit
49             {
50 1     1 0 4 my $self = shift;
51 1         5 my $param = $self->{param};
52              
53 1         4 $self->{col} = $param->{column}-1;
54 1 50       6 if($self->{col} < 0)
55             {
56 0         0 print STDERR "txdrecycle: Non-positive column does not work!\n";
57 0         0 return -1;
58             }
59              
60 1         5 return 0;
61             }
62              
63             sub line
64             {
65 841     841 0 26692 my $self = shift;
66              
67 841 100       2146 if(not $self->{data})
68             {
69             $self->{data} = 1
70 3 100       15 if $self->{txd}->line_check($_[0]);
71             }
72              
73 841 100       2141 if($self->{data})
74             {
75 839 100       3106 if($_[0] =~ /^\s*$/)
76             {
77 13         37 my $future = $_[0];
78 13         43 $_[0] = '';
79 13         81 $self->finish_block($_[0]);
80 13         52 $_[0] .= $future;
81 13         70 return;
82             }
83              
84 826         2748 my $d = $self->{txd}->line_data($_[0]);
85 826 50       2108 if(defined $d)
86             {
87 826         1428 push(@{$self->{block}}, $d);
  826         2177  
88             }
89 826         2159 $_[0] = '';
90             }
91             }
92              
93             sub begin
94             {
95 1     1 0 159 my $self = shift;
96              
97 1         13 $self->new_txd();
98 1         4 $self->{block} = [];
99 1         4 $self->{data} = 0;
100             }
101              
102             sub end
103             {
104 1     1 0 48 my $self = shift;
105              
106 1         14 $self->finish_block($_[0]);
107             }
108              
109             sub finish_block
110             {
111 14     14 0 36 my $self = shift;
112 14         63 $self->recycle_block();
113              
114 826         3124 $_[0] .= ${$self->{txd}->data_line($_)}
115 14         33 for (@{$self->{block}});
  14         55  
116              
117             # Think about caching periods 'n' stuff and check consistency.
118 14         412 $self->{block} = [];
119             }
120              
121             # The scheme:
122             # shift 'abcdea' by 3 letters -> 'deabcd'
123             sub recycle_block
124             {
125 14     14 0 40 my $self = shift;
126 14         49 my $param = $self->{param};
127              
128             # Nothing to do for such small data sets... cannot possibly make any sense.
129             # I require the end points being identical, so to have something, there must be some data in between.
130 14 50       29 return if @{$self->{block}} < 2;
  14         65  
131              
132             # I support ascending and descending data.
133 14         37 my $period = $self->{block}[$#{$self->{block}}][$self->{col}] - $self->{block}[0][$self->{col}];
  14         149  
134 14 50       63 my $dir = $period > 0 ? +1 : -1;
135 14         46 $period = abs($period);
136              
137             # Yeah, that check is not very floating-point safe.
138 14 50       55 return unless $period > 0;
139              
140             # Shift needs to be oriented according to sorting order.
141 14         60 my $shift=$param->{shift}*$dir;
142             # Shift withing one period.
143 14         68 $shift -= int($shift/$period)*$period;
144             # ... but still, positive!
145 14 50       50 $shift += $period if $shift < 0;
146             #print STDERR "real shift: $shift\n";
147              
148             # The point of split, beginning plus shift.
149 14         61 my $i = nearest_index($self->{block}[0][$self->{col}]+$dir*$shift, $dir, \@{$self->{block}});
  14         84  
150             #print STDERR "wrap point: $i\n";
151 14 50 33     81 return if ($i == 0 or $i == $#{$self->{block}}); # No need to split there.
  14         82  
152              
153 14         42 my @a = @{$self->{block}}; # Remember, just a bunch of references.
  14         158  
154             # Need a copy before messing with the references.
155 14         32 my @boundary = @{$a[$i]};
  14         68  
156              
157             # Start filling the recycled data.
158 14         122 @{$self->{block}} = (@a[$i..$#a-1]);
  14         86  
159             # The data got moved one pace back, adjust coordinate.
160 14         51 $_->[$self->{col}] -= $dir*$period for (@{$self->{block}});
  14         537  
161             # Shove in the unchanged remaining data, plus the new boundary.
162 14         39 push(@{$self->{block}}, @a[0 .. $i-1]);
  14         84  
163 14         35 push(@{$self->{block}}, \@boundary);
  14         51  
164             # Bring the coordinates back into a sane range.
165             # Am I sure that this is correct? Caring for $dir is tedious.
166             # Examples:
167 14 50       86 if($dir*$self->{block}[0][$self->{col}] < -$period)
168             {
169             $_->[$self->{col}] += $dir*$period
170 14         36 for (@{$self->{block}});
  14         440  
171             }
172             }
173              
174             sub nearest_index
175             {
176 14     14 0 60 my ($val, $dir, $arr) = (@_);
177 14 50       90 my $lower = $dir == +1 ? 0 : $#{$arr};
  0         0  
178 14 50       59 my $upper = $dir == +1 ? $#{$arr} : 0;
  14         37  
179              
180             # Hacking around to get a loop that runs both ways...
181 14         73 for(my $i=$lower+$dir; $i!=$upper+$dir; $i+=$dir)
182             {
183 154 100       568 if($arr->[$i][0] >= $val)
184             {
185 14         33 $upper = $i;
186 14         42 $lower = $i-$dir;
187 14         39 last;
188             }
189             }
190 14 50       76 return ($val - $arr->[$lower][0] < $arr->[$upper][0] - $val) ? $lower : $upper;
191             }