193 lines
6.8 KiB
Perl
193 lines
6.8 KiB
Perl
package DDG::Goodie::ResistorColors;
|
||
# ABSTRACT: Convert a resistor value into color codes.
|
||
# e.g. "4.7K ohms" -> "yellow purple black brown"
|
||
|
||
# Ideas for future improvements
|
||
# - reverse query (e.g. "yellow purple black brown" -> "4.7K ohms"
|
||
# - show 4 and 5 band markings
|
||
# - show surface mount resistor markings
|
||
# - detect if query contains tolerance (e.g. 10%) and use appropriate color
|
||
|
||
use strict;
|
||
use DDG::Goodie;
|
||
use Math::Round;
|
||
use POSIX qw(abs floor log10 pow);
|
||
use Lingua::Any::Numbers qw(:std);
|
||
use utf8;
|
||
|
||
# \x{2126} is the unicode ohm symbol
|
||
triggers query_nowhitespace => qr/^(\d+[\.kmKM]?\d*[kmKM]?)((ohm|ohms|\x{2126})|resistor)+$/i;
|
||
|
||
zci is_cached => 1;
|
||
zci answer_type => 'resistor_colors';
|
||
|
||
# These hex codes came from
|
||
# http://en.wikipedia.org/wiki/Electronic_color_code
|
||
my %digits_to_colors = (
|
||
-2 => { hex => '#c0c0c0', label => '#000', name => 'silver', multiplier => '0.01' , tolerance => '10%' },
|
||
-1 => { hex => '#cfb53b', label => '#000', name => 'gold' , multiplier => '0.1' , tolerance => '5%' },
|
||
0 => { hex => '#000000', label => '#fff', name => 'black' , multiplier => '1' , tolerance => undef },
|
||
1 => { hex => '#964b00', label => '#fff', name => 'brown' , multiplier => '10' , tolerance => '1%' },
|
||
2 => { hex => '#ff0000', label => '#fff', name => 'red' , multiplier => '100' , tolerance => '2%' },
|
||
3 => { hex => '#ffa500', label => '#000', name => 'orange', multiplier => '1K' , tolerance => undef },
|
||
4 => { hex => '#ffff00', label => '#000', name => 'yellow', multiplier => '10K' , tolerance => undef },
|
||
5 => { hex => '#9acd32', label => '#000', name => 'green' , multiplier => '100K' , tolerance => '0.5%' },
|
||
6 => { hex => '#6495ed', label => '#000', name => 'blue' , multiplier => '1M' , tolerance => '0.25%' },
|
||
7 => { hex => '#ee82ee', label => '#000', name => 'purple', multiplier => '10M' , tolerance => '0.1%' },
|
||
8 => { hex => '#a0a0a0', label => '#000', name => 'gray' , multiplier => '100M' , tolerance => '0.05%' },
|
||
9 => { hex => '#ffffff', label => '#000', name => 'white' , multiplier => '1000M', tolerance => undef },
|
||
);
|
||
|
||
my $default_tolerance = -1; # 5% / gold
|
||
|
||
handle matches => sub {
|
||
my $input = shift;
|
||
my $value = parse_value($input);
|
||
if (defined $value && ($value == 0 || ($value <= 99900000000 && $value >= 1))) {
|
||
$value = round_to_significant_places($value, 2);
|
||
my $tolerance = $default_tolerance; # Currently always 5%.
|
||
my @digits = number_to_color_digits($value, $tolerance);
|
||
return render($value, \@digits);
|
||
}
|
||
return;
|
||
};
|
||
|
||
# Given ohm rating as string (e.g. 3.2m or 3M2), return
|
||
# integer value (e.g. 3200000).
|
||
sub parse_value {
|
||
my $input = shift;
|
||
if ($input =~ m/^(\d+)(\.(\d+))?([km])?$/i) {
|
||
# e.g. 123, 1.23, 1M, 1.23M
|
||
my $multiplier = 1;
|
||
if ($4) {
|
||
$multiplier = 1000 if $4 eq 'k' || $4 eq 'K';
|
||
$multiplier = 1000000 if $4 eq 'm' || $4 eq 'M';
|
||
}
|
||
return ("$1." . ($3 || 0)) * $multiplier;
|
||
} elsif ($input =~ m/^(\d+)([km])(\d+)$/i) {
|
||
# e.g. 12K3
|
||
my $multiplier = 1;
|
||
$multiplier = 1000 if $2 eq 'k' || $2 eq 'K';
|
||
$multiplier = 1000000 if $2 eq 'm' || $2 eq 'M';
|
||
return ("$1." . ($3 || 0)) * $multiplier;
|
||
} else {
|
||
return;
|
||
}
|
||
};
|
||
|
||
# Round a value to significant places.
|
||
# e.g. (123456789, 3) -> 123000000)
|
||
# (0.0045678, 3) -> 0.00457)
|
||
sub round_to_significant_places {
|
||
my $value = shift;
|
||
my $significant = shift;
|
||
if ($value == 0) {
|
||
return 0;
|
||
}
|
||
return nearest(pow(10, int(floor(log10(abs($value))) - ($significant - 1))), $value);
|
||
}
|
||
|
||
# Given ohm rating as integer, and tolerance digit (e.g. 470000, -1), return
|
||
# array of color digits (e.g. 4, 7, 0, 3, -1). See %digits_to_colors.
|
||
sub number_to_color_digits {
|
||
my ($value, $tolerance) = @_;
|
||
return (0, 0, 0, $tolerance) if $value == 0; # special case
|
||
my @value_digits = split(//, $value * 100);
|
||
return (
|
||
$value_digits[0] || 0,
|
||
$value_digits[1] || 0,
|
||
scalar(@value_digits) - 4,
|
||
$tolerance);
|
||
};
|
||
|
||
# Given a numeric value, format it like '3.2M' etc.
|
||
sub format_value {
|
||
my $value = shift;
|
||
if ($value >= 1000000) {
|
||
return ($value / 1000000) . 'M';
|
||
} elsif ($value >= 1000) {
|
||
return ($value / 1000) . 'K';
|
||
} else {
|
||
return $value;
|
||
}
|
||
};
|
||
|
||
# Given array of color digits (e.g. 3, 3, 1, 4),
|
||
# return text and html representation of colors.
|
||
sub render {
|
||
my ($value, $digits) = @_;
|
||
my $formatted_value = format_value($value);
|
||
my $ohms = $formatted_value eq '1' ? 'ohm' : 'ohms';
|
||
my $text = "$formatted_value\x{2126}";
|
||
my $bands = ucfirst to_string(scalar @$digits);
|
||
|
||
my $title = $text;
|
||
$text .= " ($ohms) resistor colors:";
|
||
|
||
#while (my ($index, $digit) = each @$digits) {
|
||
my @resistor_bands;
|
||
my $index = 0;
|
||
foreach my $digit (@$digits) {
|
||
if (exists $digits_to_colors{$digit}) {
|
||
my $class = $digits_to_colors{$digit}{name};
|
||
my $name = ucfirst $class;
|
||
my $hex = $digits_to_colors{$digit}{hex};
|
||
my $label = $digits_to_colors{$digit}{label};
|
||
my ($text_prefix, $html_prefix, $display_digit);
|
||
if ($index == scalar(@$digits) - 2) {
|
||
# multiplier digit
|
||
$text_prefix = "\x{00D7}";
|
||
$html_prefix = '×';
|
||
$display_digit = $digits_to_colors{$digit}{multiplier};
|
||
} elsif ($index == scalar(@$digits) - 1) {
|
||
# tolerance digit
|
||
$text_prefix = "\x{00B1}";
|
||
$html_prefix = '±';
|
||
$display_digit = $digits_to_colors{$digit}{tolerance};
|
||
} else {
|
||
# numeric digits
|
||
$text_prefix = '';
|
||
$html_prefix = '';
|
||
$display_digit = $digit;
|
||
}
|
||
$text .= " $class ($text_prefix$display_digit)";
|
||
if ($index != scalar(@$digits - 1)) {
|
||
$text .= ','; # Comma delimit all but last
|
||
}
|
||
|
||
push (@resistor_bands, {
|
||
class => $class,
|
||
html_prefix => $html_prefix,
|
||
display_digit => $display_digit
|
||
});
|
||
|
||
} else {
|
||
return;
|
||
}
|
||
$index++;
|
||
}
|
||
|
||
return $text,
|
||
structured_answer => {
|
||
meta => {
|
||
sourceName => "resisto.rs",
|
||
sourceUrl => "http://resisto.rs/#$formatted_value"
|
||
},
|
||
data => {
|
||
title => $title,
|
||
subtitle => $bands . ' Bands',
|
||
resistor_bands => \@resistor_bands
|
||
},
|
||
templates => {
|
||
group => 'text',
|
||
item => 0,
|
||
options => {
|
||
content => 'DDH.resistor_colors.content',
|
||
moreAt => 1
|
||
}
|
||
}
|
||
};
|
||
};
|
||
|
||
1;
|