Created
April 5, 2016 14:18
-
-
Save joxl/138b0132cde3e641ed97808ee95adc20 to your computer and use it in GitHub Desktop.
Python utility for generating HSB gradient color samples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import argparse | |
import colorsys | |
HTML_TEMPLATE = """<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
body { | |
background: linear-gradient(to right, %s); | |
} | |
</style> | |
</head> | |
<body> | |
</body> | |
<html>""" | |
class Color(object): | |
VALID = set("0123456789abcdef") | |
def __init__(self, hex_str): | |
hex_str = hex_str.lower() | |
if len(hex_str) != 6 or not set(hex_str).issubset(self.VALID): | |
raise ValueError(hex_str) | |
self._hex_str = hex_str | |
@classmethod | |
def from_hsv(cls, hue, sat, val): | |
def perc2byte(perc): | |
return int(round(perc * 255, 0)) | |
r, g, b = colorsys.hsv_to_rgb(hue, sat, val) | |
hex_str = "%02x%02x%02x" % (perc2byte(r), perc2byte(g), perc2byte(b)) | |
return cls(hex_str) | |
@property | |
def hex_str(self): | |
return self._hex_str | |
@property | |
def rgb(self): | |
def hex2color(hexval): | |
return int(hexval, 16) / 255.0 | |
red = hex2color(self.hex_str[0:2]) | |
green = hex2color(self.hex_str[2:4]) | |
blue = hex2color(self.hex_str[4:6]) | |
return red, green, blue | |
@property | |
def hsv(self): | |
return colorsys.rgb_to_hsv(*self.rgb) | |
def __repr__(self): | |
return "<Color hex='%s'>" % (self.hex_str,) | |
def gradient(src, dst, samples, reverse=False): | |
assert samples >= 2, "Must have at least 2 samples" | |
def ilerp(it1, it2): | |
step = (it2 - it1) / (samples - 1) | |
for index in xrange(samples): | |
yield it1 + (step * index) | |
def ilerp_hue(hue1, hue2, reverse): | |
if not reverse and hue1 > hue2: | |
# wrap foward (e.g 300..360..0..50) | |
for hue in ilerp(hue1, hue2 + 1.0): | |
if hue <= 1.0: | |
yield hue | |
else: | |
yield hue - 1.0 | |
elif reverse and hue1 < hue2: | |
# wrap backward (e.g 50..0..360..300) | |
for hue in ilerp(hue1, hue2 - 1.0): | |
if hue >= 0.0: | |
yield hue | |
else: | |
yield hue + 1.0 | |
else: | |
for hue in ilerp(hue1, hue2): | |
yield hue | |
hue1, sat1, val1 = src.hsv | |
hue2, sat2, val2 = dst.hsv | |
return map(Color.from_hsv, | |
ilerp_hue(hue1, hue2, reverse), | |
ilerp(sat1, sat2), | |
ilerp(val1, val2), | |
) | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument("source", metavar="SRC_HEX", | |
help="source color (in hex format, e.g. FF0000)") | |
parser.add_argument("dest", metavar="DST_HEX", | |
help="destination color (in hex format, e.g. 0000FF)") | |
parser.add_argument("-s", "--samples", metavar="N", default=10, type=int, | |
help="generate %(metavar)s gradient color samples (default=%(default)s)") | |
parser.add_argument("-r", "--reverse", action="store_true", default=False, | |
help="perform reverse gradient in the hue spectrum") | |
parser.add_argument("--html", action="store_true", default=False, | |
help="Output HTML code for viewing gradient") | |
opts = parser.parse_args() | |
# validate args | |
if opts.samples < 2: | |
parser.error("Must have at least 2 samples") | |
try: | |
src = Color(opts.source) | |
dst = Color(opts.dest) | |
except ValueError: | |
parser.error("Invalid source/destination colors (%s/%s)" % (opts.source, | |
opts.dest)) | |
# calculate gradient colors | |
colors = gradient(src, dst, opts.samples, opts.reverse) | |
# output results | |
if opts.html: | |
# in html | |
css = [] | |
for color in colors: | |
css.append("#%s" % (color.hex_str,)) | |
print(HTML_TEMPLATE % (",".join(css),)) | |
else: | |
# just raw color hex strings | |
for color in colors: | |
print(color.hex_str) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment