<jupyter_start><jupyter_text>Table of Contents1&nbsp;&nbsp;Motivation1.1&nbsp;&nbsp;Shades of HERE Aqua and HERE Gray2&nbsp;&nbsp;Discover Ipyvolume3&nbsp;&nbsp;Convert Hex to RGB Colors4&nbsp;&nbsp;Plot Color Schemes4.1&nbsp;&nbsp;All 'Scraped' Colors4.2&nbsp;&nbsp;Secondary Colors Only4.3&nbsp;&nbsp;Primary Colors Only5&nbsp;&nbsp;First grade math Analysis6&nbsp;&nbsp;Hypothesis7&nbsp;&nbsp;Conclusions  Visualising Color Schemes in 3DThis is a decent experiment in using [Ipyvolume](http://ipyvolume.readthedocs.io/en/latest/index.html) (and [Three.js](http://threejs.org)) to visualize in 3D the [color scheme](https://brandlive.here.com/colors) of some corporate identity as implemented by a sample company, in this case [HERE Technologies](https://here.com). Apart from this introduction there is not much more prose inside this notebook. This should be considered a short appetizer to see how easy it is to use Ipyvolume.**N.B.**: If you see this Jupyter notebook on GitHub or some online notebook viewer you will likely miss the whole point, namely the rendered 3D plots (Ipyvolume includes warnings where this happens, saying something like: "A Jupyter widget could not be displayed because the widget state could not be found. This could happen if the kernel storing the widget is no longer available, or if the widget state was not saved in the notebook. You may be able to create the widget by running the appropriate cells. [...]")! In this case [download/install Ipyvolume](http://ipyvolume.readthedocs.io/en/latest/install.html) and download/open this notebook locally! You might also want to run a local [Jupyter inside Docker](https://github.com/jupyter/docker-demo-images).  MotivationInvestigate some mysteries with a color scheme description, namely a possible buglet or inconsistency in the description of one shade of the two primary colors, HERE *Aqua* and *Gray*. Notice in the left image below how the second row of *Aqua* is unnamed, while the third is named HERE *Dark Aqua*. And compare to the shades of *Gray* on the right side.   Shades of HERE Aqua and HERE Gray<jupyter_code>from collections import OrderedDict

# values entered manually from https://brandlive.here.com/colors
here_primary_cols = OrderedDict(
    HERE_Aqua         = '#48dad0',
    HERE_Aqua_UNKNOWN = '#00908a', # unknown status, maybe an error?
    HERE_Aqua_Dark    = '#00afaa',
    HERE_Aqua_75      = '#76e3dc',
    HERE_Aqua_50      = '#a3ece7',
    HERE_Aqua_25      = '#d1f6f3',
    
    HERE_Gray      = '#383c45',
    HERE_Gray_Dark = '#0f1621',
    HERE_Gray_75   = '#6a6d74',
    HERE_Gray_50   = '#9b9da2',
    HERE_Gray_25   = '#cdced0',
    HERE_Gray_00   = '#ffffff'  # a.k.a. white
)<jupyter_output><empty_output><jupyter_text>To be explored further below…  Discover Ipyvolume<jupyter_code>import numpy as np
import ipyvolume as ipv
x, y, z = np.random.random((3, 1000))
ipv.quickscatter(x, y, z, size=1, marker="sphere")<jupyter_output><empty_output><jupyter_text>From here on we use other imports allowing to specify more aspects of the plots.<jupyter_code>import numpy as np
import ipyvolume.pylab as p3
f = p3.figure()
p3.xyzlabel('x1', 'y1', 'z1')
scale = np.linspace(0, 1, num=10)
p3.scatter(scale, scale, scale, size=3, marker="sphere")
p3.show()<jupyter_output><empty_output><jupyter_text>Now let's define a single function with some defaults to have a one-line callable, later.<jupyter_code>def scatter3d(points, **kwargs):
    "Render a 3D scatter plot with some predefined defaults."

    f = p3.figure()
    p3.xyzlabel(*kwargs.get('xyzlabels', ['x1', 'y1', 'z1']))
    
    kwargs1 = {}
    if 'size' not in kwargs:
        kwargs1.update(size=3)
    if 'marker' not in kwargs:
        kwargs1.update(marker='sphere')
    kwargs1.update(kwargs)

    p3.scatter(*points, **kwargs1)
    p3.show()
points = [np.linspace(0, 1, num=10)] * 3
scatter3d(points)
scale = np.linspace(0, 1, num=10)
points = [scale] * 3
color = np.array(points)
scatter3d(points, color=color.T)
scale = np.linspace(0, 1, num=10)
points = [scale] * 3
color = np.array(points)
scatter3d(points, color=color.T, size=(1 - scale) * 8)
x, y, z = np.random.random((3, 1000))
points = [x, y, z]
color = np.array(points).T
scatter3d(points, color=color, size=3)<jupyter_output><empty_output><jupyter_text>Convert Hex to RGB Colors<jupyter_code>tuple(bytes.fromhex("aabbcc")) == (170, 187, 204)
def hex2rgb(hex_color):
    """Convert web hex color to normalized RGB tuple.
    
    E.g. hex2rgb('#aabbcc') -> (0.6666666666666666, 0.7333333333333333, 0.8)
    """
    clean_hex_color = hex_color[1:] if hex_color.startswith('#') else hex_color
    r, g, b = tuple(bytes.fromhex(clean_hex_color))
    return (r/255., g/255., b/255.)
hex2rgb("aabbcc") == (170/255., 187/255., 204/255.)<jupyter_output><empty_output><jupyter_text>Plot Color Schemes All 'Scraped' ColorsBefore you continue, have a look at the [color scheme description](https://brandlive.here.com/colors) to know what to expect for the primary and secondary colors used in the full color scheme!<jupyter_code># Define a function with some defaults to make this a one-line call.

def scatter_colors_3d(colors, **kwargs):
    "Render a 3D scatter plot with defaults for a list of hex colors."

    rgb = np.array([hex2rgb(val) for val in colors])
    r = rgb[:, 0]
    g = rgb[:, 1]
    b = rgb[:, 2]
    color = np.array((r, g, b)).T    

    kwargs1 = {}
    if 'size' not in kwargs:
        kwargs1.update(size=3)
    if 'marker' not in kwargs:
        kwargs1.update(marker='sphere')
    kwargs1.update(color=color)
    kwargs1.update(kwargs)

    f = p3.figure()
    p3.xyzlabel(*kwargs.get('xyzlabels', ['red', 'green', 'blue']))
    p3.scatter(r, g, b, **kwargs1)
    p3.show()
import re
import requests

def scrape_hex_colors(url):
    "Return all Hex web colors 'scraped' from some webpage."
    
    html = requests.get(url).content.decode('utf-8')
    return re.findall('#[0-9a-fA-F]{6,6}', html)
# Mind the 's' in https!
url = 'https://brandlive.here.com/colors'
here_all_colors = set(scrape_hex_colors(url))

# Just to be sure we have the values even if the website should be changed later:
# here_all_colors = '''#a3ece7 #c53580 #c41c33 #48dad0 #0f1621 #b7c99d #6f83bd #7dbae4 
    #00afaa #3f59a7 #d35566 #673a93 #6a6d74 #52a3db #f5b086 #00908a #8d6bae #d468a0 
    #383c45 #b39cc9 #cdced0 #f1894a #44ca9d #fbca40 #e29abf #06b87c #a8d1ed #fab800 
    #e18d99 #ec610e #76e3dc #ffffff #94af6d #9b9da2 #d1f6f3 #fcdb7f #9facd3 #70943c 
    #82dbbd'''.split()
scatter_colors_3d(here_all_colors)<jupyter_output><empty_output><jupyter_text>Secondary Colors Only<jupyter_code>all_cols = here_all_colors
pri_cols = here_primary_cols.values()
sec_cols = {*all_cols}.difference(pri_cols)
scatter_colors_3d(sec_cols)<jupyter_output><empty_output><jupyter_text>Primary Colors Only<jupyter_code>here_primary_cols
scatter_colors_3d(here_primary_cols.values())<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12199337462192136, 'fraction_numerical': 0.05084257525565317, 'mean_word_length': 4.640942323314379, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 34, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '99358', 'license': 'MIT', 'n_tokens_mistral': 2668, 'n_tokens_neox': 2440, 'n_words': 702, 'path': 'color_scheme_3d/visualising_color_schemes_3d.ipynb', 'repo_name': 'deeplook/notebooks', 'size': '6943'}