File Coverage

blib/lib/Text/NumericData/App/txdrecycle.pm
Criterion Covered Total %
statement 83 92 90.2
branch 22 36 61.1
condition 1 6 16.6
subroutine 10 10 100.0
pod 0 8 0.0
total 116 152 76.3


line stmt bran cond sub pod time code
1             package Text::NumericData::App::txdrecycle;
2              
3 1     1   71658 use Text::NumericData::App;
  1         3  
  1         34  
4              
5 1     1   7 use strict;
  1         4  
  1         1120  
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 96 my $class = shift;
23 1         8 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             , 'smother', 0, 'm',
30             'compute arithmetic mean of initial outer boundaries that are supposed to be identical, but are not'
31             , 'smother-except', [], 'x',
32             'list of columns to extempt from smothering (in addition to the coordinate column)'
33             );
34 1         18 return $class->SUPER::new
35             ({
36             parconf=>
37             {
38             info=>$infostring # default version,
39             # default author
40             # default copyright
41             }
42             ,pardef=>\@pars
43             ,filemode=>0
44             ,pipemode=>1
45             ,pipe_init=>\&preinit
46             ,pipe_begin=>\&begin
47             ,pipe_line=>\&line
48             ,pipe_end=>\&end
49             });
50             }
51              
52             sub preinit
53             {
54 1     1 0 5 my $self = shift;
55 1         3 my $param = $self->{param};
56              
57 1         6 $self->{col} = $param->{column}-1;
58 1 50       3 if($self->{col} < 0)
59             {
60 0         0 print STDERR "txdrecycle: Non-positive column does not work!\n";
61 0         0 return -1;
62             }
63              
64 1         3 return 0;
65             }
66              
67             sub line
68             {
69 841     841 0 20156 my $self = shift;
70              
71 841 100       1654 if(not $self->{data})
72             {
73             $self->{data} = 1
74 3 100       10 if $self->{txd}->line_check($_[0]);
75             }
76              
77 841 100       1595 if($self->{data})
78             {
79 839 100       2159 if($_[0] =~ /^\s*$/)
80             {
81 13         27 my $future = $_[0];
82 13         21 $_[0] = '';
83 13         35 $self->finish_block($_[0]);
84 13         26 $_[0] .= $future;
85 13         36 return;
86             }
87              
88 826         1902 my $d = $self->{txd}->line_data($_[0]);
89 826 50       1605 if(defined $d)
90             {
91 826         1114 push(@{$self->{block}}, $d);
  826         1610  
92             }
93 826         1615 $_[0] = '';
94             }
95             }
96              
97             sub begin
98             {
99 1     1 0 162 my $self = shift;
100              
101 1         41 $self->new_txd();
102 1         3 $self->{block} = [];
103 1         4 $self->{data} = 0;
104             }
105              
106             sub end
107             {
108 1     1 0 45 my $self = shift;
109              
110 1         6 $self->finish_block($_[0]);
111             }
112              
113             sub finish_block
114             {
115 14     14 0 23 my $self = shift;
116 14         37 $self->recycle_block();
117              
118 826         2383 $_[0] .= ${$self->{txd}->data_line($_)}
119 14         28 for (@{$self->{block}});
  14         29  
120              
121             # Think about caching periods 'n' stuff and check consistency.
122 14         205 $self->{block} = [];
123             }
124              
125             # The scheme:
126             # shift 'abcdea' by 3 letters -> 'deabcd'
127             sub recycle_block
128             {
129 14     14 0 20 my $self = shift;
130 14         26 my $param = $self->{param};
131              
132             # Nothing to do for such small data sets... cannot possibly make any sense.
133             # I require the end points being identical, so to have something, there must be some data in between.
134 14 50       20 return if @{$self->{block}} < 2;
  14         38  
135              
136             # I support ascending and descending data.
137 14         24 my $period = $self->{block}[$#{$self->{block}}][$self->{col}] - $self->{block}[0][$self->{col}];
  14         112  
138 14 50       41 my $dir = $period > 0 ? +1 : -1;
139 14         25 $period = abs($period);
140              
141             # Yeah, that check is not very floating-point safe.
142 14 50       31 return unless $period > 0;
143              
144             # Shift needs to be oriented according to sorting order.
145 14         26 my $shift=$param->{shift}*$dir;
146             # Shift withing one period.
147 14         30 $shift -= int($shift/$period)*$period;
148             # ... but still, positive!
149 14 50       29 $shift += $period if $shift < 0;
150             #print STDERR "real shift: $shift\n";
151              
152             # The point of split, beginning plus shift.
153 14         30 my $i = nearest_index($self->{block}[0][$self->{col}]+$dir*$shift, $dir, \@{$self->{block}});
  14         40  
154             #print STDERR "wrap point: $i\n";
155 14 50 33     39 return if ($i == 0 or $i == $#{$self->{block}}); # No need to split there.
  14         46  
156              
157 14         21 my @a = @{$self->{block}}; # Remember, just a bunch of references.
  14         107  
158             # Need a copy before messing with the references.
159 14         25 my @boundary = @{$a[$i]};
  14         50  
160              
161             # Start filling the recycled data.
162 14         59 @{$self->{block}} = (@a[$i..$#a-1]);
  14         120  
163             # The data got moved one pace back, adjust coordinate.
164 14         43 $_->[$self->{col}] -= $dir*$period for (@{$self->{block}});
  14         365  
165             # Shove in the unchanged remaining data, plus the new boundary.
166             # $a[0] is supposed to the identical to $a[$#a]. But in might not be so,
167             # for a DG model, for example. Let's smother it a bit.
168 14 50       44 if($param->{smother})
169             {
170 0         0 my @except = ($self->{col}, @{$param->{'smother-except'}});
  0         0  
171 0         0 for(my $i = 0; $i< @{$a[0]}; ++$i)
  0         0  
172             {
173             $a[0][$i] = 0.5*($a[0][$i] + $a[$#a][$i])
174 0 0 0     0 if(not grep { $_ == $i } @except and $a[0][$i] != $a[$#a][$i]);
  0         0  
175             }
176             }
177 14         20 push(@{$self->{block}}, @a[0 .. $i-1]);
  14         41  
178 14         26 push(@{$self->{block}}, \@boundary);
  14         25  
179             # Bring the coordinates back into a sane range.
180             # Am I sure that this is correct? Caring for $dir is tedious.
181             # Examples:
182 14 50       56 if($dir*$self->{block}[0][$self->{col}] < -$period)
183             {
184             $_->[$self->{col}] += $dir*$period
185 14         23 for (@{$self->{block}});
  14         318  
186             }
187             }
188              
189             sub nearest_index
190             {
191 14     14 0 37 my ($val, $dir, $arr) = (@_);
192 14 50       37 my $lower = $dir == +1 ? 0 : $#{$arr};
  0         0  
193 14 50       25 my $upper = $dir == +1 ? $#{$arr} : 0;
  14         25  
194              
195             # Hacking around to get a loop that runs both ways...
196 14         35 for(my $i=$lower+$dir; $i!=$upper+$dir; $i+=$dir)
197             {
198 154 100       384 if($arr->[$i][0] >= $val)
199             {
200 14         22 $upper = $i;
201 14         21 $lower = $i-$dir;
202 14         27 last;
203             }
204             }
205 14 50       43 return ($val - $arr->[$lower][0] < $arr->[$upper][0] - $val) ? $lower : $upper;
206             }