Color Wheel Tutorial#

In this notebook, you will learn how to create a color picker with a moving wheel in manim (scroll to the end to see the result)

[1]:
from manim import *
config.media_embed = True
from PIL import Image
import colorsys
import math
from colorutils import hsv_to_hex,hex_to_hsv #pip install colorutils
Manim Community v0.17.3

[2]:
size=490
im = Image.new("RGB", (size,size))
radius = min(im.size)/2.0
cx, cy = im.size[0]/2, im.size[1]/2
pix = im.load()

for x in range(im.width):
    for y in range(im.height):
        rx = x - cx
        ry = y - cy
        s = (rx ** 2.0 + ry ** 2.0) ** 0.5 / radius
        if s <= 1.0:
            h = ((math.atan2(ry, rx) / math.pi) + 1.0) / 2.0
            rgb = colorsys.hsv_to_rgb(h, s, 1)
            pix[x,y] = tuple([int(round(c*255.0)) for c in rgb])
hsv_hue_sat = im
display(hsv_hue_sat)
_images/ch6_2_0.png
[3]:
im = Image.new("RGB", (size,size))
radius = min(im.size)/2.0
cx, cy = im.size[0]/2, im.size[1]/2
pix = im.load()

for x in range(im.width):
    for y in range(im.height):
        rx = x - cx
        ry = y - cy
        s = (rx ** 2.0 + ry ** 2.0) ** 0.5 / radius
        if s <= 1.0:
            h = ((math.atan2(ry, rx) / math.pi) + 1.0) / 2.0
            rgb = colorsys.hsv_to_rgb(0, s, 1 )
            rgb = [np.mean(rgb)]*3
            pix[x,y] = tuple([int(255-round(c*255.0)) for c in rgb])
hsv_value = im
display(hsv_value)
_images/ch6_3_0.png
[4]:
class ColorWheels(Group):
    def __init__(self, **kwargs):
        Group.__init__(self, **kwargs)
        im_hue = ImageMobject(hsv_hue_sat).set_z_index(-5)
        im_val = ImageMobject(hsv_value).set_z_index(-5)
    #    im_hue = Circle(radius=1.5).set_style(fill_color=WHITE, fill_opacity=1).set_z_index(-5)
    #    im_val = Circle(radius=1.5).set_style(fill_color=WHITE, fill_opacity=1).set_z_index(-5)
        self.radius = im_hue.height/2
        self.add(im_hue, im_val)
        Group(*self.submobjects).arrange(DOWN, SMALL_BUFF*1.3).to_edge(RIGHT)
        t1= Text("Hue and Saturation").scale(0.3)
        t1.next_to(im_hue, UP, buff=SMALL_BUFF).rotate(35*DEGREES, about_point=im_hue.get_center())
        self.add(t1)
        t2= Text("Value").scale(0.3)
        t2.next_to(im_val, UP, buff=SMALL_BUFF).rotate(35*DEGREES, about_point=im_val.get_center())
        self.add(t2)
        global CENTER_HUE , CENTER_VAL
        CENTER_HUE = im_hue.get_center()
        CENTER_VAL = im_val.get_center()
[5]:
class HueValSlider(Group):
    def __init__(self, wheels, h, s, v,**kwargs):
        hue_tracker= ValueTracker(h)
        sat_tracker= ValueTracker(s)
        val_tracker= ValueTracker(v)
        self.hue_tracker= hue_tracker
        self.sat_tracker= sat_tracker
        self.val_tracker= val_tracker

        Group.__init__(self, **kwargs)
        hue_dot = Dot(CENTER_HUE+LEFT).set_color(BLACK).scale(0.8).set_z_index(1)
        hue_line = Line(CENTER_HUE, hue_dot.get_center()).set_color(BLACK).set_stroke(width=2)
        self.hue_line =hue_line
        hue_circ= Circle().set_color(BLACK).scale(0.08).move_to(hue_dot.get_center())
        hue_dot.add_updater(lambda x: x.move_to(CENTER_HUE+wheels.radius*sat_tracker.get_value()* np.array([-np.cos(hue_tracker.get_value()*DEGREES),np.sin(hue_tracker.get_value()*DEGREES),0])))
        hue_dot.add_updater(lambda x: x.set_color(hsv_to_hex((hue_tracker.get_value()%360, sat_tracker.get_value(),val_tracker.get_value()))))
        hue_line.add_updater(lambda x: x.put_start_and_end_on(CENTER_HUE, hue_dot.get_center()))

        hue_circ.add_updater(lambda x: x.move_to(hue_dot.get_center()))
        self.add(hue_dot, hue_circ, hue_line)

        val_dot = Dot(CENTER_VAL+LEFT).set_color(BLACK).scale(0.8).set_z_index(1)
        val_line = Line(CENTER_VAL, val_dot.get_center()).set_color(BLACK).set_stroke(width=2)
        val_circ= Circle().set_color(BLACK).scale(0.08).move_to(val_dot.get_center())
        val_dot.add_updater(lambda x: x.move_to(CENTER_VAL+wheels.radius*val_tracker.get_value()* np.array([-np.cos(hue_tracker.get_value()*DEGREES),np.sin(hue_tracker.get_value()*DEGREES),0])))
        val_dot.add_updater(lambda x: x.set_color(hsv_to_hex((hue_tracker.get_value()%360, sat_tracker.get_value(),val_tracker.get_value()))))
        val_line.add_updater(lambda x: x.put_start_and_end_on(CENTER_VAL, val_dot.get_center()))
        val_circ.add_updater(lambda x: x.move_to(val_dot.get_center()))
        self.add(val_dot, val_circ, val_line)
[6]:
%%manim  -v WARNING -qm  --disable_caching Idea3

class Idea3(Scene):
    def construct(self):
        wheels = ColorWheels()
        self.add(wheels)
        t1= Dot().scale(4)
        t2= Dot().scale(4)

        t3 = Dot().scale(4)
        gr = VGroup(t1,t2,t3).arrange(DOWN)
        self.add(gr)

        t1.add_updater(lambda x: x.set_color(hsv_to_hex((huevals1.hue_tracker.get_value()%360, huevals1.sat_tracker.get_value(),1))))
        t2.add_updater(lambda x: x.set_color(hsv_to_hex((huevals2.hue_tracker.get_value()%360, huevals2.sat_tracker.get_value(),1))))
        t3.add_updater(lambda x: x.set_color(hsv_to_hex((huevals3.hue_tracker.get_value()%360, huevals3.sat_tracker.get_value(),1))))


        huevals1=HueValSlider(wheels,0,1,1)
        huevals2=HueValSlider(wheels,120,1,1)
        huevals3=HueValSlider(wheels,240,1,1)



        self.add(huevals1)
        self.add(huevals2)
        self.add(huevals3)
        hues_all_tracker = ValueTracker(0)

        self.add(hues_all_tracker)
        self.add(huevals1.hue_tracker)
        self.add(huevals2.hue_tracker)
        self.add(huevals3.hue_tracker)

        huevals1.hue_tracker.add_updater(lambda mobject, dt: mobject.increment_value(dt*30))
        huevals2.hue_tracker.add_updater(lambda mobject, dt: mobject.increment_value(dt*30))
        huevals3.hue_tracker.add_updater(lambda mobject, dt: mobject.increment_value(dt*30))

        self.wait(3)

        self.play(
            huevals1.sat_tracker.animate.increment_value(-0.2),
            huevals2.sat_tracker.animate.increment_value(-0.2),
            huevals3.sat_tracker.animate.increment_value(-0.2),
        )
        self.wait(1)
        self.play(
            huevals1.val_tracker.animate.increment_value(-0.2),
            huevals2.val_tracker.animate.increment_value(-0.2),
            huevals3.val_tracker.animate.increment_value(-0.2),
        )
        self.wait(1)

[ ]: