Skip to content
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

Uniform color distribution #35

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 53 additions & 52 deletions data_highlight/support/color_picker.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,53 @@
import random
import colorsys


def color_for(hash_code, color_dict):
"""
Get a color for a specific 'hash code' by either taking one we've already recorded for this hash code,
or generating a new random one.
"""
# do we have it already?
if hash_code in color_dict:
# yes, job done
return color_dict[hash_code]
else:
# no, generate one
hue = random.random()
sat = 0.9 + random.random() * 0.1
rgb = colorsys.hsv_to_rgb(hue, sat, 0.9)
r = int(rgb[0] * 255)
g = int(rgb[1] * 255)
b = int(rgb[2] * 255)
new_col = (r, g, b)
# store the new color
color_dict[hash_code] = new_col
return new_col


def hex_color_for(rgb):
"""
Convert a 3-element rgb structure to a HTML color definition
"""
opacity_shade = 0.3
return "rgba(%d,%d,%d,%f)" % (rgb[0], rgb[1], rgb[2], opacity_shade)


def mean_color_for(color_arr):
"""
find the mean of the provided colors

Args:
color_arr: three-element list of R, G and B components of color
"""
r = 0
g = 0
b = 0
for color in color_arr:
r += color[0]
g += color[1]
b += color[2]

arr_len = len(color_arr)
return int(r / arr_len), int(g / arr_len), int(b / arr_len)
import random
import colorsys


def color_for(hash_code, color_dict, total_colors):
"""
Get a color for a specific 'hash code' by either taking one we've already recorded for this hash code,
or generating a new one at regular intervals in the HSV color model.
"""
# do we have it already?
if hash_code in color_dict:
# yes, job done
return color_dict[hash_code]
else:
# no, generate one
hue = (len(color_dict) / total_colors) % 1.0
sat = 0.9
val = 0.9
rgb = colorsys.hsv_to_rgb(hue, sat, val)
r = int(rgb[0] * 255)
g = int(rgb[1] * 255)
b = int(rgb[2] * 255)
new_col = (r, g, b)
# store the new color
color_dict[hash_code] = new_col
return new_col


def hex_color_for(rgb):
"""
Convert a 3-element rgb structure to a HTML color definition
"""
opacity_shade = 0.3
return "rgba(%d,%d,%d,%f)" % (rgb[0], rgb[1], rgb[2], opacity_shade)


def mean_color_for(color_arr):
"""
find the mean of the provided colors

Args:
color_arr: three-element list of R, G and B components of color
"""
r = 0
g = 0
b = 0
for color in color_arr:
r += color[0]
g += color[1]
b += color[2]

arr_len = len(color_arr)
return int(r / arr_len), int(g / arr_len), int(b / arr_len)
114 changes: 63 additions & 51 deletions test/test_Colors.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,63 @@
import unittest
from data_highlight.support.color_picker import color_for, hex_color_for, mean_color_for


class ColorTests(unittest.TestCase):

############################
#### setup and teardown ####
############################

def setUp(self):
pass

def tearDown(self):
pass

####################
#### file tests ####
####################

def test_ColorFor(self):
color_dict = {}
color1 = color_for("aaa", color_dict)
assert color1 is not None
self.assertEqual(1, len(color_dict))

color2 = color_for("bbb", color_dict)
assert color2 is not None
self.assertEqual(2, len(color_dict))
self.assertNotEqual(color1, color2)

color3 = color_for("aaa", color_dict)
self.assertEqual(2, len(color_dict), "Should not have created new dict entry")
self.assertEqual(color1, color3)

def test_HexConversion(self):
red = (255, 0, 0)
self.assertEqual("rgba(255,0,0,0.300000)", hex_color_for(red))

def test_MeanColor(self):
color1 = (100, 50, 200)
color2 = (50, 0, 150)
color3 = (150, 100, 250)

self.assertEqual((75, 25, 175), mean_color_for((color1, color2)))
self.assertEqual((100, 50, 200), mean_color_for((color3, color2)))
self.assertEqual((100, 50, 200), mean_color_for((color1, color2, color3)))


if __name__ == "__main__":
unittest.main()
import unittest
from data_highlight.support.color_picker import color_for, hex_color_for, mean_color_for


class ColorTests(unittest.TestCase):

############################
#### setup and teardown ####
############################

def setUp(self):
pass

def tearDown(self):
pass

#####################
#### color tests ####
#####################

def test_ColorFor(self):
color_dict = {}
color1 = color_for("aaa", color_dict, 10)
assert color1 is not None
self.assertEqual(1, len(color_dict))

color2 = color_for("bbb", color_dict, 10)
assert color2 is not None
self.assertEqual(2, len(color_dict))
self.assertNotEqual(color1, color2)

color3 = color_for("aaa", color_dict, 10)
self.assertEqual(2, len(color_dict), "Should not have created new dict entry")
self.assertEqual(color1, color3)

def test_HexConversion(self):
red = (255, 0, 0)
self.assertEqual("rgba(255,0,0,0.300000)", hex_color_for(red))

def test_MeanColor(self):
color1 = (100, 50, 200)
color2 = (50, 0, 150)
color3 = (150, 100, 250)

self.assertEqual((75, 25, 175), mean_color_for((color1, color2)))
self.assertEqual((100, 50, 200), mean_color_for((color3, color2)))
self.assertEqual((100, 50, 200), mean_color_for((color1, color2, color3)))

def test_UniformColorDistribution(self):
color_dict = {}
total_colors = 10
colors = [color_for(str(i), color_dict, total_colors) for i in range(total_colors)]
self.assertEqual(total_colors, len(set(colors)), "Colors are not uniformly distributed")

for i in range(total_colors - 1):
color1 = colors[i]
color2 = colors[i + 1]
contrast = abs(color1[0] - color2[0]) + abs(color1[1] - color2[1]) + abs(color1[2] - color2[2])
self.assertGreater(contrast, 50, "Adjacent colors do not have sufficient contrast")


if __name__ == "__main__":
unittest.main()