Updated 2004-08-16 22:40:00

Brian Theado - 14Aug04 - Here is some code to display a harmonic color wheel. See http://www.colorschemer.com/tutorial1.html for what such a color wheel can be useful for.

The picture above displays two color wheels. Each color in the outer wheel is the complement of the corresponding color in the inner wheel.
 if {[llength [info commands lassign]] == 0} {
    proc lassign {l args} {uplevel [list foreach $args $l break]}
    }

 # rgb to hsv (swiped from the tk demos)
    # The procedure below converts an RGB value to HSB.  It takes red, green,
    # and blue components (0-65535) as arguments, and returns a list containing
    # HSB components (floating-point, 0-1) as result.  The code here is a copy
    # of the code on page 615 of "Fundamentals of Interactive Computer Graphics"
    # by Foley and Van Dam.
    proc rgbToHsv {red green blue} {
        if {$red > $green} {
            set max [expr {double($red)}]
            set min [expr {double($green)}]
        } else {
            set max [expr {double($green)}]
            set min [expr {double($red)}]
        }
        if {$blue > $max} {
            set max [expr {double($blue)}]
        } elseif {$blue < $min} {
            set min [expr {double($blue)}]
        }
        set range [expr {$max-$min}]
        if {$max == 0} {
            set sat 0
        } else {
            set sat [expr {($max-$min)/$max}]
        }
        if {$sat == 0} {
            set hue 0
        } else {
            set rc [expr {($max - $red)/$range}]
            set gc [expr {($max - $green)/$range}]
            set bc [expr {($max - $blue)/$range}]
            if {$red == $max} {
                set hue [expr {($bc - $gc)/6.0}]
            } elseif {$green == $max} {
                set hue [expr {(2 + $rc - $bc)/6.0}]
            } else {
                set hue [expr {(4 + $gc - $rc)/6.0}]
            }
            if {$hue < 0.0} {
                set hue [expr {$hue + 1.0}]
            }
        }
        return [list $hue $sat [expr {$max/65535}]]
        }

 # hsv to rgb (swiped from the tk demos)
    # The procedure below converts an HSB value to RGB.  It takes hue, saturation,
    # and value components (floating-point, 0-1.0) as arguments, and returns a
    # list containing RGB components (integers, 0-65535) as result.  The code
    # here is a copy of the code on page 616 of "Fundamentals of Interactive
    # Computer Graphics" by Foley and Van Dam.

    proc hsvToRgb {hue sat value} {
        set v [format %.0f [expr {65535.0*$value}]]
        if {$sat == 0} {
            return "$v $v $v"
        } else {
            set hue [expr {$hue*6.0}]
            if {$hue >= 6.0} {
                set hue 0.0
            }
            scan $hue. %d i
            set f [expr {$hue-$i}]
            set p [format %.0f [expr {65535.0*$value*(1 - $sat)}]]
            set q [format %.0f [expr {65535.0*$value*(1 - ($sat*$f))}]]
            set t [format %.0f [expr {65535.0*$value*(1 - ($sat*(1 - $f)))}]]
            switch $i {
                0 {return "$v $t $p"}
                1 {return "$q $v $p"}
                2 {return "$p $v $t"}
                3 {return "$p $q $v"}
                4 {return "$t $p $v"}
                5 {return "$v $p $q"}
                default {error "i value $i is out of range"}
            }
        }
    }

 package require Tk
 # Displays harmonic color wheel starting at the given rgb
 proc displayColorWheel {c r g b {scale 1.0}} {
    # Fill most of the canvas, assuming a zero based coordinate system
    set x [expr round([$c cget -width]/2.1*$scale)]
    set y [expr round([$c cget -height]/2.1*$scale)]
    set numWedges 12.0
    set wedgeWidth [expr 360/$numWedges]
    lassign [rgbToHsv $r $g $b] h s v
    for {set wedge 0} {$wedge < $numWedges} {incr wedge} {
        # Draw the current wedge
        set start [expr $wedge * $wedgeWidth]
        $c create arc -$x -$y $x $y -extent $wedgeWidth -start $start -fill #[format %02x%02x%02x $r $g $b] -tags [list colorwheel wedgenum-$wedge]

        # The next color in a harmonic color wheel is derived by linearly incrementing the hue
        set h1 [expr $h + (($wedge + 1) / $numWedges)]
        if {$h1 > 1} {set h1 [expr $h1 - 1.0]}
        lassign [hsvToRgb $h1 $s $v] r g b
        }
    }
 proc displayComplementColorWheel {c r g b} {
    lassign [rgbToHsv $r $g $b] h s v
    set h1 [expr $h + 0.5]
    if {$h1 > 1} {set h1 [expr $h1 - 1.0]}
    lassign [hsvToRgb $h1 $s $v] r g b
    displayColorWheel $c $r $g $b 0.30
    }
 proc displayRandomColorWheel c {
    set r [expr round(rand()*255)]
    set g [expr round(rand()*255)]
    set b [expr round(rand()*255)]
    displayColorWheel $c $r $g $b
    displayComplementColorWheel $c $r $g $b
    }

# Demonstration code
 proc centerCanvas {W h w} {
    set h [expr {$h / 2.0}]
    set w [expr {$w / 2.0}]
    $W config -scrollregion [list -$w -$h $w $h]
    }
 package require Tk
 toplevel .t
 wm title .t "harmonic color wheel"
 canvas .t.c
 pack .t.c -expand 1
 displayRandomColorWheel .t.c
 bind .t.c <Configure> [namespace code {centerCanvas %W %h %w}]
 bind .t.c <1> [namespace code {
    %W delete colorwheel
    displayRandomColorWheel %W
    }]

George Peter Staplin: Your colorwheel is interesting. I think I will use it to choose colors for my new website. Thanks for sharing. :)

Category Graphics