New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resistor color codes! #133

Closed
wants to merge 9 commits into
base: master
from
Copy path View file
@@ -0,0 +1,174 @@
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 DDG::Goodie;
use Math::Round;
use POSIX qw(abs floor log10 pow);
# \x{2126} is the unicode ohm symbol
triggers query_nowhitespace => qr/^(.*)(ohm|ohms|\x{2126})/i;
zci is_cached => 1;
zci answer_type => 'resistor_colors';
primary_example_queries '4.7k ohm';
secondary_example_queries '1\x{2126}';
description 'find resistor color bands';
name 'ResistorColors';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ResistorColors.pm';
category 'conversions';
topics 'science';
attribution twitter => 'joewalnes',
web => ['http://joewalnes.com', 'joewalnes.com'],
email => ['joe@walnes.com', 'Joe Walnes'];
# 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} ($ohms) resistor colors:";
my $html = "$formatted_value&#x2126; ($ohms) resistor colors:";
while (my ($index, $digit) = each @$digits) {
if (exists $digits_to_colors{$digit}) {
my $name = $digits_to_colors{$digit}{name};
my $hex = $digits_to_colors{$digit}{hex};
my $label = $digits_to_colors{$digit}{label};
my $style = "display:inline-block;background-color:$hex;color:$label;"
. "border:1px solid #c8c8c8;margin-top:-1px;padding:0px 4px;"
. "border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;";
my ($text_prefix, $html_prefix, $display_digit);
if ($index == scalar(@$digits) - 2) {
# multiplier digit
$text_prefix = "\x{00D7}";
$html_prefix = '&times;';
$display_digit = $digits_to_colors{$digit}{multiplier};
} elsif ($index == scalar(@$digits) - 1) {
# tolerance digit
$text_prefix = "\x{00B1}";
$html_prefix = '&plusmn;';
$display_digit = $digits_to_colors{$digit}{tolerance};
} else {
# numeric digits
$text_prefix = '';
$html_prefix = '';
$display_digit = $digit;
}
$text .= " $name ($text_prefix$display_digit)";
if ($index != scalar(@$digits - 1)) {
$text .= ','; # Comma delimit all but last
}
$html .= " <span style='$style'>$name ($html_prefix$display_digit)</span>";
} else {
return;
}
}
$html .= "<br/>"
. "<a href='http://resisto.rs/#$formatted_value' style='font-size:92.8%'>"
. "More at resisto.rs</a>";
return $text, html => $html;
};
1;
Copy path View file
@@ -0,0 +1,101 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use DDG::Test::Goodie;
zci answer_type => "resistor_colors";
zci is_cached => 1;
ddg_goodie_test(
[qw(
DDG::Goodie::ResistorColors
)],
# Check trigger kicks in.
"330 ohms" => test_zci("330\x{2126} (ohms) resistor colors: orange (3), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"330 ohm" => test_zci("330\x{2126} (ohms) resistor colors: orange (3), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"330 \x{2126}" => test_zci("330\x{2126} (ohms) resistor colors: orange (3), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"330ohms" => test_zci("330\x{2126} (ohms) resistor colors: orange (3), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"330ohm" => test_zci("330\x{2126} (ohms) resistor colors: orange (3), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"330\x{2126}" => test_zci("330\x{2126} (ohms) resistor colors: orange (3), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
# Various multipliers
"472000 ohms" => test_zci("470K\x{2126} (ohms) resistor colors: yellow (4), purple (7), yellow (\x{00D7}10K), gold (\x{00B1}5%)", html => qr/./),
"400000 ohms" => test_zci("400K\x{2126} (ohms) resistor colors: yellow (4), black (0), yellow (\x{00D7}10K), gold (\x{00B1}5%)", html => qr/./),
"12300 ohms" => test_zci("12K\x{2126} (ohms) resistor colors: brown (1), red (2), orange (\x{00D7}1K), gold (\x{00B1}5%)", html => qr/./),
# Rounding
"1.2345 ohms" => test_zci("1.2\x{2126} (ohms) resistor colors: brown (1), red (2), gold (\x{00D7}0.1), gold (\x{00B1}5%)", html => qr/./),
"1.2555 ohms" => test_zci("1.3\x{2126} (ohms) resistor colors: brown (1), orange (3), gold (\x{00D7}0.1), gold (\x{00B1}5%)", html => qr/./),
"12.345 ohms" => test_zci("12\x{2126} (ohms) resistor colors: brown (1), red (2), black (\x{00D7}1), gold (\x{00B1}5%)", html => qr/./),
"12.555 ohms" => test_zci("13\x{2126} (ohms) resistor colors: brown (1), orange (3), black (\x{00D7}1), gold (\x{00B1}5%)", html => qr/./),
"123.45 ohms" => test_zci("120\x{2126} (ohms) resistor colors: brown (1), red (2), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"125.55 ohms" => test_zci("130\x{2126} (ohms) resistor colors: brown (1), orange (3), brown (\x{00D7}10), gold (\x{00B1}5%)", html => qr/./),
"1234.5 ohms" => test_zci("1.2K\x{2126} (ohms) resistor colors: brown (1), red (2), red (\x{00D7}100), gold (\x{00B1}5%)", html => qr/./),
"1255.5 ohms" => test_zci("1.3K\x{2126} (ohms) resistor colors: brown (1), orange (3), red (\x{00D7}100), gold (\x{00B1}5%)", html => qr/./),
"12345 ohms" => test_zci("12K\x{2126} (ohms) resistor colors: brown (1), red (2), orange (\x{00D7}1K), gold (\x{00B1}5%)", html => qr/./),
"12555 ohms" => test_zci("13K\x{2126} (ohms) resistor colors: brown (1), orange (3), orange (\x{00D7}1K), gold (\x{00B1}5%)", html => qr/./),
"123450 ohms" => test_zci("120K\x{2126} (ohms) resistor colors: brown (1), red (2), yellow (\x{00D7}10K), gold (\x{00B1}5%)", html => qr/./),
"125550 ohms" => test_zci("130K\x{2126} (ohms) resistor colors: brown (1), orange (3), yellow (\x{00D7}10K), gold (\x{00B1}5%)", html => qr/./),
"1234500 ohms" => test_zci("1.2M\x{2126} (ohms) resistor colors: brown (1), red (2), green (\x{00D7}100K), gold (\x{00B1}5%)", html => qr/./),
"1255500 ohms" => test_zci("1.3M\x{2126} (ohms) resistor colors: brown (1), orange (3), green (\x{00D7}100K), gold (\x{00B1}5%)", html => qr/./),
"12345000 ohms" => test_zci("12M\x{2126} (ohms) resistor colors: brown (1), red (2), blue (\x{00D7}1M), gold (\x{00B1}5%)", html => qr/./),
"12555000 ohms" => test_zci("13M\x{2126} (ohms) resistor colors: brown (1), orange (3), blue (\x{00D7}1M), gold (\x{00B1}5%)", html => qr/./),
"123450000 ohms" => test_zci("120M\x{2126} (ohms) resistor colors: brown (1), red (2), purple (\x{00D7}10M), gold (\x{00B1}5%)", html => qr/./),
"125550000 ohms" => test_zci("130M\x{2126} (ohms) resistor colors: brown (1), orange (3), purple (\x{00D7}10M), gold (\x{00B1}5%)", html => qr/./),
"1234500000 ohms" => test_zci("1200M\x{2126} (ohms) resistor colors: brown (1), red (2), gray (\x{00D7}100M), gold (\x{00B1}5%)", html => qr/./),
"1255500000 ohms" => test_zci("1300M\x{2126} (ohms) resistor colors: brown (1), orange (3), gray (\x{00D7}100M), gold (\x{00B1}5%)", html => qr/./),
# kilo and mega multipliers
"27kohm" => test_zci("27K\x{2126} (ohms) resistor colors: red (2), purple (7), orange (\x{00D7}1K), gold (\x{00B1}5%)", html => qr/./),
"27Kohm" => test_zci("27K\x{2126} (ohms) resistor colors: red (2), purple (7), orange (\x{00D7}1K), gold (\x{00B1}5%)", html => qr/./),
"27 K ohm" => test_zci("27K\x{2126} (ohms) resistor colors: red (2), purple (7), orange (\x{00D7}1K), gold (\x{00B1}5%)", html => qr/./),
"4K2 ohm" => test_zci("4.2K\x{2126} (ohms) resistor colors: yellow (4), red (2), red (\x{00D7}100), gold (\x{00B1}5%)", html => qr/./),
"4.2K ohm" => test_zci("4.2K\x{2126} (ohms) resistor colors: yellow (4), red (2), red (\x{00D7}100), gold (\x{00B1}5%)", html => qr/./),
# Decimal points
"2.9ohm" => test_zci("2.9\x{2126} (ohms) resistor colors: red (2), white (9), gold (\x{00D7}0.1), gold (\x{00B1}5%)", html => qr/./),
# Negative multipliers
"1 ohm" => test_zci("1\x{2126} (ohm) resistor colors: brown (1), black (0), gold (\x{00D7}0.1), gold (\x{00B1}5%)", html => qr/./),
"29 ohms" => test_zci("29\x{2126} (ohms) resistor colors: red (2), white (9), black (\x{00D7}1), gold (\x{00B1}5%)", html => qr/./),
# Zero special case
"0 ohms" => test_zci("0\x{2126} (ohms) resistor colors: black (0), black (0), black (\x{00D7}1), gold (\x{00B1}5%)", html => qr/./),
# Range
"99000M ohms" => test_zci("99000M\x{2126} (ohms) resistor colors: white (9), white (9), white (\x{00D7}1000M), gold (\x{00B1}5%)", html => qr/./),
"99500M ohms" => undef,
"1.1 ohms" => test_zci("1.1\x{2126} (ohms) resistor colors: brown (1), brown (1), gold (\x{00D7}0.1), gold (\x{00B1}5%)", html => qr/./),
"1 ohms" => test_zci("1\x{2126} (ohm) resistor colors: brown (1), black (0), gold (\x{00D7}0.1), gold (\x{00B1}5%)", html => qr/./),
"0.9 ohms" => undef,
"-10 ohms" => undef,
# Don't trigger
"343.3.3.3 ohms" => undef,
"chicken ohms" => undef,
"what is ohms law" => undef,
"ohm ma darling" => undef,
# Check the HTML. Just once.
"1.5m ohm" => test_zci("1.5M\x{2126} (ohms) resistor colors: brown (1), green (5), green (\x{00D7}100K), gold (\x{00B1}5%)", html =>
"1.5M&#x2126; (ohms) resistor colors: "
. "<span style='display:inline-block;background-color:#964b00;"
. "color:#fff;border:1px solid #c8c8c8;margin-top:-1px;padding:0px 4px;border-radius:4px;"
. "-webkit-border-radius:4px;-moz-border-radius:4px;'>brown (1)</span> "
. "<span style='display:inline-block;background-color:#9acd32;"
. "color:#000;border:1px solid #c8c8c8;margin-top:-1px;padding:0px 4px;border-radius:4px;"
. "-webkit-border-radius:4px;-moz-border-radius:4px;'>green (5)</span> "
. "<span style='display:inline-block;background-color:#9acd32;"
. "color:#000;border:1px solid #c8c8c8;margin-top:-1px;padding:0px 4px;border-radius:4px;"
. "-webkit-border-radius:4px;-moz-border-radius:4px;'>green (&times;100K)</span> "
. "<span style='display:inline-block;background-color:#cfb53b;"
. "color:#000;border:1px solid #c8c8c8;margin-top:-1px;padding:0px 4px;border-radius:4px;"
. "-webkit-border-radius:4px;-moz-border-radius:4px;'>gold (&plusmn;5%)</span>"
. "<br/><a href='http://resisto.rs/#1.5M' style='font-size:92.8%'>More at resisto.rs</a>"),
);
done_testing;
ProTip! Use n and p to navigate between commits in a pull request.