File Coverage

lib/Graphics/Toolkit/Color.pm
Criterion Covered Total %
statement 209 214 97.6
branch 131 152 86.1
condition 65 93 69.8
subroutine 24 24 100.0
pod 15 15 100.0
total 444 498 89.1


line stmt bran cond sub pod time code
1              
2             # public user level API: docs, help and arg cleaning
3              
4             package Graphics::Toolkit::Color;
5             our $VERSION = '2.02';
6              
7 4     4   335145 use v5.12;
  4         11  
8 4     4   16 use warnings;
  4         6  
  4         213  
9 4     4   16 use Exporter 'import';
  4         4  
  4         117  
10 4     4   599 use Graphics::Toolkit::Color::Space::Util qw/is_nr/;
  4         6  
  4         216  
11 4     4   1392 use Graphics::Toolkit::Color::SetCalculator;
  4         8  
  4         11137  
12              
13             my $default_space_name = Graphics::Toolkit::Color::Space::Hub::default_space_name();
14             our @EXPORT_OK = qw/color is_in_gamut/;
15              
16             ## constructor #########################################################
17              
18 24     24 1 600101 sub color { Graphics::Toolkit::Color->new ( @_ ) }
19              
20             sub new {
21 87     87 1 1213 my ($pkg, @args) = @_;
22 87         241 my $help = <
23             constructor new of Graphics::Toolkit::Color object needs either:
24             1. a color name: new('red') or new('SVG:red')
25             3. RGB hex string new('#FF0000') or new('#f00')
26             4. $default_space_name array or ARRAY ref: new( 255, 0, 0 ) or new( [255, 0, 0] )
27             5. named array or ARRAY ref: new( 'HSL', 255, 0, 0 ) or new( ['HSL', 255, 0, 0 ])
28             6. named string: new( 'HSL: 0, 100, 50' ) or new( 'ncol(r0, 0%, 0%)' )
29             7. HASH or HASH ref with values from RGB or any other space:
30             new(r => 255, g => 0, b => 0) or new({ hue => 0, saturation => 100, lightness => 50 })
31             EOH
32 87         299 my $color_def = _compact_color_def_into_scalar( @args );
33 87 100       269 return $help unless defined $color_def;
34 78         243 my $self = _new_from_scalar_def( $color_def );
35 78 100       734 return (ref $self) ? $self : $help;
36             }
37             sub _compact_color_def_into_scalar {
38 91     91   217 my (@args) = @_;
39 91         321 my $first_arg_is_color_space = Graphics::Toolkit::Color::Space::Hub::is_space_name( $args[0] );
40 91 100 100     417 @args = ([ $args[0], @{$args[1]} ]) if @args == 2 and $first_arg_is_color_space and ref $args[1] eq 'ARRAY';
  2   66     8  
41 91 100 100     511 @args = ([ @args ]) if @args == 3 or (@args > 3 and $first_arg_is_color_space);
      100        
42 91 100 100     457 @args = ({ @args }) if @args == 6 or @args == 8;
43 91 100       278 return (@args == 1) ? $args[0] : undef;
44             }
45             sub _new_from_scalar_def { # color defs of method arguments
46 137     137   264 my ($color_def) = shift;
47 137 100       359 return $color_def if ref $color_def eq __PACKAGE__;
48 117         459 return _new_from_value_obj( Graphics::Toolkit::Color::Values->new_from_any_input( $color_def ) );
49             }
50             sub _new_from_value_obj {
51 292     292   413 my ($value_obj) = @_;
52 292 100       593 return $value_obj unless ref $value_obj eq 'Graphics::Toolkit::Color::Values';
53 244         1043 return bless {values => $value_obj};
54             }
55              
56             ########################################################################
57             sub _split_named_args {
58 169     169   398 my ($raw_args, $only_parameter, $required_parameter, $optional_parameter, $parameter_alias) = @_;
59 169 50 66     600 @$raw_args = %{$raw_args->[0]} if @$raw_args == 1 and ref $raw_args->[0] eq 'HASH' and not
  0   0     0  
      33        
60             (defined $only_parameter and $only_parameter eq 'to' and ref _new_from_scalar_def( $raw_args ) );
61              
62 169 100 100     528 if (@$raw_args == 1 and defined $only_parameter and $only_parameter){
      66        
63 34 50       69 return "The one default argument can not cover multiple, required parameter !" if @$required_parameter > 1;
64 34 50 66     102 return "The default argument does not cover the required argument!"
65             if @$required_parameter and $required_parameter->[0] ne $only_parameter;
66              
67 34         136 my %defaults = %$optional_parameter;
68 34         71 delete $defaults{$only_parameter};
69 34         179 return {$only_parameter => $raw_args->[0], %defaults};
70             }
71 135         170 my %clean_arg;
72 135 100       352 if (@$raw_args % 2) {
73 2 50 33     13 return (defined $only_parameter and $only_parameter)
74             ? "Got odd number of values, please use key value pairs as arguments or one default argument !\n"
75             : "Got odd number of values, please use key value pairs as arguments !\n"
76             }
77 133         361 my %arg_hash = @$raw_args;
78 133         253 for my $parameter_name (@$required_parameter){
79 72 100 66     304 if (ref $parameter_alias eq 'HASH' and exists $parameter_alias->{ $parameter_name }
      66        
80             and exists $arg_hash{ $parameter_alias->{$parameter_name} }){
81 10         32 $arg_hash{ $parameter_name } = delete $arg_hash{ $parameter_alias->{$parameter_name} };
82             }
83 72 100       198 return "Argument '$parameter_name' is missing\n" unless exists $arg_hash{$parameter_name};
84 66         186 $clean_arg{ $parameter_name } = delete $arg_hash{ $parameter_name };
85             }
86 127         312 for my $parameter_name (keys %$optional_parameter){
87 412 50 66     667 if (ref $parameter_alias eq 'HASH' and exists $parameter_alias->{ $parameter_name }
      33        
88             and exists $arg_hash{ $parameter_alias->{$parameter_name} }){
89 0         0 $arg_hash{ $parameter_name } = delete $arg_hash{ $parameter_alias->{$parameter_name} };
90             }
91             $clean_arg{ $parameter_name } = exists $arg_hash{$parameter_name}
92             ? delete $arg_hash{ $parameter_name }
93 412 100       912 : $optional_parameter->{ $parameter_name };
94             }
95 127 100       290 return "Inserted unknown argument(s): ".(join ',', keys %arg_hash)."\n" if %arg_hash;
96 120         283 return \%clean_arg;
97             }
98              
99             ## getter ##############################################################
100             sub values {
101 60     60 1 41895 my ($self, @args) = @_;
102 60         363 my $arg = _split_named_args( \@args, 'in', [],
103             { in => $default_space_name, as => 'list',
104             precision => undef, range => undef, suffix => undef } );
105 60         238 my $help = <
106             GTC method 'values' accepts either no arguments, one color space name or four optional, named args:
107             values ( ...
108             in => 'HSL', # color space name, defaults to "$default_space_name"
109             as => 'css_string', # output format name, default is "list"
110             range => 1, # value range (SCALAR or ARRAY), default set by space def
111             precision => 3, # value precision (SCALAR or ARRAY), default set by space
112             suffix => '%', # value suffix (SCALAR or ARRAY), default set by color space
113              
114             EOH
115 60 100       137 return $arg.$help unless ref $arg;
116 59         253 $self->{'values'}->formatted( @$arg{qw/in as suffix range precision/} );
117             }
118              
119             sub name {
120 36     36 1 6638 my ($self, @args) = @_;
121 36 100       179 return $self->{'values'}->name unless @args;
122 2         14 my $arg = _split_named_args( \@args, 'from', [], {from => 'default', all => 0, full => 0, distance => 0});
123 2         5 my $help = <
124             GTC method 'name' accepts three optional, named arguments:
125             name ( ...
126             'CSS' # color naming scheme works as only positional argument
127             from => 'CSS' # same scheme (defaults to internal: X + CSS + PantoneReport)
128             from => ['SVG', 'X'] # more color naming schemes at once, without duplicates
129             all => 1 # returns list of all names with the object's RGB values (defaults 0)
130             full => 1 # adds color scheme name to the color name. 'SVG:red' (defaults 0)
131             distance => 3 # color names from within distance of 3 (defaults 0)
132             EOH
133 2         11 return Graphics::Toolkit::Color::Name::from_values( $self->{'values'}->shaped, @$arg{qw/from all full distance/});
134             }
135              
136             sub closest_name {
137 11     11 1 8689 my ($self, @args) = @_;
138 11         69 my $arg = _split_named_args( \@args, 'from', [], {from => 'default', all => 0, full => 0});
139 11         26 my $help = <
140             GTC method 'name' accepts three optional, named arguments:
141             closest_name ( ...
142             'CSS' # color naming scheme works as only positional argument
143             from => 'CSS' # same scheme (defaults to internal: X + CSS + PantoneReport)
144             from => ['SVG', 'X'] # more color naming schemes at once, without duplicates
145             all => 1 # returns list of all names with the object's RGB values (defaults 0)
146             full => 1 # adds color scheme name to the color name. 'SVG:red' (defaults 0)
147             EOH
148             my ($name, $distance) = Graphics::Toolkit::Color::Name::closest_from_values(
149 11         55 $self->{'values'}->shaped, @$arg{qw/from all full/});
150 11 100       112 return wantarray ? ($name, $distance) : $name;
151             }
152              
153             sub distance {
154 9     9 1 1749 my ($self, @args) = @_;
155 9         59 my $arg = _split_named_args( \@args, 'to', ['to'], {in => $default_space_name, select => undef, range => undef});
156 9         69 my $help = <
157             GTC method 'distance' accepts as arguments either a scalar color definition or
158             four named arguments, only the first being required:
159             distance ( ...
160             to => 'green' # color object or color definition (required)
161             in => 'HSL' # color space name, defaults to "$default_space_name"
162             select => 'red' # axis name or names (ARRAY ref), default is none
163             range => 2**16 # value range definition, defaults come from color space def
164             EOH
165 9 100       33 return $arg.$help unless ref $arg;
166 7         24 my $target_color = _new_from_scalar_def( $arg->{'to'} );
167 7 50       18 return "target color definition: $arg->{to} is ill formed" unless ref $target_color;
168 7         25 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $arg->{'in'} );
169 7 50       17 return "$color_space\n".$help unless ref $color_space;
170 7 100       16 if (defined $arg->{'select'}){
171 3 100       15 if (not ref $arg->{'select'}){
    50          
172             return $arg->{'select'}." is not an axis name in color space: ".$color_space->name
173 2 50       12 unless $color_space->is_axis_name( $arg->{'select'} );
174             } elsif (ref $arg->{'select'} eq 'ARRAY'){
175 1         3 for my $axis_name (@{$arg->{'select'}}) {
  1         5  
176 2 50       9 return "$axis_name is not an axis name in color space: ".$color_space->name
177             unless $color_space->is_axis_name( $axis_name );
178             }
179 0         0 } else { return "The 'select' argument needs one axis name or an ARRAY with several axis names".
180             " from the same color space!" }
181             }
182 7         26 my $range_def = $color_space->shape->try_check_range_definition( $arg->{'range'} );
183 7 50       19 return $range_def unless ref $range_def;
184             Graphics::Toolkit::Color::Space::Hub::distance(
185 7         33 $self->{'values'}->normalized, $target_color->{'values'}->normalized, $color_space->name ,$arg->{'select'}, $range_def );
186             }
187              
188              
189             sub is_in_gamut {
190 4     4 1 3092 my ($self, @args) = @_;
191 4 100       23 unshift @args, $self unless ref $self eq __PACKAGE__;
192 4         19 my $color_def = _compact_color_def_into_scalar(@args);
193 4 50       17 return 0 unless defined $color_def;
194 4         18 Graphics::Toolkit::Color::Values::is_in_gamut($color_def); # range def later as second arg
195             }
196            
197             ## single color creation methods #######################################
198             sub apply {
199 4     4 1 2263 my ($self, @args) = @_;
200 4         18 my $arg = _split_named_args( \@args, undef, ['gamma'], {in => $default_space_name} );
201 4         8 my $help = <
202             GTC method 'apply' accepts one named argument with a numeric value:
203             apply ( ...
204             gamma => 2.2, # reverse is with 1 / 2.2
205             in => 'OKlab', # compute in oklab space
206             EOH
207 4 50       6 return $arg.$help unless ref $arg;# 'ARRAY' length == axis
208 4         13 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $arg->{'in'} );
209 4 50       7 return "$color_space\n".$help unless ref $color_space;
210 4         16 _new_from_value_obj( Graphics::Toolkit::Color::Calculator::apply_gamma( $self->{'values'}, $arg->{'gamma'}, $color_space ) );
211             }
212              
213             sub set_value {
214 10     10 1 9033 my ($self, @args) = @_;
215 10 50 33     25 @args = %{$args[0]} if @args == 1 and ref $args[0] eq 'HASH';
  0         0  
216 10         16 my $help = <
217             GTC method 'set_value' needs a value HASH (not a ref) whose keys are axis names or
218             short names from one color space. If the chosen axis name(s) is/are ambiguous,
219             you might add the "in" argument:
220             set_value( green => 20 ) or set( g => 20 ) or
221             set_value( hue => 240, in => 'HWB' )
222             EOH
223 10 100 66     48 return $help if @args % 2 or not @args or @args > 10;
      66        
224 9         17 my $partial_color = { @args };
225 9         15 my $space_name = delete $partial_color->{'in'};
226 9         22 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $space_name );
227 9 50       13 return "$color_space\n".$help unless ref $color_space;
228 9         26 _new_from_value_obj( Graphics::Toolkit::Color::Calculator::set_value( $self->{'values'}, $partial_color, $space_name ) );
229             }
230              
231             sub add_value {
232 10     10 1 6685 my ($self, @args) = @_;
233 10 50 33     29 @args = %{$args[0]} if @args == 1 and ref $args[0] eq 'HASH';
  0         0  
234 10         13 my $help = <
235             GTC method 'add_value' needs a value HASH (not a ref) whose keys are axis names or
236             short names from one color space. If the chosen axis name(s) is/are ambiguous,
237             you might add the "in" argument:
238             add_value( blue => -10 ) or set( b => -10 )
239             add_value( hue => 100 , in => 'HWB' )
240             EOH
241 10 100 66     60 return $help if @args % 2 or not @args or @args > 10;
      66        
242 9         21 my $partial_color = { @args };
243 9         17 my $space_name = delete $partial_color->{'in'};
244 9         23 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $space_name );
245 9 50       15 return "$color_space\n".$help unless ref $color_space;
246 9         30 _new_from_value_obj( Graphics::Toolkit::Color::Calculator::add_value( $self->{'values'}, $partial_color, $space_name ) );
247             }
248              
249             sub mix {
250 24     24 1 14673 my ($self, @args) = @_;
251 24         106 my $arg = _split_named_args( \@args, 'to', ['to'], {in => $default_space_name, amount => -1});
252 24         65 my $help = <
253             GTC method 'mix' accepts three named arguments, only the first being required:
254             mix ( ...
255             to => ['HSL', 240, 100, 50] # scalar color definition or ARRAY ref thereof
256             amount => 20 # percentage value or ARRAY ref thereof, default is 50
257             in => 'HSL' # color space name, defaults to "$default_space_name"
258             Please note that either both or none of the first two arguments has to be an ARRAY.
259             Both ARRAY have to have the same length. 'amount' refers to the color(s) picked with 'to'.
260             EOH
261 24 100       49 return $arg.$help unless ref $arg;
262 22         63 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( delete $arg->{'in'} );
263 22 100       44 return "$color_space\n".$help unless ref $color_space;
264 21         40 my $recipe = _new_from_scalar_def( $arg->{'to'} );
265 21 100       27 if (ref $recipe){
266 10 100       29 return "argument 'amount' has to be a sacalar value if only one color is mixed !\n".$help if ref $arg->{'amount'};
267 9 100       24 $arg->{'amount'} = 50 if $arg->{'amount'} < 0;
268 9 100       17 $arg->{'amount'} = 100 if $arg->{'amount'} > 100;
269 9         27 $recipe = [{color => $recipe->{'values'}, percent => $arg->{'amount'}}];
270 9 100       35 push @$recipe, {color => $self->{'values'}, percent => 100 - $arg->{'amount'} } if $arg->{'amount'} < 100;
271              
272             } else {
273 11 100       25 if (ref $arg->{'to'} ne 'ARRAY'){
274 2         13 return "target color definition (argument 'to'): '$arg->{to}' is ill formed. It has to be one color definition or an ARRAY of the.";
275             } else {
276             return "Argument 'amount' has to be an ARRAY of same length as argument 'to' (color definitions)!\n".$help
277 9 100 66     38 if ref $arg->{'to'} eq 'ARRAY' and ref $arg->{'amount'} eq 'ARRAY' and @{$arg->{'amount'}} != @{$arg->{'to'}};
  4   100     8  
  4         16  
278 8         11 my $color_count = 1 + @{$arg->{'to'}};
  8         13  
279 8 100       15 unless (ref $arg->{'amount'}){
280             $arg->{'amount'} = ($arg->{'amount'} < 0)
281             ? [(100/$color_count) x $color_count]
282 5 50       21 : [($arg->{'amount'}) x $color_count];
283             }
284 8         11 $recipe = [];
285 8         10 my $amount_sum = 0;
286 8         8 for my $color_nr (0 .. $#{$arg->{'to'}}){
  8         20  
287 14         16 my $color_def = $arg->{'to'}[$color_nr];
288 14         15 my $color = _new_from_scalar_def( $color_def );
289 14 50       19 return "target color nr. $color_nr definition: '$color_def' is ill formed" unless ref $color;
290 14         42 push @$recipe, { color => $color->{'values'}, percent => $arg->{'amount'}[$color_nr] };
291 14         27 $amount_sum += $arg->{'amount'}[$color_nr];
292             }
293 8 100       25 push @$recipe, {color => $self->{'values'}, percent => 100 - $amount_sum } if $amount_sum < 100;
294 8 100       15 if ($amount_sum > 100){
295 1         6 $_->{'percent'} = ($_->{'percent'} / $amount_sum * 100) for @$recipe;
296             }
297             }
298             }
299 17         63 _new_from_value_obj( Graphics::Toolkit::Color::Calculator::mix( $self->{'values'}, $recipe, $color_space ) );
300             }
301              
302             sub invert {
303 15     15 1 1808 my ($self, @args) = @_;
304 15         62 my $arg = _split_named_args( \@args, 'in', [], {in => $default_space_name, only => undef});
305 15         37 my $help = <
306             GTC method 'invert' accepts one optional argument, which can be positional or named:
307             invert ( ...
308             in => 'HSL', # color space name, defaults to "$default_space_name"
309             only => 'Saturation', # inverts only second value of the tuple
310             only => [qw/s l/], # axis name or names have to match selected space
311             EOH
312 15 100 66     61 return $arg.$help unless ref $arg and (not ref $arg->{'only'} or ref $arg->{'only'} eq 'ARRAY');
      100        
313 14         35 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $arg->{'in'} );
314 14 100       26 return "$color_space\n".$help unless ref $color_space;
315 13         42 _new_from_value_obj( Graphics::Toolkit::Color::Calculator::invert( $self->{'values'}, $arg->{'only'}, $color_space ) );
316             }
317              
318             ## color set creation methods ##########################################
319             sub complement {
320 12     12 1 697 my ($self, @args) = @_;
321 12         70 my $arg = _split_named_args( \@args, 'steps', [], {steps => 1, tilt => 0, target => {}});
322 12         77 my $help = <
323             GTC method 'complement' is computed in HSL and has two named, optional arguments:
324             complement ( ...
325             steps => 20 # count of produced colors, default is 1
326             tilt => 10 # default is 0
327             target => {h => 10, s => 20, l => 3} # sub-keys are independent, default to 0
328             EOH
329 12 100       38 return $arg.$help unless ref $arg;
330 11 100       49 return "Optional argument 'steps' has to be a number !\n".$help unless is_nr($arg->{'steps'});
331 9 50       34 return "Optional argument 'steps' is zero, no complement colors will be computed !\n".$help unless $arg->{'steps'};
332 9 100       23 return "Optional argument 'tilt' has to be a number !\n".$help unless is_nr($arg->{'tilt'});
333 8 100       37 return "Optional argument 'target' has to be a HASH ref !\n".$help if ref $arg->{'target'} ne 'HASH';
334 7         13 my ($target_values, $space_name);
335 7 100       21 if (keys %{$arg->{'target'}}){
  7         25  
336 2         12 ($target_values, $space_name) = Graphics::Toolkit::Color::Space::Hub::deformat_partial_hash( $arg->{'target'}, 'HSL' );
337 2 100       13 return "Optional argument 'target' got HASH keys that do not fit HSL space (use 'h','s','l') !\n".$help
338             unless ref $target_values;
339 5         8 } else { $target_values = [] }
340 13         17 map {_new_from_value_obj( $_ )}
341 6         37 Graphics::Toolkit::Color::SetCalculator::complement( $self->{'values'}, @$arg{qw/steps tilt/}, $target_values );
342             }
343              
344             sub gradient {
345 14     14 1 2247 my ($self, @args) = @_;
346 14         118 my $arg = _split_named_args( \@args, 'to', ['to'], {steps => 10, tilt => 0, in => $default_space_name});
347 14         62 my $help = <
348             GTC method 'gradient' accepts four named arguments, only the first is required:
349             gradient ( ...
350             to => 'blue' # scalar color definition or ARRAY ref thereof
351             steps => 20 # count of produced colors, defaults to 10
352             tilt => 1 # dynamics of color change, defaults to 0
353             in => 'HSL' # color space name, defaults to "$default_space_name"
354             EOH
355 14 100       54 return $arg.$help unless ref $arg;
356 12         34 my @colors = ($self->{'values'});
357 12         39 my $target_color = _new_from_scalar_def( $arg->{'to'} );
358 12 100       23 if (ref $target_color) {
359 9         17 push @colors, $target_color->{'values'} }
360             else {
361 3 100 66     20 return "Argument 'to' contains malformed color definition!\n".$help if ref $arg->{'to'} ne 'ARRAY' or not @{$arg->{'to'}};
  2         8  
362 2         3 for my $color_def (@{$arg->{'to'}}){
  2         4  
363 5         8 my $target_color = _new_from_scalar_def( $color_def );
364 5 100       22 return "Argument 'to' contains malformed color definition: $color_def !\n".$help unless ref $target_color;
365 4         13 push @colors, $target_color->{'values'};
366             }
367             }
368             return "Argument 'steps' has to be a number greater zero !\n".$help
369 10 100 66     24 unless is_nr($arg->{'steps'}) and $arg->{'steps'} > 0;
370 9         19 $arg->{'steps'} = int $arg->{'steps'};
371 9 100       18 return "Argument 'tilt' has to be a number !\n".$help unless is_nr($arg->{'tilt'});
372 8         20 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $arg->{'in'} );
373 8 100       24 return "$color_space\n".$help unless ref $color_space;
374 53         53 map {_new_from_value_obj( $_ )}
375 7         27 Graphics::Toolkit::Color::SetCalculator::gradient( \@colors, @$arg{qw/steps tilt/}, $color_space);
376             }
377              
378             sub cluster {
379 18     18 1 8635 my ($self, @args) = @_;
380 18         147 my $arg = _split_named_args( \@args, undef, ['radius', 'minimal_distance'], {in => $default_space_name},
381             {radius => 'r', minimal_distance => 'min_d'} );
382 18         79 my $help = <
383             GTC method 'cluster' accepts three named arguments, the first two being required:
384             cluster ( ...
385             radius => 3 # ball shaped cluster with cuboctahedral packing or
386             r => [10, 5, 3] # cuboid shaped cluster with cubical packing
387             minimal_distance => 0.5 # minimal distance between colors in cluster
388             min_d => 0.5 # short alias for minimal distance
389             in => 'HSL' # color space name, defaults to "$default_space_name"
390             EOH
391 18 100       105 return $arg.$help unless ref $arg;
392 12         47 my $color_space = Graphics::Toolkit::Color::Space::Hub::try_get_space( $arg->{'in'} );
393 12 100       46 return "$color_space\n".$help unless ref $color_space;
394             return "Argument 'radius' has to be a number or an ARRAY of numbers".$help
395 11 100 100     42 unless is_nr($arg->{'radius'}) or $color_space->is_number_tuple( $arg->{'radius'} );
396             return "Argument 'distance' has to be a number greater zero !\n".$help
397 8 100 100     24 unless is_nr($arg->{'minimal_distance'}) and $arg->{'minimal_distance'} > 0;
398             return "Ball shaped cluster works only in spaces with three dimensions !\n".$help
399 6 100 100     26 if $color_space->axis_count > 3 and not ref $arg->{'radius'};
400 57         57 map {_new_from_value_obj( $_ )}
401 5         31 Graphics::Toolkit::Color::SetCalculator::cluster( $self->{'values'}, @$arg{qw/radius minimal_distance/}, $color_space);
402             }
403              
404             1;
405              
406             __END__