Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Color choosing function - does one exist

6 views
Skip to first unread message

Richard Owlett

unread,
Sep 17, 2007, 4:30:09 PM9/17/07
to
Colors can be specified in #RGB format where R/G/B can take on 4/8/12/16
bit hex values.

Is there a Tcl function to map a monotonic varying value to a constant
intensity color that varies smoothly thru the rainbow?

TIA

Stephan Kuhagen

unread,
Sep 17, 2007, 4:46:12 PM9/17/07
to
Hello

Richard Owlett wrote:

> Is there a Tcl function to map a monotonic varying value to a constant
> intensity color that varies smoothly thru the rainbow?

Maybe a proc converting HSV color space to RGB color space is what you need.
H is hue and can have values 0 to 360 and S and V are Saturation of the
color and Value in the range from 0 to 1. Choose the Saturation and Value
and use H to wander the trough the rainbow...

Below is a hsv2rgb-proc, that I use in one of my packages.

HTH
Stephan

----------------------------------------------------------------------------
proc hsv2rgb {h s v} {
# hsv2rgb
# @param h: Hue in the range of 0-360 float
# @param s: Saturation in the range of 0-1 float
# @param v: Value in the range of 0-1 float
# @return list r, g, b (0-255 int)

set hi [expr {int($h/60.0)%6}]
set f [expr {($h/60.0)-$hi}]
set p [expr {$v*(1-$s)}]
set q [expr {$v*(1-($f*$s))}]
set t [expr {$v*(1-((1.0-$f)*$s))}]

if {$hi==0} {
set r $v
set g $t
set b $p
} elseif {$hi==1} {
set r $q
set g $v
set b $p
} elseif {$hi==2} {
set r $p
set g $v
set b $t
} elseif {$hi==3} {
set r $p
set g $q
set b $v
} elseif {$hi==4} {
set r $t
set g $p
set b $v
} elseif {$hi==5} {
set r $v
set g $p
set b $q
}
return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr
{int($b*255)}]]
} ;# proc hsv2rgb {h s v}

Richard Owlett

unread,
Sep 17, 2007, 5:15:34 PM9/17/07
to
Stephan Kuhagen wrote:
> Hello
>
> Richard Owlett wrote:
>
>
>>Is there a Tcl function to map a monotonic varying value to a constant
>>intensity color that varies smoothly thru the rainbow?
>
>
> Maybe a proc converting HSV color space to RGB color space is what you need.
> H is hue and can have values 0 to 360 and S and V are Saturation of the
> color and Value in the range from 0 to 1. Choose the Saturation and Value
> and use H to wander the trough the rainbow...
>
> Below is a hsv2rgb-proc, that I use in one of my packages.
>
> HTH
> Stephan
>
[snip code]

Your description matches well with what I was trying to say. If V is
what is often called "luminance" in TV, then it's just what I'm looking for.

I'll have to run some test cases to see if result matches my
visualization. IOW does my specification match my desires ;}


Thanks.

Richard Owlett

unread,
Sep 17, 2007, 6:03:22 PM9/17/07
to
Stephan Kuhagen wrote:

I wanted to do a preliminary test.
I loaded above code by doing a cut-n-paste.

I then entered:

hsv2rgb {5.0 0.3 0.4}

The response was:

wrong # args: should be "expr arg ?arg ...?"

What *DUMB* newbie mistake maketh i ;{

Gerry Snyder

unread,
Sep 17, 2007, 6:32:45 PM9/17/07
to
Richard Owlett wrote:
>
>>
>> ----------------------------------------------------------------------------
>>
>> proc hsv2rgb {h s v} {
>> ....

>> return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr
>> {int($b*255)}]]
>> ....

> I then entered:
>
> hsv2rgb {5.0 0.3 0.4}
>
> The response was:
>
> wrong # args: should be "expr arg ?arg ...?"
>
> What *DUMB* newbie mistake maketh i ;{

Remove the newline which breaks up the return statement. That's supposed
to be a single line.

And I think you meant that you entered:

hsv2rgb 5.0 0.3 0.4

HTH,

Gerry

Richard Owlett

unread,
Sep 17, 2007, 7:01:53 PM9/17/07
to
Gerry Snyder wrote:

NOPE :<

Same error whether I enter
hsv2rgb 5.0 0.3 0.4
or
hsv2rgb {5.0 0.3 0.4}

I've done enough training that I suspect I'm making a *REALLY* dumb error ;<

Gerry Snyder

unread,
Sep 17, 2007, 7:47:09 PM9/17/07
to
Richard Owlett wrote:

> Gerry Snyder wrote:
>
>> Remove the newline which breaks up the return statement. That's
>> supposed to be a single line.
>

Did you do the above, the first part of my reply?

The error comes from the line:

return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr


Gerry

Richard Owlett

unread,
Sep 17, 2007, 7:59:56 PM9/17/07
to
Gerry Snyder wrote:

I suspect I've made VERY fundamental mistake

my session is as follows

(bin) 1 %
(bin) 1 % proc hsv2rgb {h s v} {

> return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr

> {int($b*255)}]]
> } ;# proc hsv2rgb {h s v}
(bin) 2 %
(bin) 2 % hsv2rgb 5.0 0.3 0.4


wrong # args: should be "expr arg ?arg ...?"

(bin) 3 %

I'm using Wish84 from ActiveState under WindowsXP Pro


Gerry Snyder

unread,
Sep 17, 2007, 8:12:08 PM9/17/07
to
Richard Owlett wrote:
> Gerry Snyder wrote:
>
>> Richard Owlett wrote:
>>
>>> Gerry Snyder wrote:
>>>
>>>> Remove the newline which breaks up the return statement. That's
>>>> supposed to be a single line.
>>>
>>>
>>
>> Did you do the above, the first part of my reply?
>>
>> The error comes from the line:
>>
>> return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr
>>
>>
>> Gerry
>
> I suspect I've made VERY fundamental mistake
>
> my session is as follows
>
> (bin) 1 %
> (bin) 1 % proc hsv2rgb {h s v} {
> > # hsv2rgb
> > # @param h: Hue in the range of 0-360 float
> > # @param s: Saturation in the range of 0-1 float
> > # @param v: Value in the range of 0-1 float
> > # @return list r, g, b (0-255 int)
> >
> > ....

> > return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr
> > {int($b*255)}]]

These last two lines need to be made into a single line, or changed to:

return [list [expr {int($r*255)}] [expr {int($g*255)}] [expr \
{int($b*255)}]]

The last expr is not on the same line as its argument. The email system
broke up one long line into two lines in Stephan's posting. You need to
make it back into one line, or tell Tcl that there is a continuation.

HTH,

Gerry

skuh...@web.de

unread,
Sep 18, 2007, 1:34:10 AM9/18/07
to

Richard Owlett wrote:

> What *DUMB* newbie mistake maketh i ;{

As Garry already said, there was a line continuation problem in the
posting. Here is a small script, that contains hsv2rgb and some lines
of a demo how to use it. You should run it with wish or tclsh
(requires Tk). It opens a window with a label showing the color and
three sliders with H, S, V from top to down. Try the H-slider to
wander through colors.

http://www.mempool.net/colors.tcl

Stephan

Message has been deleted

skuh...@web.de

unread,
Sep 18, 2007, 2:32:27 AM9/18/07
to

skuha...@web.de wrote:
> As Garry...

"Gerry", not "Garry", very sorry!

Stephan

Andreas Leitgeb

unread,
Sep 18, 2007, 6:41:43 AM9/18/07
to
skuh...@web.de <skuh...@web.de> wrote:
> Here is a small script, that contains hsv2rgb and some lines
> of a demo how to use it. You should run it with wish or tclsh
> (requires Tk). It opens a window with a label showing the color and
> three sliders with H, S, V from top to down. Try the H-slider to
> wander through colors.
> http://www.mempool.net/colors.tcl

Is there any reason for the if-elseif-...-else construct in favour of a
simple switch?

just curious.

Googie

unread,
Sep 18, 2007, 7:05:04 AM9/18/07
to
http://wiki.tcl.tk/19711 is related.

--
Pozdrawiam! (Regards!)
Googie

skuh...@web.de

unread,
Sep 18, 2007, 7:09:17 AM9/18/07
to

Andreas Leitgeb wrote:
> Is there any reason for the if-elseif-...-else construct in favour of a
> simple switch?
>
> just curious.

Speed.

Normally this is not a problem, because it does not matter, if
execution is a millisecond faster or slower. But I wrote a color
selection dialog in pure Tk (the build in dialog is very outdated and
the dialogs I found at the wiki did not really satisfy me) with real
time updates of RGB- and HSV-sliders and a color field (much like the
color dialog of gimp, if you know that one), where I used as much
optimizations as possible (that's why the proc also does no range- or
type-checks for the arguments, but the public wrapper of the proc
does). I wanted at least 20 or more frames per second for calculating
all widget settings and updates of the color field on my 2.1 GHz
MacBook and I got them (even more)...

I will release the dialog and the procs for conversion as hsv2rgb and
as open source soon (some weeks, since I'm moving to a new
apartment...) as a package. Tk will have a fairly decent color
selection dialog then.

Stephan

sch...@uni-oldenburg.de

unread,
Sep 18, 2007, 7:34:13 AM9/18/07
to

skuha...@web.de wrote:
> Andreas Leitgeb wrote:
> > Is there any reason for the if-elseif-...-else construct in favour of a
> > simple switch?
> >
> > just curious.
>
> Speed.

Hmm, don't remember exactly since when switch gets bytecompiled,
either in the 8.4 series or only in 8.5, then it should no longer make
a difference.

Michael

skuh...@web.de

unread,
Sep 18, 2007, 7:56:22 AM9/18/07
to

schl...@uni-oldenburg.de wrote:
> Hmm, don't remember exactly since when switch gets bytecompiled,
> either in the 8.4 series or only in 8.5, then it should no longer make
> a difference.

I think, it does:

------------------------------------------------------------------------------------------------------------
proc switchTest {v} {
switch $v {
0 { set result 0 }
1 { set result 1 }
2 { set result 2 }
3 { set result 3 }
4 { set result 4 }
default { set result -1 }
}
return $result
}

proc ifElseTest {v} {
if {$v == 0} {
set result 0
} elseif {$v == 1} {
set result 1
} elseif {$v == 2} {
set result 2
} elseif {$v == 3} {
set result 3
} elseif {$v == 4} {
set result 4
} else {
set result -1
}
return $result
}

set values {0 1 2 3 4 5}

puts "switch [time { foreach v $values {switchTest $v} } 10000]"
puts "ifelse [time { foreach v $values {ifElseTest $v} } 10000]"
------------------------------------------------------------------------------------------------------------

When running with tcl 8.4.14 on Linux Fedora 7 on a P...@3.00GHz the
results are
switch 20.7521 microseconds per iteration
ifelse 16.5151 microseconds per iteration

The same with tcl 8.5a6:
switch 38.6253 microseconds per iteration
ifelse 16.6042 microseconds per iteration

The difference is even greater as in tcl 8.4...

Regards
Stephan

Kaitzschu

unread,
Sep 18, 2007, 9:29:37 AM9/18/07
to
On Tue, 18 Sep 2007, skuh...@web.de wrote:

> schl...@uni-oldenburg.de wrote:
>> Hmm, don't remember exactly since when switch gets bytecompiled, either
>> in the 8.4 series or only in 8.5, then it should no longer make a
>> difference.
>
> I think, it does:
>

> When running with tcl 8.4.14 on Linux Fedora 7 on a P...@3.00GHz the
> results are
> switch 20.7521 microseconds per iteration
> ifelse 16.5151 microseconds per iteration
>
> The same with tcl 8.5a6:
> switch 38.6253 microseconds per iteration
> ifelse 16.6042 microseconds per iteration
>
> The difference is even greater as in tcl 8.4...

That is because [switch] is bugged in 8.5a6.

[code]
proc switchTest2 {v} {
switch -exact -- $v {


0 { set result 0 }
1 { set result 1 }
2 { set result 2 }
3 { set result 3 }
4 { set result 4 }
default { set result -1 }
}
return $result
}

[/code]

switch 15.9789 microseconds per iteration
ifelse 11.2008 microseconds per iteration
switc2 9.6635 microseconds per iteration

This with 8.5a6.

--
-Kaitzschu
s="TCL ";while true;do echo -en "\r$s";s=${s:1:${#s}}${s:0:1};sleep .1;done

"Good thing programmers don't build bridges
[the same way as Kaitzschu writes code]."
--John Kelly in comp.lang.tcl

Stephan Kuhagen

unread,
Sep 18, 2007, 10:24:45 AM9/18/07
to
Kaitzschu wrote:

> That is because [switch] is bugged in 8.5a6.

...


> switch 15.9789 microseconds per iteration
> ifelse 11.2008 microseconds per iteration
> switc2 9.6635 microseconds per iteration

Thanks for the information. I will wait for newer versions of tcl 8.5 and
possibly use switch for 8.5 and if-else for 8.4.

Regards
Stephan

Bryan Oakley

unread,
Sep 18, 2007, 10:48:57 AM9/18/07
to

Don't wait, and don't switch. Unless that if or switch is in a tight
loop the speed difference is a moot point. Optimize your code for
readability and maintainability, and only focus on raw performance when
you can measure a performance bottleneck.


--
Bryan Oakley
http://www.tclscripting.com

Donal K. Fellows

unread,
Sep 18, 2007, 11:17:33 AM9/18/07
to
Stephan Kuhagen wrote:
> Thanks for the information. I will wait for newer versions of tcl 8.5 and
> possibly use switch for 8.5 and if-else for 8.4.

No, just put the -- in so that you no longer have problems waiting to
jump up and bite you. (And as a bonus, 8.5 will then become able to
compile it for you.)

Donal.

skuh...@web.de

unread,
Sep 19, 2007, 1:54:49 AM9/19/07
to

Bryan Oakley wrote:
> Don't wait, and don't switch. Unless that if or switch is in a tight
> loop the speed difference is a moot point. Optimize your code for
> readability and maintainability, and only focus on raw performance when
> you can measure a performance bottleneck.

I think, my code for the color selection dialog is very clean so far,
but OTOH I need to optimize all parts because of the real time updates
of the color field. Otherwise the dialog will be unusable on not so
fast systems. Currently it runs very good on 3GHz P4 and 2.1GHz
CoreDuo and is usable on a lousy 800MHz PowerPC G3 (!) and I can live
with the performance even on a Nokia N800 Handheld. But for this I had
to optimize every bit and line. And, of course, it's only a fun
project... So if I have to create two versions of some calls because
of those performance differences between [if] and [switch] to gain 0.5
fps an the N800, I will do so... ;-)

But for the bread and butter work I agree completely with you: I
optimize only where it's really needed and effective and focus on
good, clean and well documented code.

Regards
Stephan

skuh...@web.de

unread,
Sep 19, 2007, 2:11:42 AM9/19/07
to

Donal K. Fellows wrote:
> No, just put the -- in so that you no longer have problems waiting to
> jump up and bite you. (And as a bonus, 8.5 will then become able to
> compile it for you.)

I will add the "--", thanks.

For the 8.4 vs. 8.5 optimizations I like to add something: I think,
8.5 is overdue for some years now, and although first beta is expected
every day now, we have to admit, that Tcl is 8.4 and not 8.5 and I'm
sure, it will be this way for some time. Even if 8.5 is launched end
of the year or Q1-3 2008 (there are always reasons for unexpected
delays...), 8.4 will be with us for a long time and many distributions
of embedded devices (such as my N800) and others will work with that
version. To ignore 8.4 specifics in optimization (if one bothers to
optimize) would mean to ignore the fact, that Tcl is a *very* slow
moving target...

Besides that: I'm really fidgeting in my seat anticipating 8.5 and
would normally give the same advice as Brian Oakley and you did.

Regards
Stephan

Cameron Laird

unread,
Sep 19, 2007, 7:53:42 AM9/19/07
to
In article <1190181289.8...@n39g2000hsh.googlegroups.com>,
<skuh...@web.de> wrote:
.
.
.

>project... So if I have to create two versions of some calls because
>of those performance differences between [if] and [switch] to gain 0.5
>fps an the N800, I will do so... ;-)
.
.
.
'Sounds as though you know what you're doing, Stephan.
Elsewhere in the thread, I think we all agreed that
"--" on switch can allow you to collapse all this back
to a single, more maintainable expression.

More generally, how are you handling "two versions of
some calls"? My favorite for general situations of
your description is something like this:

switch $the_situation {
one_environment {
proc important_proc1 ...
proc important_proc1 ...
...
}
other_environment {
proc important_proc1 ...
proc important_proc1 ...
...
}
...
}

do_the_real_work

This makes for a nice combination of run-time initialization
and optimization.

skuh...@web.de

unread,
Sep 19, 2007, 10:20:46 AM9/19/07
to

Cameron Laird wrote:

> More generally, how are you handling "two versions of
> some calls"? My favorite for general situations of
> your description is something like this:

...


> This makes for a nice combination of run-time initialization
> and optimization.

My approach is similar, but I try to generalize my procs a little bit
more. In such a case where only parts of the thing differ as with
[switch] vs. [if-else], I do something like the following:

---
set body {
code_for_the_proc
%specialization%
more_code
}

switch -- $condition {
c1 {
set code { something}
}
...
default {
set code { something_default }
}
}

proc theProc {the args} [string map [list %specialization% $code ...]
$body]
proc otherProc {the args} [string map [list %other_specialization%
$other_code ...] $body]
...
---

This way I have only one version of a proc body to maintain and can
focus on the specializations only.

Regards
Stephan

0 new messages