| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
# geometry of space: value range checks, normalisation and computing distance |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Graphics::Toolkit::Color::Space::Shape; |
|
5
|
50
|
|
|
50
|
|
269225
|
use v5.12; |
|
|
50
|
|
|
|
|
212
|
|
|
6
|
50
|
|
|
50
|
|
183
|
use warnings; |
|
|
50
|
|
|
|
|
66
|
|
|
|
50
|
|
|
|
|
2131
|
|
|
7
|
50
|
|
|
50
|
|
805
|
use Graphics::Toolkit::Color::Space::Basis; |
|
|
50
|
|
|
|
|
60
|
|
|
|
50
|
|
|
|
|
1298
|
|
|
8
|
50
|
|
|
50
|
|
172
|
use Graphics::Toolkit::Color::Space::Util qw/round_decimals is_nr/; |
|
|
50
|
|
|
|
|
67
|
|
|
|
50
|
|
|
|
|
156302
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#### constructor ####################################################### |
|
11
|
|
|
|
|
|
|
sub new { |
|
12
|
583
|
|
|
583
|
0
|
4518
|
my $pkg = shift; |
|
13
|
583
|
|
|
|
|
1497
|
my ($basis, $type, $range, $precision, $constraint) = @_; |
|
14
|
583
|
100
|
|
|
|
1201
|
return unless ref $basis eq 'Graphics::Toolkit::Color::Space::Basis'; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# expand axis type definition |
|
17
|
582
|
100
|
100
|
|
|
1403
|
if (not defined $type){ $type = [ (1) x $basis->axis_count ] } # set all axis as linear per default |
|
|
395
|
100
|
|
|
|
768
|
|
|
18
|
|
|
|
|
|
|
elsif (ref $type eq 'ARRAY' and @$type == $basis->axis_count ) { |
|
19
|
185
|
|
|
|
|
351
|
for my $i ($basis->axis_iterator) { |
|
20
|
553
|
|
|
|
|
580
|
my $atype = $type->[$i]; # type def of this axis |
|
21
|
553
|
50
|
|
|
|
683
|
return unless defined $atype; |
|
22
|
553
|
100
|
100
|
|
|
1734
|
if ($atype eq 'angular' or $atype eq 'circular' or $atype eq '0') { $type->[$i] = 0 } |
|
|
187
|
100
|
100
|
|
|
345
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
23
|
360
|
|
|
|
|
512
|
elsif ($atype eq 'linear' or $atype eq '1') { $type->[$i] = 1 } |
|
24
|
4
|
|
|
|
|
6
|
elsif ($atype eq 'no' or $atype eq '2') { $type->[$i] = 2 } |
|
25
|
2
|
|
|
|
|
13
|
else { return 'invalid axis type at element '.$i.'. It has to be "angular", "linear" or "no".' } |
|
26
|
|
|
|
|
|
|
} |
|
27
|
2
|
|
|
|
|
4
|
} else { return 'invalid axis type definition in color space '.$basis->space_name } |
|
28
|
|
|
|
|
|
|
|
|
29
|
578
|
|
|
|
|
1243
|
$range = expand_range_definition(undef, $basis, $range ); |
|
30
|
578
|
100
|
|
|
|
942
|
return $range unless ref $range; |
|
31
|
570
|
|
|
|
|
983
|
$precision = expand_precision_definition( $basis, $precision ); |
|
32
|
570
|
100
|
|
|
|
845
|
return $precision unless ref $precision; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# check constraint def |
|
35
|
568
|
100
|
|
|
|
772
|
if (defined $constraint){ |
|
36
|
74
|
100
|
100
|
|
|
257
|
return 'color space constraint definition has to be a none empty HASH ref' if ref $constraint ne 'HASH' or not %$constraint; |
|
37
|
72
|
|
|
|
|
152
|
for my $constraint_name (keys %$constraint){ |
|
38
|
73
|
|
|
|
|
106
|
my $properties = $constraint->{$constraint_name}; |
|
39
|
73
|
100
|
66
|
|
|
248
|
return 'a color space constraint has to be a HASH ref with three keys' unless ref $properties eq 'HASH' and keys(%$properties) == 3; |
|
40
|
71
|
|
|
|
|
201
|
$properties = {%$properties}; |
|
41
|
71
|
|
|
|
|
164
|
my $error_msg = 'constraint "$constraint_name" in '.$basis->space_name.' color space'; |
|
42
|
71
|
|
|
|
|
121
|
for (qw/checker error remedy/){ |
|
43
|
|
|
|
|
|
|
return $error_msg." needs the string-propertiy '$_'" |
|
44
|
207
|
100
|
33
|
|
|
752
|
unless exists $properties->{$_} and $properties->{$_} and not ref $properties->{$_}; |
|
|
|
|
66
|
|
|
|
|
|
45
|
|
|
|
|
|
|
} |
|
46
|
68
|
|
|
|
|
105
|
$properties->{'checker_code'} = $properties->{'checker'}; |
|
47
|
68
|
|
|
|
|
5724
|
$properties->{'checker'} = eval 'sub {'.$properties->{'checker_code'}.'}'; |
|
48
|
68
|
50
|
|
|
|
202
|
return 'checker code of '.$error_msg.":'$properties->{checker_code}' does not eval - $@" if $@; |
|
49
|
68
|
|
|
|
|
119
|
$properties->{'remedy_code'} = $properties->{'remedy'}; |
|
50
|
68
|
|
|
|
|
5275
|
$properties->{'remedy'} = eval 'sub {'.$properties->{'remedy_code'}.'}'; |
|
51
|
68
|
50
|
|
|
|
176
|
return 'remedy code of '.$error_msg.":'$properties->{remedy_code}' does not eval - $@" if $@; |
|
52
|
68
|
|
|
|
|
152
|
$constraint->{ $constraint_name } = $properties; |
|
53
|
|
|
|
|
|
|
} |
|
54
|
494
|
|
|
|
|
513
|
} else { $constraint = '' } |
|
55
|
|
|
|
|
|
|
|
|
56
|
561
|
|
|
|
|
2368
|
bless { basis => $basis, type => $type, range => $range, precision => $precision, constraint => $constraint } |
|
57
|
|
|
|
|
|
|
} |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
#### object attribute checker ########################################## |
|
60
|
|
|
|
|
|
|
sub expand_range_definition { # check if range def is valid and eval (expand) it |
|
61
|
2300
|
|
|
2300
|
0
|
3156
|
my ($self, $basis, $range) = @_; |
|
62
|
2300
|
50
|
|
|
|
3874
|
$basis = $basis->{'basis'} if ref $basis eq __PACKAGE__; |
|
63
|
2300
|
|
|
|
|
2459
|
my $error_msg = 'Bad value range definition!'; |
|
64
|
2300
|
100
|
100
|
|
|
5965
|
$range = 1 if not defined $range or $range eq 'normal'; |
|
65
|
2300
|
100
|
|
|
|
3701
|
$range = 100 if $range eq 'percent'; |
|
66
|
2300
|
50
|
66
|
|
|
6730
|
return $error_msg." It has to be 'normal', 'percent', a number or ARRAY of numbers (by axis position) or HASH (by axis name).'. |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
67
|
|
|
|
|
|
|
' Instead of a number you can also insert ARRAY with two number!" |
|
68
|
|
|
|
|
|
|
unless (not ref $range and is_nr( $range )) or ref $range eq 'ARRAY' or ref $range eq 'HASH'; |
|
69
|
2300
|
100
|
|
|
|
3728
|
if (ref $range eq 'HASH') { |
|
70
|
6
|
|
|
|
|
10
|
my $range_array = []; |
|
71
|
6
|
|
|
|
|
11
|
for my $axis_name (keys %$range){ |
|
72
|
13
|
50
|
|
|
|
21
|
next unless $basis->is_axis_name( $axis_name ); |
|
73
|
13
|
|
|
|
|
176
|
$range_array->[ $basis->pos_from_axis_name($axis_name) ] = $range->{$axis_name}; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
6
|
|
|
|
|
12
|
for my $axis_index ($basis->axis_iterator){ |
|
76
|
18
|
100
|
66
|
|
|
37
|
next if exists $range_array->[$axis_index] and defined $range_array->[$axis_index]; |
|
77
|
5
|
100
|
66
|
|
|
13
|
next unless ref $self and ref $self->{'range'}; |
|
78
|
2
|
|
|
|
|
3
|
$range_array->[$axis_index] = $self->{'range'}[$axis_index]; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
6
|
|
|
|
|
12
|
$range = $range_array; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
2300
|
100
|
|
|
|
3772
|
$range = [$range] unless ref $range; |
|
83
|
2300
|
100
|
|
|
|
4032
|
$range = [(@$range) x $basis->axis_count] if @$range == 1; |
|
84
|
2300
|
100
|
|
|
|
3703
|
return "Range definition needs inside an ARRAY or HASH a number or pair of them in an ARRAY for each axis!" |
|
85
|
|
|
|
|
|
|
if @$range != $basis->axis_count; |
|
86
|
2296
|
|
|
|
|
3385
|
for my $axis_index ($basis->axis_iterator) { |
|
87
|
6935
|
|
|
|
|
7146
|
my $axis_range = $range->[$axis_index]; |
|
88
|
6935
|
100
|
|
|
|
9165
|
if (not ref $axis_range){ |
|
|
|
50
|
|
|
|
|
|
|
89
|
3449
|
100
|
|
|
|
4929
|
if ($axis_range eq 'normal') {$range->[$axis_index] = [0, 1]} |
|
|
3
|
50
|
|
|
|
9
|
|
|
90
|
0
|
|
|
|
|
0
|
elsif ($axis_range eq 'percent') {$range->[$axis_index] = [0, 100]} |
|
91
|
3446
|
|
|
|
|
5604
|
else {$range->[$axis_index] = [0, $axis_range+0]} |
|
92
|
|
|
|
|
|
|
} elsif (ref $axis_range eq 'ARRAY') { |
|
93
|
3486
|
100
|
|
|
|
4654
|
return $error_msg.' Array at axis number '.$axis_index.' has to have two elements' unless @$axis_range == 2; |
|
94
|
3484
|
50
|
|
|
|
4505
|
return $error_msg.' None numeric value at lower bound for axis number '.$axis_index unless is_nr( $axis_range->[0] ); |
|
95
|
3484
|
100
|
|
|
|
4626
|
return $error_msg.' None numeric value at upper bound for axis number '.$axis_index unless is_nr( $axis_range->[1] ); |
|
96
|
3483
|
100
|
|
|
|
6037
|
return $error_msg.' Lower bound (first value) is >= than upper bound at axis number '.$axis_index if $axis_range->[0] >= $axis_range->[1]; |
|
97
|
0
|
|
|
|
|
0
|
} else { return "Range definitin for axis $axis_index was not an two element ARRAY!" } |
|
98
|
|
|
|
|
|
|
} |
|
99
|
2292
|
|
|
|
|
15843
|
return $range; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
sub try_check_range_definition { # check if range def is valid and eval (expand) it |
|
102
|
4561
|
|
|
4561
|
0
|
5633
|
my ($self, $range) = @_; |
|
103
|
4561
|
100
|
|
|
|
8060
|
return $self->{'range'} unless defined $range; |
|
104
|
1722
|
|
|
|
|
3182
|
return $self->expand_range_definition( $self->{'basis'}, $range ); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub expand_precision_definition { # check if precision def is valid and eval (exapand) it |
|
108
|
933
|
|
|
933
|
0
|
1324
|
my ($basis, $precision) = @_; |
|
109
|
933
|
50
|
|
|
|
1684
|
$basis = $basis->{'basis'} if ref $basis eq __PACKAGE__; |
|
110
|
933
|
100
|
|
|
|
1346
|
$precision = -1 unless defined $precision; |
|
111
|
933
|
100
|
|
|
|
1891
|
$precision = [($precision) x $basis->axis_count] unless ref $precision; |
|
112
|
933
|
50
|
|
|
|
1599
|
return 'need an ARRAY as definition of axis value precision' unless ref $precision eq 'ARRAY'; |
|
113
|
933
|
100
|
|
|
|
1384
|
return 'definition of axis value precision has to have same lengths as basis' unless @$precision == $basis->axis_count; |
|
114
|
931
|
|
|
|
|
1316
|
return $precision; |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
sub try_check_precision_definition { # check if range def is valid and eval (expand) it |
|
117
|
1533
|
|
|
1533
|
0
|
1925
|
my ($self, $precision) = @_; |
|
118
|
1533
|
100
|
|
|
|
2901
|
return $self->{'precision'} unless defined $precision; |
|
119
|
363
|
|
|
|
|
714
|
return expand_precision_definition( $self->{'basis'}, $precision ); |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
#### getter of space object ############################################ |
|
123
|
12391
|
|
|
12391
|
0
|
20858
|
sub basis { $_[0]{'basis'}} |
|
124
|
|
|
|
|
|
|
# per axis |
|
125
|
|
|
|
|
|
|
sub is_axis_numeric { |
|
126
|
13151
|
|
|
13151
|
0
|
13760
|
my ($self, $axis_nr) = @_; |
|
127
|
13151
|
100
|
66
|
|
|
27070
|
return 0 if not defined $axis_nr or not exists $self->{'type'}[$axis_nr]; |
|
128
|
13147
|
100
|
|
|
|
37009
|
$self->{'type'}[$axis_nr] < 2 ? 1 : 0; |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
sub is_axis_euclidean { |
|
132
|
2986
|
|
|
2986
|
0
|
3168
|
my ($self, $axis_nr) = @_; |
|
133
|
2986
|
100
|
66
|
|
|
6056
|
return 0 if not defined $axis_nr or not exists $self->{'type'}[$axis_nr]; |
|
134
|
2985
|
100
|
|
|
|
5312
|
$self->{'type'}[$axis_nr] == 1 ? 1 : 0; |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
sub is_axis_angular { |
|
138
|
3052
|
|
|
3052
|
0
|
3316
|
my ($self, $axis_nr) = @_; |
|
139
|
3052
|
100
|
66
|
|
|
6557
|
return 0 if not defined $axis_nr or not exists $self->{'type'}[$axis_nr]; |
|
140
|
3051
|
100
|
|
|
|
5762
|
$self->{'type'}[$axis_nr] == 0 ? 1 : 0; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
sub axis_value_max { # --> +value |
|
143
|
18
|
|
|
18
|
0
|
36
|
my ($self, $axis_nr, $range) = @_; |
|
144
|
18
|
|
|
|
|
41
|
$range = $self->try_check_range_definition( $range ); |
|
145
|
18
|
50
|
|
|
|
36
|
return undef unless ref $range; |
|
146
|
18
|
100
|
|
|
|
36
|
return undef unless $self->is_axis_numeric($axis_nr); |
|
147
|
17
|
|
|
|
|
136
|
return $range->[$axis_nr][1]; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
sub axis_value_min { # --> +value |
|
150
|
4
|
|
|
4
|
0
|
8
|
my ($self, $axis_nr, $range) = @_; |
|
151
|
4
|
|
|
|
|
8
|
$range = $self->try_check_range_definition( $range ); |
|
152
|
4
|
50
|
|
|
|
7
|
return undef unless ref $range; |
|
153
|
4
|
100
|
|
|
|
8
|
return undef unless $self->is_axis_numeric($axis_nr); |
|
154
|
3
|
|
|
|
|
11
|
return $range->[$axis_nr][0]; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
sub axis_value_precision { # --> +precision? |
|
157
|
16
|
|
|
16
|
0
|
1822
|
my ($self, $axis_nr, $precision) = @_; |
|
158
|
16
|
50
|
33
|
|
|
64
|
return undef if not defined $axis_nr or not exists $self->{'type'}[$axis_nr]; |
|
159
|
16
|
100
|
|
|
|
26
|
return undef unless $self->is_axis_numeric($axis_nr); |
|
160
|
15
|
|
33
|
|
|
40
|
$precision //= $self->{'precision'}; |
|
161
|
15
|
50
|
33
|
|
|
41
|
return undef unless ref $precision eq 'ARRAY' and exists $precision->[$axis_nr]; |
|
162
|
15
|
|
|
|
|
46
|
$precision->[$axis_nr]; |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# all axis |
|
166
|
|
|
|
|
|
|
sub is_euclidean { # all axis linear ? |
|
167
|
36
|
|
|
36
|
0
|
1192
|
my ($self) = @_; |
|
168
|
36
|
100
|
|
|
|
129
|
map { return 0 if $self->{'type'}[$_] != 1 } $self->basis->axis_iterator; |
|
|
92
|
|
|
|
|
326
|
|
|
169
|
24
|
|
|
|
|
91
|
return 1; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
sub is_cylindrical { # one axis angular, rest linear ? |
|
172
|
36
|
|
|
36
|
0
|
149
|
my ($self) = @_; |
|
173
|
36
|
|
|
|
|
62
|
my $angular_axis = 0; |
|
174
|
36
|
100
|
|
|
|
102
|
map { $angular_axis++ if $self->{'type'}[$_] == 0; |
|
|
109
|
|
|
|
|
296
|
|
|
175
|
109
|
100
|
|
|
|
283
|
return 0 if $self->{'type'}[$_] > 1; } $self->basis->axis_iterator; |
|
176
|
35
|
100
|
|
|
|
197
|
return ($angular_axis == 1) ? 1 : 0; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
sub is_int_valued { # all ranges int valued ? |
|
179
|
3
|
|
|
3
|
0
|
11
|
my ($self) = @_; |
|
180
|
3
|
100
|
|
|
|
7
|
map { return 0 if $self->{'precision'}[$_] != 0 } $self->basis->axis_iterator; |
|
|
4
|
|
|
|
|
40
|
|
|
181
|
0
|
|
|
|
|
0
|
return 1; |
|
182
|
|
|
|
|
|
|
} |
|
183
|
1388
|
100
|
|
1388
|
0
|
1767
|
sub has_constraints { my ($self) = @_; return (ref $self->{'constraint'}) ? 1 : 0 } # --> ? |
|
|
1388
|
|
|
|
|
2909
|
|
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
#### value checker ##################################################### |
|
187
|
|
|
|
|
|
|
sub check_value_shape { # @tuple -- $range, $precision --> $@vals | ~! |
|
188
|
373
|
|
|
373
|
0
|
673
|
my ($self, $tuple, $range, $precision) = @_; |
|
189
|
373
|
100
|
|
|
|
719
|
return 'color value tuple in '.$self->basis->space_name.' space needs to be ARRAY ref with '.$self->basis->axis_count.' elements' |
|
190
|
|
|
|
|
|
|
unless $self->basis->is_value_tuple( $tuple ); |
|
191
|
303
|
|
|
|
|
632
|
$range = $self->try_check_range_definition( $range ); |
|
192
|
303
|
50
|
|
|
|
1106
|
return $range unless ref $range; |
|
193
|
303
|
|
|
|
|
603
|
$precision = $self->try_check_precision_definition( $precision ); |
|
194
|
303
|
50
|
|
|
|
524
|
return $precision unless ref $precision; |
|
195
|
303
|
|
|
|
|
465
|
my @names = $self->basis->long_axis_names; |
|
196
|
303
|
|
|
|
|
490
|
for my $axis_index ($self->basis->axis_iterator){ |
|
197
|
696
|
50
|
|
|
|
944
|
next unless $self->is_axis_numeric( $axis_index ); |
|
198
|
696
|
100
|
|
|
|
2034
|
return $names[$axis_index]." value is below minimum of ".$range->[$axis_index][0] |
|
199
|
|
|
|
|
|
|
if $tuple->[$axis_index] < $range->[$axis_index][0]; |
|
200
|
596
|
100
|
|
|
|
1632
|
return $names[$axis_index]." value is above maximum of ".$range->[$axis_index][1] |
|
201
|
|
|
|
|
|
|
if $tuple->[$axis_index] > $range->[$axis_index][1]; |
|
202
|
492
|
100
|
100
|
|
|
1251
|
return $names[$axis_index]." value is not properly rounded " |
|
203
|
|
|
|
|
|
|
if $precision->[$axis_index] >= 0 |
|
204
|
|
|
|
|
|
|
and round_decimals($tuple->[$axis_index], $precision->[$axis_index]) != $tuple->[$axis_index]; |
|
205
|
|
|
|
|
|
|
} |
|
206
|
80
|
100
|
|
|
|
278
|
if ($self->has_constraints){ |
|
207
|
11
|
|
|
|
|
96
|
my $tuple = $self->normalize($tuple, $range); |
|
208
|
11
|
|
|
|
|
13
|
for my $constraint (values %{$self->{'constraint'}}){ |
|
|
11
|
|
|
|
|
39
|
|
|
209
|
11
|
100
|
|
|
|
234
|
return $constraint->{'error'} unless $constraint->{'checker'}->( $tuple ); |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
} |
|
212
|
78
|
|
|
|
|
429
|
return $tuple; |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub is_equal { # @tuple_a, @tuple_b -- $precision --> ? |
|
216
|
8
|
|
|
8
|
0
|
20
|
my ($self, $tuple_a, $tuple_b, $precision) = @_; |
|
217
|
8
|
100
|
100
|
|
|
18
|
return 0 unless $self->basis->is_value_tuple( $tuple_a ) and $self->basis->is_value_tuple( $tuple_b ); |
|
218
|
3
|
|
|
|
|
9
|
$precision = $self->try_check_precision_definition( $precision ); |
|
219
|
3
|
|
|
|
|
7
|
for my $axis_nr ($self->basis->axis_iterator) { |
|
220
|
9
|
50
|
|
|
|
20
|
return 0 if round_decimals($tuple_a->[$axis_nr], $precision->[$axis_nr]) |
|
221
|
|
|
|
|
|
|
!= round_decimals($tuple_b->[$axis_nr], $precision->[$axis_nr]); |
|
222
|
|
|
|
|
|
|
} |
|
223
|
3
|
|
|
|
|
15
|
return 1; |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub is_in_constraints { # @tuple --> ? # normalized values only, so it works on any ranges |
|
227
|
2
|
|
|
2
|
0
|
6
|
my ($self, $tuple) = @_; |
|
228
|
2
|
50
|
|
|
|
7
|
return 0 unless $self->basis->is_number_tuple( $tuple ); |
|
229
|
2
|
50
|
|
|
|
7
|
return 1 unless $self->has_constraints; |
|
230
|
2
|
|
|
|
|
3
|
for my $constraint (values %{$self->{'constraint'}}){ |
|
|
2
|
|
|
|
|
9
|
|
|
231
|
2
|
100
|
|
|
|
113
|
return 0 unless $constraint->{'checker'}->( $tuple ); |
|
232
|
|
|
|
|
|
|
} |
|
233
|
1
|
|
|
|
|
7
|
return 1; |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub is_in_bounds { # @tuple --> ? |
|
237
|
2
|
|
|
2
|
0
|
4
|
my ($self, $tuple, $range) = @_; |
|
238
|
2
|
50
|
|
|
|
5
|
return 0 unless $self->is_in_linear_bounds( $tuple, $range ); |
|
239
|
2
|
|
|
|
|
3
|
$range = $self->try_check_range_definition( $range ); |
|
240
|
2
|
|
|
|
|
3
|
for my $axis_nr ($self->basis->axis_iterator) { |
|
241
|
4
|
100
|
|
|
|
6
|
next if $self->{'type'}[$axis_nr]; # skip none linear axis |
|
242
|
1
|
50
|
33
|
|
|
7
|
return 0 if $tuple->[$axis_nr] < $range->[$axis_nr][0] |
|
243
|
|
|
|
|
|
|
or $tuple->[$axis_nr] > $range->[$axis_nr][1]; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
1
|
|
|
|
|
4
|
return 1; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub is_in_linear_bounds { # @tuple --> ? |
|
249
|
380
|
|
|
380
|
0
|
493
|
my ($self, $tuple, $range) = @_; |
|
250
|
380
|
100
|
|
|
|
533
|
return 0 unless $self->basis->is_number_tuple( $tuple ); |
|
251
|
377
|
|
|
|
|
591
|
$range = $self->try_check_range_definition( $range ); |
|
252
|
377
|
|
|
|
|
497
|
for my $axis_nr ($self->basis->axis_iterator) { |
|
253
|
1062
|
100
|
|
|
|
1368
|
next if $self->{'type'}[$axis_nr] != 1; # skip none linear axis |
|
254
|
978
|
100
|
100
|
|
|
2420
|
return 0 if $tuple->[$axis_nr] < $range->[$axis_nr][0] |
|
255
|
|
|
|
|
|
|
or $tuple->[$axis_nr] > $range->[$axis_nr][1]; |
|
256
|
|
|
|
|
|
|
} |
|
257
|
298
|
50
|
|
|
|
441
|
if ($self->has_constraints){ |
|
258
|
0
|
|
|
|
|
0
|
return $self->is_in_constraints( $self->normalize( $tuple, $range) ); |
|
259
|
|
|
|
|
|
|
} |
|
260
|
298
|
|
|
|
|
572
|
return 1; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
#### value ops ######################################################### |
|
264
|
|
|
|
|
|
|
sub clamp { # change values if outside of range to nearest boundary, angles get rotated into range |
|
265
|
995
|
|
|
995
|
0
|
2735
|
my ($self, $tuple, $range) = @_; |
|
266
|
995
|
|
|
|
|
1377
|
$range = $self->try_check_range_definition( $range ); |
|
267
|
995
|
50
|
|
|
|
1627
|
return $range unless ref $range; |
|
268
|
995
|
50
|
|
|
|
1582
|
$tuple = [] unless ref $tuple eq 'ARRAY'; |
|
269
|
995
|
|
|
|
|
1586
|
pop @$tuple while @$tuple > $self->basis->axis_count; |
|
270
|
995
|
|
|
|
|
1494
|
$tuple = [@$tuple]; |
|
271
|
|
|
|
|
|
|
|
|
272
|
995
|
|
|
|
|
1315
|
for my $axis_nr ($self->basis->axis_iterator){ |
|
273
|
3003
|
50
|
|
|
|
3544
|
next unless $self->is_axis_numeric( $axis_nr ); |
|
274
|
3003
|
100
|
|
|
|
3707
|
if (not defined $tuple->[$axis_nr]){ |
|
275
|
80
|
|
|
|
|
95
|
my $default_value = 0; |
|
276
|
80
|
100
|
66
|
|
|
275
|
$default_value = $range->[$axis_nr][0] if $default_value < $range->[$axis_nr][0] |
|
277
|
|
|
|
|
|
|
or $default_value > $range->[$axis_nr][1]; |
|
278
|
80
|
|
|
|
|
141
|
$tuple->[$axis_nr] = $default_value; |
|
279
|
|
|
|
|
|
|
} else { |
|
280
|
2923
|
100
|
|
|
|
3714
|
next unless $self->is_axis_euclidean( $axis_nr ); |
|
281
|
2698
|
100
|
|
|
|
4299
|
$tuple->[$axis_nr] = $range->[$axis_nr][0] if $tuple->[$axis_nr] < $range->[$axis_nr][0]; |
|
282
|
2698
|
100
|
|
|
|
4335
|
$tuple->[$axis_nr] = $range->[$axis_nr][1] if $tuple->[$axis_nr] > $range->[$axis_nr][1]; |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
} |
|
285
|
995
|
|
|
|
|
1913
|
$tuple = $self->rotate($tuple, $range); |
|
286
|
995
|
100
|
|
|
|
1861
|
if ($self->has_constraints){ |
|
287
|
29
|
|
|
|
|
149
|
$tuple = $self->normalize( $tuple, $range); |
|
288
|
29
|
|
|
|
|
54
|
for my $constraint (values %{$self->{'constraint'}}){ |
|
|
29
|
|
|
|
|
77
|
|
|
289
|
29
|
100
|
|
|
|
636
|
$tuple = $constraint->{'remedy'}->($tuple) unless $constraint->{'checker'}->( $tuple ); |
|
290
|
|
|
|
|
|
|
} |
|
291
|
29
|
|
|
|
|
79
|
$tuple = $self->denormalize( $tuple, $range); |
|
292
|
|
|
|
|
|
|
} |
|
293
|
995
|
|
|
|
|
2344
|
return $tuple; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
sub rotate { # rotate values of circular dimensions into range |
|
296
|
1010
|
|
|
1010
|
0
|
8373
|
my ($self, $tuple, $range) = @_; |
|
297
|
1010
|
50
|
|
|
|
1438
|
return unless $self->basis->is_number_tuple( $tuple ); |
|
298
|
1010
|
|
|
|
|
1667
|
$range = $self->try_check_range_definition( $range ); |
|
299
|
1010
|
50
|
|
|
|
1564
|
return $range unless ref $range; |
|
300
|
1010
|
|
|
|
|
2094
|
$tuple = [@$tuple]; |
|
301
|
1010
|
|
|
|
|
1538
|
for my $axis_nr ($self->basis->axis_iterator){ |
|
302
|
3048
|
100
|
|
|
|
3717
|
next unless $self->is_axis_angular( $axis_nr ); |
|
303
|
244
|
50
|
|
|
|
384
|
if (not defined $tuple->[$axis_nr]){ |
|
304
|
0
|
|
|
|
|
0
|
my $default_value = 0; |
|
305
|
0
|
0
|
0
|
|
|
0
|
$default_value = $range->[$axis_nr][0] if $default_value < $range->[$axis_nr][0] |
|
306
|
|
|
|
|
|
|
or $default_value > $range->[$axis_nr][1]; |
|
307
|
0
|
|
|
|
|
0
|
$tuple->[$axis_nr] = $default_value; |
|
308
|
|
|
|
|
|
|
} else { |
|
309
|
244
|
|
|
|
|
363
|
my $delta = $range->[$axis_nr][1] - $range->[$axis_nr][0]; |
|
310
|
244
|
|
|
|
|
509
|
$tuple->[$axis_nr] += $delta while $tuple->[$axis_nr] < $range->[$axis_nr][0]; |
|
311
|
244
|
|
|
|
|
452
|
$tuple->[$axis_nr] -= $delta while $tuple->[$axis_nr] > $range->[$axis_nr][1]; |
|
312
|
244
|
100
|
|
|
|
456
|
$tuple->[$axis_nr] = $range->[$axis_nr][0] if $tuple->[$axis_nr] == $range->[$axis_nr][1]; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
} |
|
315
|
1010
|
|
|
|
|
1873
|
return $tuple; |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub round { # $tuple -- $precision --> $tuple |
|
319
|
1227
|
|
|
1227
|
0
|
3208
|
my ($self, $tuple, $precision) = @_; |
|
320
|
1227
|
50
|
|
|
|
1820
|
return unless $self->basis->is_value_tuple( $tuple ); |
|
321
|
1227
|
|
|
|
|
2153
|
$precision = $self->try_check_precision_definition( $precision ); |
|
322
|
1227
|
50
|
|
|
|
1885
|
return "round got bad precision definition" unless ref $precision; |
|
323
|
1227
|
100
|
66
|
|
|
1746
|
[ map { ($self->is_axis_numeric( $_ ) and $precision->[$_] >= 0) ? round_decimals ($tuple->[$_], $precision->[$_]) : $tuple->[$_] } $self->basis->axis_iterator ]; |
|
|
3686
|
|
|
|
|
4672
|
|
|
324
|
|
|
|
|
|
|
} |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# normalisation |
|
327
|
|
|
|
|
|
|
sub normalize { |
|
328
|
762
|
|
|
762
|
0
|
2056
|
my ($self, $tuple, $range) = @_; |
|
329
|
762
|
50
|
|
|
|
1322
|
return unless $self->basis->is_value_tuple( $tuple ); |
|
330
|
762
|
|
|
|
|
1324
|
$range = $self->try_check_range_definition( $range ); |
|
331
|
762
|
50
|
|
|
|
1202
|
return $range unless ref $range; |
|
332
|
762
|
50
|
|
|
|
1092
|
[ map { ($self->is_axis_numeric( $_ )) ? (($tuple->[$_] - $range->[$_][0]) / ($range->[$_][1]-$range->[$_][0])) |
|
|
2294
|
|
|
|
|
2967
|
|
|
333
|
|
|
|
|
|
|
: $tuple->[$_] } $self->basis->axis_iterator ]; |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub denormalize { |
|
337
|
1053
|
|
|
1053
|
0
|
2997
|
my ($self, $tuple, $range) = @_; |
|
338
|
1053
|
50
|
|
|
|
1613
|
return unless $self->basis->is_value_tuple( $tuple ); |
|
339
|
1053
|
|
|
|
|
1797
|
$range = $self->try_check_range_definition( $range ); |
|
340
|
1053
|
50
|
|
|
|
1525
|
return $range unless ref $range; |
|
341
|
1053
|
50
|
|
|
|
1450
|
return [ map { ($self->is_axis_numeric( $_ )) ? ($tuple->[$_] * ($range->[$_][1]-$range->[$_][0]) + $range->[$_][0]) |
|
|
3166
|
|
|
|
|
3799
|
|
|
342
|
|
|
|
|
|
|
: $tuple->[$_] } $self->basis->axis_iterator ]; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub denormalize_delta { |
|
346
|
30
|
|
|
30
|
0
|
518
|
my ($self, $delta_values, $range) = @_; |
|
347
|
30
|
50
|
|
|
|
43
|
return unless $self->basis->is_value_tuple( $delta_values ); |
|
348
|
30
|
|
|
|
|
59
|
$range = $self->try_check_range_definition( $range ); |
|
349
|
30
|
50
|
|
|
|
54
|
return $range unless ref $range; |
|
350
|
30
|
50
|
|
|
|
43
|
[ map { ($self->is_axis_numeric( $_ )) |
|
|
91
|
|
|
|
|
106
|
|
|
351
|
|
|
|
|
|
|
? ($delta_values->[$_] * ($range->[$_][1]-$range->[$_][0])) |
|
352
|
|
|
|
|
|
|
: $delta_values->[$_] } $self->basis->axis_iterator ]; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub delta { # @normal_tuple_a, @normal_tuple_b --> @delta_tuple |
|
356
|
62
|
|
|
62
|
0
|
5502
|
my ($self, $tuple1, $tuple2) = @_; |
|
357
|
62
|
100
|
100
|
|
|
119
|
return unless $self->basis->is_value_tuple( $tuple1 ) and $self->basis->is_value_tuple( $tuple2 ); |
|
358
|
|
|
|
|
|
|
# ignore none numeric dimensions |
|
359
|
56
|
50
|
|
|
|
142
|
my @delta = map { $self->is_axis_numeric($_) ? ($tuple2->[$_] - $tuple1->[$_]) : 0 } $self->basis->axis_iterator; |
|
|
169
|
|
|
|
|
243
|
|
|
360
|
56
|
100
|
|
|
|
100
|
[ map { $self->{'type'}[$_] ? $delta[$_] : # adapt to circular dimensions |
|
|
169
|
100
|
|
|
|
419
|
|
|
|
|
100
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
$delta[$_] < -0.5 ? ($delta[$_]+1) : |
|
362
|
|
|
|
|
|
|
$delta[$_] > 0.5 ? ($delta[$_]-1) : $delta[$_] } $self->basis->axis_iterator ]; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
1; |