Colourcodes: new query patterns (#4453)

* ColorCodes: Cleaning out comments and some minor refactoring

* ColorCodes: Tabs -> spaces and failing test for #190

* ColorCodes: We need more control over the actual query handling so I want the raw query

* ColorCodes: red: \d green: \d blue: \d queries now work

* ColorCodes: refactoring out the need for capturing the inversion words

* ColorCodes: Removing unnecessary hash

* ColourCodes: Attempting to push as much limiting and protection as early as possible

* ColourCodes: Layout tinkering

* ColorCodes: Matches can be wider now we're planning to use named captures

* ColorCodes: Adding named captures for loop removal

* ColorCodes: Converting to use just the named captures; tests failing

* ColorCodes: need to map the type for cmyk

* ColorCodes: Layout

* ColorCodes: RGBA can be implemented as an alias now in the regex :D

* ColorCodes: Will now respond to bluishblack html not just html bluishblack!

* ColorCodes: This regex case seems to make no difference to passing tests or query patterns

* ColorCodes: Types are case sensitive!

* ColorCodes: Using the matches we can construct a colour without hacking the input query

* ColorCodes: We can use the matches for detecting inversion

* ColorCodes: Rearranging

* ColorCodes: Removing unused

* ColorCodes: Bit of cleaning

* ColorCodes: Don't need to do this in the else anymore, we can do it before!

* ColorCodes: show -> to flip the ternary

* ColorCodes: Adding tests for #3758

* ColourCodes: Switching to query_lc

* ColourCodes: Regex refactor
master
Rob Emery 2017-09-06 00:54:36 +01:00 committed by Zaahir Moolla
parent 781856b4e0
commit 85f28710a6
2 changed files with 65 additions and 74 deletions

View File

@ -13,6 +13,7 @@ use Math::Round;
use Try::Tiny;
my %types = ( # hash of keyword => Convert::Color prefix
rgba => 'rgb8',
rgb => 'rgb8',
hex => 'rgb8',
html => 'rgb8',
@ -23,7 +24,7 @@ my %types = ( # hash of keyword => Convert::Color prefix
cmy => 'cmy',
cmyk => 'cmyk',
cmyb => 'cmyk',
);
);
# Eliminate NBS_ISCC sub-dictionaries from our lookups.
# They contain "idiosyncratic" color names (including 'email' in NBS_ISCC::M) which will
@ -31,88 +32,70 @@ my %types = ( # hash of keyword => Convert::Color prefix
my $color_dictionaries = join(',', grep { $_ !~ /^nbs-iscc-/ } map { $_->id } Color::Library->dictionaries);
my $typestr = join '|', sort { length $b <=> length $a } keys %types;
$typestr =~ s/([#\^\$\*\+\?])/\\$1/g;
triggers query_raw => qr/^
my $inverse_words = qr/inverse|negative|opposite/;
my $trigger_and_guard = qr/^
(?:what(?:\si|'?)s \s* (?:the)? \s+)? # what's the, whats the, what is the, what's, what is, whats
(?:(inverse|negative|opposite)\s+(?:of)?)?
(?<inv>$inverse_words\s+(?:of)?(?:\s?the\s?)?)?
(?:
(.*?)\s*(.+?)\bcolou?r(?:\s+code)?| # handles "rgb red color code", "red rgb color code", etc
(.*?)\s*(.+?)\brgb(?:\s+code)?| # handles "red rgb code", etc
(.*?)\s*colou?r(?:\s+code)?(?:\s+for)?\s+(.+?)| # handles "rgb color code for red", "red color code for html", etc
(.*?)(rgba)\s*:?\s*\(?\s*(.+?)\s*\)?| # handles "rgba( red )", "rgba:255,0,0", "rgba(255 0 0)", etc
([^\s]*?)\s*($typestr)\s*:?\s*\(?\s*(.+?)\s*\)?|# handles "rgb( red )", "rgb:255,0,0", "rgb(255 0 0)", etc
\#?([0-9a-f]{6})|\#([0-9a-f]{3}) # handles #00f, #0000ff, etc
red:\s*(?<r>[0-9]{1,3})\s*green:\s*(?<g>[0-9]{1,3})\s*blue:\s*(?<b>[0-9]{1,3})| # handles red: x green: y blue: z
(?<type>$typestr)\s*colou?r(?:\s+code)?(?:\s+for)?\s+(?<color>.+?)| # handles "rgb color code for red", "red color code for html", etc
(?<type>$typestr)\s*:?\s*\(?\s*(?<color>.+?)\s*\)?| # handles "rgb( red )", "rgb:255,0,0", "rgb(255 0 0)", etc
(?<color>.+?)\b(rgb|css|html)(?:\s+code)?| # handles "red rgb code", etc
\#?(?<color>[0-9a-f]{6})|\#(?<color>[0-9a-f]{3}) # handles #00f, #0000ff, etc
)
(?:(?:'?s)?\s+(inverse|negative|opposite))?
(?<inv>(?:'?s)?\s+$inverse_words)?
(?:\sto\s(?:$typestr))?
$/ix;
$/ix;
triggers query_raw => $trigger_and_guard;
zci is_cached => 1;
zci answer_type => 'color_code';
my %trigger_invert = map { $_ => 1 } (qw( inverse negative opposite ));
my %trigger_filler = map { $_ => 1 } (qw( code ));
my $color_mix = Color::Mix->new;
sub percentify {
my @out;
push @out, ($_ <= 1 ? round(($_ * 100))."%" : round($_)) for @_;
return @out;
return map { ($_ <= 1 ? round(($_ * 100))."%" : round($_)) } @_;
}
handle matches => sub {
handle query_lc => sub {
my $color;
my $filler_count;
my $inverse;
my $type = 'rgb8'; # Default type, can be overridden below.
my @matches = @_;
s/\sto\s(?:$typestr)//;
$filler_count = 0;
foreach my $q (map { lc $_ } grep { defined $_ } @matches) {
# $q now contains the defined normalized matches which can be:
if (exists $types{$q}) {
$type = $types{$q}; # - One of our types.
} elsif ($trigger_invert{$q}) {
$inverse = 1; # - An inversion trigger
} elsif (!$trigger_filler{$q}) { # - A filler word for more natural querying
if ($q =~ /(?:^[a-z]+\s)+/) {
$filler_count = $filler_count + 1;
} else {
$color = $q; # - A presumed color
}
}
}
return unless $color; # Need a color to continue!
$color =~ s/\sto\s//;
return if $filler_count;
my $alpha = "1";
$color =~ s/(,\s*|\s+)/,/g; # Spaces to commas for things like "hsl 194 0.53 0.79"
if ($color =~ s/#?([0-9a-f]{3,6})$/$1/) { # Color looks like a hex code, strip the leading #
$color = join('', map { $_ . $_ } (split '', $color)) if (length($color) == 3); # Make three char hex into six chars by repeating each in turn
$type = 'rgb8';
} elsif ($color =~ s/([0-9]+,[0-9]+,[0-9]+),([0]?\.[0-9]+)/$alpha = $2; $1/e) { #hack rgba into rgb and extract alpha
my $inverse = 0;
my $type = 'rgb8';
s/\sto\s(?:$typestr)?//g;
$_ =~ $trigger_and_guard;
$type = lc $+{'type'} if defined $+{'type'} and exists $types{lc $+{'type'}};
$color = "$+{'r'} $+{'g'} $+{'b'}" if defined $+{'r'} and defined $+{'g'} and defined $+{'b'};
$color = lc $+{'color'} if defined $+{'color'};
$inverse = 1 if defined $+{'inv'};
$color =~ s/,?\s+/,/g;
$color =~ s/([0-9]+,[0-9]+,[0-9]+),([0]?\.[0-9]+)/$alpha = $2; $1/e;
if ($color =~ s/#?([0-9a-f]{3,6})$/$1/) {
$color = join('', map { $_ . $_ } (split '', $color)) if (length($color) == 3);
$type = 'rgb8';
} else {
try {
# See if we can find the color in one of our dictionaries.
$color = join(',', Convert::Color::Library->new($color_dictionaries . '/' . $color)->as_rgb8->hex);
$type = 'rgb8'; # We asked for rgb8 from our dictionary, so make sure our type matches.
$type = 'rgb8';
};
}
my $col = try { Convert::Color->new("$type:$color") }; # Everything should be ready for conversion now.
return unless $col; # Guess not.
my $col = try { Convert::Color->new("$type:$color") };
return unless $col;
if ($inverse) { # We triggered on the inverse, so do the flip.
if ($inverse) {
my $orig_rgb = $col->as_rgb8;
$col = Convert::Color::RGB8->new(255 - $orig_rgb->red, 255 - $orig_rgb->green, 255 - $orig_rgb->blue);
}
@ -138,13 +121,9 @@ handle matches => sub {
$complementary = uc($complementary);
#greyscale colours have no hue and saturation
my $show_column_2 = !($hsl[0] eq 0 && $hsl[1] eq '0%');
my $hide_column_2 = ($hsl[0] eq 0 && $hsl[1] eq '0%');
my $column_2 = '';
if ($show_column_2) {
$column_2 = "\n" . "Complementary: #$complementary" . "\n" . "Analogous: #$analogous[0], #$analogous[1]";
}
my $column_2 = $hide_column_2 ? "" : "\nComplementary: #$complementary\nAnalogous: #$analogous[0], #$analogous[1]";
return "$hexc ~ $rgb ~ $rgb_pct ~ $hslc ~ $cmyb$column_2",
structured_answer => {
@ -154,7 +133,7 @@ handle matches => sub {
rgb => $rgb,
hslc => $hslc,
cmyb => $cmyb,
show_column_2 => $show_column_2,
show_column_2 => !$hide_column_2,
analogous => \@analogous,
complementary => $complementary,
},

View File

@ -9,7 +9,6 @@ use DDG::Test::Goodie;
zci answer_type => 'color_code';
zci is_cached => 1;
my $green_answer = 'Hex: #00FF00 ~ RGBA(0, 255, 0, 1) ~ RGB(0%, 100%, 0%) ~ HSL(120, 100%, 50%) ~ CMYB(100%, 0%, 100%, 0%)'."\n".'Complementary: #FF00FF'."\n".'Analogous: #00FF80, #80FF00';
my %basic_answer = (
@ -48,7 +47,7 @@ sub green_args {
ddg_goodie_test(
[qw(DDG::Goodie::ColorCodes)],
[qw(DDG::Goodie::ColorCodes)],
'hex color code for cyan' => test_zci(
'Hex: #00FFFF ~ RGBA(0, 255, 255, 1) ~ RGB(0%, 100%, 100%) ~ HSL(180, 100%, 50%) ~ CMYB(100%, 0%, 0%, 0%)'."\n".'Complementary: #FF0000'."\n".'Analogous: #0080FF, #00FF80',
%basic_answer
@ -77,6 +76,10 @@ ddg_goodie_test(
'Hex: #202428 ~ RGBA(32, 36, 40, 1) ~ RGB(13%, 14%, 16%) ~ HSL(210, 11%, 14%) ~ CMYB(20%, 10%, 0%, 84%)'."\n".'Complementary: #292521'."\n".'Analogous: #212129, #212929',
%basic_answer
),
'bluishblack html' => test_zci(
'Hex: #202428 ~ RGBA(32, 36, 40, 1) ~ RGB(13%, 14%, 16%) ~ HSL(210, 11%, 14%) ~ CMYB(20%, 10%, 0%, 84%)'."\n".'Complementary: #292521'."\n".'Analogous: #212129, #212929',
%basic_answer
),
# Single full HTML check.
'red html code' => test_zci(
'Hex: #FF0000 ~ RGBA(255, 0, 0, 1) ~ RGB(100%, 0%, 0%) ~ HSL(0, 100%, 50%) ~ CMYB(0%, 100%, 100%, 0%)'."\n".'Complementary: #00FFFF'."\n".'Analogous: #FF8000, #FF0080',
@ -86,18 +89,26 @@ ddg_goodie_test(
'Hex: #633CB0 ~ RGBA(99, 60, 176, 0.5) ~ RGB(39%, 24%, 69%) ~ HSL(260, 49%, 46%) ~ CMYB(44%, 66%, 0%, 31%)'."\n".'Complementary: #89B03C'."\n".'Analogous: #9D3CB0, #3C4FB0',
%basic_answer
),
'#dc5f3c' => test_zci(
'Hex: #DC5F3C ~ RGBA(220, 95, 60, 1) ~ RGB(86%, 37%, 24%) ~ HSL(13, 70%, 55%) ~ CMYB(0%, 57%, 73%, 14%)'."\n".'Complementary: #3BB9DB'."\n".'Analogous: #DBAE3B, #DB3B69',
'#dc5f3c' => test_zci(
'Hex: #DC5F3C ~ RGBA(220, 95, 60, 1) ~ RGB(86%, 37%, 24%) ~ HSL(13, 70%, 55%) ~ CMYB(0%, 57%, 73%, 14%)'."\n".'Complementary: #3BB9DB'."\n".'Analogous: #DBAE3B, #DB3B69',
%basic_answer
),
#Colours with no hue shouldn't have complements or analogs
),
#Colours with no hue shouldn't have complements or analogs
'#000000' => test_zci(
'Hex: #000000 ~ RGBA(0, 0, 0, 1) ~ RGB(0%, 0%, 0%) ~ HSL(0, 0%, 0%) ~ CMYB(0%, 0%, 0%, 100%)',
%basic_answer
),
'#FFFFFF' => test_zci(
'#FFFFFF' => test_zci(
'Hex: #FFFFFF ~ RGBA(255, 255, 255, 1) ~ RGB(100%, 100%, 100%) ~ HSL(0, 0%, 100%) ~ CMYB(0%, 0%, 0%, 0%)',
%basic_answer
%basic_answer
),
'red: 255 green: 255 blue: 255' => test_zci(
'Hex: #FFFFFF ~ RGBA(255, 255, 255, 1) ~ RGB(100%, 100%, 100%) ~ HSL(0, 0%, 100%) ~ CMYB(0%, 0%, 0%, 0%)',
%basic_answer
),
'red: 99 green: 60 blue: 176' => test_zci(
'Hex: #633CB0 ~ RGBA(99, 60, 176, 1) ~ RGB(39%, 24%, 69%) ~ HSL(260, 49%, 46%) ~ CMYB(44%, 66%, 0%, 31%)'."\n".'Complementary: #89B03C'."\n".'Analogous: #9D3CB0, #3C4FB0',
%basic_answer
),
# Check the content of the structured answer. Just once.
'hsl 194 0.53 0.79' => test_zci(
@ -106,13 +117,14 @@ ddg_goodie_test(
),
# Queries to ignore.
'bluishblack html' => undef,
'HTML email' => undef,
'wield color' => undef,
'whats the symbolism of the color red' => undef,
'red color porsche 911' => undef,
'yoda lightsaber green color' => undef,
'product red color iphone' => undef,
'w3 html5.1' => undef,
'w3c html5.1' => undef
);
done_testing;