Continuous Coloring with Sine waves

This notebooks show how can use sine waves and a continuous index to generate smoothly blending rgb colors for fractals.


In [16]:
import csv
import os
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.colors as col
import matplotlib.cm as cm

In [17]:
cont_idx = np.array(1)
escape_idx = np.array(1)

In [18]:
def read_csv(source_file):
    if (os.path.isfile(source_file)):
        with open(source_file,'r') as csv_file:
            data_iter = csv.reader(csv_file, 
                                   delimiter = ';', 
                                   quotechar = '"')
            data = [data for data in data_iter]
        return data
    else:
        return np.nan

In [19]:
def rgb_continuous(cont_index, escape_time, bailout, rgb_base, rgb_freq, rgb_phase):
    '''
    Convert cont_index with rgb_base rgb_freq and rgb_phase to a rgb value between 0 and 255 
    using the power of the sine function
    '''
    red = rgb_base[0]
    green = rgb_base[1]
    blue = rgb_base[2]
    if (escape_time == bailout):
        return (0, 0, 0)
    
    if (rgb_freq[0] > 0):
        red = np.abs(np.sin(rgb_freq[0] * cont_index + rgb_phase[0]) * (255 - rgb_base[0]) + rgb_base[0])
    if (rgb_freq[1] > 0):
        green = np.abs(np.sin(rgb_freq[1] * cont_index + rgb_phase[1]) * (255 - rgb_base[1]) + rgb_base[1])
    if (rgb_freq[2] > 0):
        blue = np.abs(np.sin(rgb_freq[2] * cont_index + rgb_phase[2]) * (255 - rgb_base[2]) + rgb_base[2])
    
    rgb = np.array([red, green, blue])
    return rgb.astype(int)

In [20]:
def read_csv_data(cont_idx, escape_idx, bailout, rgb_base, rgb_freq, rgb_phase):
    rgb_tuples = np.zeros((cont_idx.size,3), dtype='uint8')
    tuples_idx = 0
    
    for cidx in cont_idx.flatten():
        rgb_tuples[tuples_idx] = rgb_continuous(cidx, 
                                                escape_idx.flat[tuples_idx],
                                                bailout,
                                                rgb_base,
                                                rgb_freq,
                                                rgb_phase)
        tuples_idx +=1
        
    return rgb_tuples

In [21]:
def generate_rgb_tuples(cont_idx, escape_idx, bailout, rgb_base, rgb_freq, rgb_phase):
    rgb_tuples = np.zeros((cont_idx.size,3), dtype='uint8')
    tuples_idx = 0
    
    for cidx in cont_idx:
        rgb_tuples[tuples_idx] = rgb_continuous(cidx, 
                                                escape_idx[tuples_idx],
                                                bailout,
                                                rgb_base,
                                                rgb_freq,
                                                rgb_phase)
        tuples_idx +=1
        
    return rgb_tuples

In [22]:
# use these only when readiong csv exports of geomandel
# cont_idx = np.asarray(read_csv("geomandel_w500_h500_b100_contindex.csv"), dtype=float)
# escape_idx = np.asarray(read_csv("geomandel_w500_h500_b100_iterindex.csv"), dtype=int)
# read_csv_data(cont_idx, escape_idx)

cont_idx = np.logspace(0.02, 3.0, num=1000)
escape_idx = np.linspace(0, 1000, 1001)
escape_idx_2 = np.linspace(0, 50, 51)
rgb_tuples_1 = generate_rgb_tuples(cont_idx, 
                                   escape_idx, 
                                   cont_idx.size,
                                   (127, 127, 127),
                                   # you have to adjust the frequency so it corresponds with the mandelbrot bailout
                                   (0.006, 0.006, 0.006),
                                   (0, 2, 4))
rgb_tuples_2 = generate_rgb_tuples(cont_idx, 
                                   escape_idx, 
                                   cont_idx.size,
                                   (127, 127, 127),
                                   (0.01, 0.008, 0.005), 
                                   (0, 0, 0))
rgb_tuples_3 = generate_rgb_tuples(cont_idx, 
                                   escape_idx, 
                                   cont_idx.size,
                                   (200, 200, 200), 
                                   (0.016, 0.013, 0.01), 
                                   (4, 2, 1))

In [23]:
bounds = np.arange(0, 1000, 1)

In [24]:
norm_obj_1 = col.Normalize(vmin=0,
                           vmax=255)

In [25]:
colmap_1 = col.ListedColormap(norm_obj_1(rgb_tuples_1))
colmap_2 = col.ListedColormap(norm_obj_1(rgb_tuples_2))
colmap_3 = col.ListedColormap(norm_obj_1(rgb_tuples_3))

In [26]:
norm_1 = mpl.colors.BoundaryNorm(bounds, colmap_1.N)
norm_2 = mpl.colors.BoundaryNorm(bounds, colmap_2.N)
norm_3 = mpl.colors.BoundaryNorm(bounds, colmap_3.N)

In [27]:
# generate 3 different figures showing the color channel waves and a corresponding colorbar

fig = plt.figure(1, figsize=(10, 6))
fig.suptitle("Rainbow Colors using out of phase waves", fontsize='medium', weight='bold')
ax_1 = fig.add_subplot(111)
ax_1.plot(cont_idx, rgb_tuples_1.T[0], c='red', lw=2)
ax_1.plot(cont_idx, rgb_tuples_1.T[1], c='green', lw=2)
ax_1.plot(cont_idx, rgb_tuples_1.T[2], c='blue', lw=2)
ax_1.set_xlabel("Continuous Index")
ax_1.set_ylabel("Color Channel")
rgb_settings_1 = "rgb-base=127,127,127 rgb-freq=0.006,0.006,0.006 rgb-phase=0,2,4"
ax_1.text(0.5, 0.92, rgb_settings_1,
         horizontalalignment='center',
         fontsize='small',
          weight='bold',
         transform = ax_1.transAxes)
ax_1.grid()
# make room for an additional axis at the bottom
fig.subplots_adjust(top=0.93, bottom=0.27)
# get the bounding box
box = ax_1.get_position()
# use the box to position the colorbar
ax_colb = fig.add_axes([box.x0, box.y0 - 0.18, box.width, 0.1])
cb_1 = mpl.colorbar.ColorbarBase(ax_colb, cmap=colmap_1,
                                 norm=norm_1,
                                 boundaries=bounds,
                                 # extend='both',
                                 # Make the length of each extension
                                 # the same as the length of the
                                 # interior colors:
                                 extendfrac='auto',
                                 spacing='uniform',
                                 orientation='horizontal')
cb_1.set_label("Rainbow Colors")

# a second figure

fig_2 = plt.figure(2, figsize=(10, 6))
fig_2.suptitle("Different Frequencies", fontsize='medium', weight='bold')
ax_2 = fig_2.add_subplot(111)
ax_2.plot(cont_idx, rgb_tuples_2.T[0], c='red', lw=2)
ax_2.plot(cont_idx, rgb_tuples_2.T[1], c='green', lw=2)
ax_2.plot(cont_idx, rgb_tuples_2.T[2], c='blue', lw=2)
ax_2.set_xlabel("Continuous Index")
ax_2.set_ylabel("Color Channel")
rgb_settings_2 = "rgb-base=127,127,127 rgb-freq=0.01,0.008,0.005 rgb-phase=0,0,0"
ax_2.text(0.5, 0.92, rgb_settings_2,
         horizontalalignment='center',
         fontsize='small',
          weight='bold',
         transform = ax_2.transAxes)
ax_2.grid()
# make room for an additional axis at the bottom
fig_2.subplots_adjust(top=0.93, bottom=0.27)
# get the bounding box
box_2 = ax_2.get_position()
# use the box to position the colorbar
ax_colb_2 = fig_2.add_axes([box_2.x0, box_2.y0 - 0.18, box_2.width, 0.1])
cb_2 = mpl.colorbar.ColorbarBase(ax_colb_2, cmap=colmap_2,
                                 norm=norm_2,
                                 boundaries=bounds,
                                 # extend='both',
                                 # Make the length of each extension
                                 # the same as the length of the
                                 # interior colors:
                                 extendfrac='auto',
                                 spacing='uniform',
                                 orientation='horizontal')
cb_2.set_label("Grey Shades to colors")

fig_3 = plt.figure(3, figsize=(10, 6))
fig_3.suptitle("Different Frequencies and out of phase", fontsize='medium', weight='bold')
ax_3 = fig_3.add_subplot(111)
ax_3.plot(cont_idx, rgb_tuples_3.T[0], c='red', lw=2)
ax_3.plot(cont_idx, rgb_tuples_3.T[1], c='green', lw=2)
ax_3.plot(cont_idx, rgb_tuples_3.T[2], c='blue', lw=2)
ax_3.set_xlabel("Continuous Index")
ax_3.set_ylabel("Color Channel")
rgb_settings_3 = "rgb-base=200,200,200 rgb-freq=0.016,0.013,0.01 rgb-phase=4,2,1"
ax_3.text(0.5, 0.92, rgb_settings_3,
         horizontalalignment='center',
         fontsize='small',
          weight='bold',
         transform = ax_3.transAxes)
ax_3.grid()
# make room for an additional axis at the bottom
fig_3.subplots_adjust(top=0.93, bottom=0.27)
# get the bounding box
box_3 = ax_3.get_position()
# use the box to position the colorbar
ax_colb_3 = fig_3.add_axes([box_3.x0, box_3.y0 - 0.18, box_3.width, 0.1])
cb_3 = mpl.colorbar.ColorbarBase(ax_colb_3, cmap=colmap_3,
                                 norm=norm_3,
                                 boundaries=bounds,
                                 # extend='both',
                                 # Make the length of each extension
                                 # the same as the length of the
                                 # interior colors:
                                 extendfrac='auto',
                                 spacing='uniform',
                                 orientation='horizontal')
cb_3.set_label("Pastel Colors")

In [28]:
plt.show()