The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

=encoding utf-8
=head1 NAME
-Msubst::dyncmap - Getopt::EX Dynamic colormap module
=head1 SYNOPSIS
option --subst-color-light \
-Msubst::dyncmap \
--colormap \
--dyncmap \
range=0-2,except=000:111:222,shift=3,even="555D/%s",odd="I;000/%s"
=head1 DESCRIPTION
Parameter is given in a form of B<name>=I<value> and connected by
comma.
=over 7
=item B<range>=I<s>-I<e>[:I<s>-I<e>[:I<s>-I<e>]]
RGB range. All range can be given like C<0-2:0-2:0-2>, or if the
number of range is less than three, last range is repeated.
Each RGB value is in the range of 0 to 5, and produces 6x6x6 216
colors.
=item B<except>
Specify exception value, like C<000:111:222>.
=item B<even>=I<colormap>
=item B<odd>=I<colormap>
Colormap string for even and odd index. String is given to C<sprintf>
function with RGB parameter.
=item B<shift>=I<number>
Range is shifted by this value for odd index map. Shifted value have
to be in the range of 0 to 5.
=item B<sort>=[I<none>,I<average>,I<luminance>]
Specify sort algorithm. Default is B<average>.
=item B<reverse>=[0,1]
If true, map is reversed.
=back
=head1 SEE ALSO
L<Getopt::EX>, L<Getopt::EX::Colormap>
L<App::Greple>, L<App::Greple::subst>
=head1 AUTHOR
Kazumasa Utashiro
=head1 LICENSE
Copyright 2020-2021 Kazumasa Utashiro.
You can redistribute it and/or modify it under the same terms
as Perl itself.
=cut
use v5.14;
use strict;
use Carp;
use List::Util qw(notall pairmap reduce sum);
##
## Dyamic colormap generator
##
sub dyncmap {
my %opt = @_;
for ($opt{range}) {
my @range = pairmap { [ $a..$b ] } /([0-5])-([0-5])/g
or die "$_: range format error";
push @range, $range[-1] while @range < 3;
$_ = \@range;
}
my @cm = cmap(%opt);
join ',', @cm;
}
sub combination {
my $c = reduce {
[ map { my @a = @$_; map { [ @a, $_ ] } @$b } @$a ];
} [ [] ], @_;
@$c;
}
sub rgb {
if (notall { 0 <= $_ && $_ <= 5 } @_) {
local $" = '';
die "@_: Invalid RGB value";
}
my($r, $g, $b) = @_;
if ($r == $g and $r == $b) {
qw(L03 L07 L11 L15 L19 L23)[$r];
} else {
"$r$g$b";
}
}
my %sort = (
none => undef,
average => sub {
local $" = '';
map { $_->[0] }
sort { $a->[1] <=> $b->[1] || $a->[2] cmp $b->[2] }
map { [ $_, sum(@$_), "@$_" ] }
@_;
},
luminance => sub {
map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, $$_[0]*30 + $$_[1]*59 + $$_[2]*11 ] }
@_;
},
);
sub cmap {
my %opt = (shift => 0, except => '', sort => 'average', @_);
my @cm = combination @{$opt{range}};
if (my %except = map { $_ => 1 } $opt{except} =~ /\b(\d\d\d)\b/g) {
local $" = '';
@cm = grep { not $except{"@$_"} } @cm;
}
if (my $algorithm = $opt{sort}) {
exists $sort{$algorithm} or die "$algorithm: unknown algorithm";
if (my $sort = $sort{$algorithm}) {
@cm = $sort->(@cm);
}
}
@cm = reverse @cm if $opt{reverse};
my @map = map {
( sprintf($opt{even}, rgb(@$_) ),
sprintf($opt{odd}, rgb(map $_ + $opt{shift}, @$_) ) );
} @cm;
}
1;
__DATA__
mode function
option --dyncmap &dyncmap($<shift>)