Attachment "plotcontour.tcl" to
ticket [1113033fff]
added by
arjenmarkus
2005-01-31 15:19:48.
#! /bin/sh
# -*- tcl -*- \
exec tclsh "$0" ${1+"$@"}
# plotcontour.tcl --
# Contour plotting test program for the Plotchart package
###############################################################################
#
# The basic idea behind the method used for contouring within this sample
# is primarily based on :
#
# (1) "Contour Plots of Large Data Sets" by Chris Johnston
# Computer Language, May 1986
#
# a somewhat similar method was also described in
#
# (2) "A Contouring Subroutine" by Paul D. Bourke
# BYTE, June 1987
# http://astronomy.swin.edu.au/~pbourke/projection/conrec/
#
#
#
# In (1) it is assumed that you have a N x M grid of data that you need
# to process. In order to generate a contour, each cell of the grid
# is handled without regard to it's neighbors. This is unlike many other
# contouring algorithms that follow the current contour line into
# neighboring cells in an attempt to produce "smoother" contours.
#
# In general the method described is:
#
# 1) for each four cornered cell of the grid,
# calculate the center of the cell (average of the four corners)
#
# data(i ,j) : Point (1)
# data(i+1,j) : Point (2)
# data(i+1,j+1) : Point (3)
# data(i ,j+1) : Point (4)
# center : Point (5)
#
# (4)-------------(3)
# | \ / |
# | \ / |
# | \ / |
# | \ / |
# | \ / |
# | (5) |
# | / \ |
# | / \ |
# | / \ |
# ^ | / \ |
# | | / \ |
# J (1)-------------(2)
#
# I ->
#
# This divides the cell into four triangles.
#
# 2) Each of the five points in the cell can be assigned a sign (+ or -)
# depending upon whether the point is above (+) the current contour
# or below (-).
#
# A contour will cross an edge whenever the points on the boundary of
# the edge are of an opposite sign.
#
# A few examples :
#
# (-) (-) (-) | (+) (-) (-) (+) | (-)
# \ _ \
# \ / \ \
# (-) - (-) | _ /(+) | - (+) -
# / / / \
# / / / \
# (-) | (+) (-) | (+) (+) | (-) (-) | (+)
#
#
# (Hopefully the "rough" character diagrams above give you the
# general idea)
#
# It turns out that there are 32 possibles combinations of + and -
# and therefore 32 basic paths through the cell. And if you swap
# the (+) and (-) in the diagram above, the "same" basic path is
# generated:
#
# (+) (+) (+) | (-) (+) (+) (-) | (+)
# \ _ \
# \ / \ \
# (+) - (+) | _ /(-) | - (-) -
# / / / \
# / / / \
# (+) | (-) (+) | (-) (-) | (+) (+) | (-)
#
#
# So, it turns out that there are 16 basic paths through the cell.
#
###############################################################################
#
# The original article/code worked on all four triangles together and
# generated one of the 16 paths.
#
# For this version of the code, I split the cell into the four triangles
# and handle each triangle individually.
#
# Doing it this way is slower than the above method for calculating the
# contour lines. But since it "simplifies" the code when doing "color filled"
# contours, I opted for the longer calculation times.
#
###############################################################################
###############################################################################
#
# Implementation details :
#
# global variables :
#
# glob_var : This array hold a variety of values
#
# x : This "2d" (NxM) array holds the X coordinates of the data
# y : This "2d" (NxM) array holds the Y coordinates of the data
# f : This "2d" (NxM) array holds the "functional" value of
# the data (refer to the proc "make_grid" for more details)
#
# NOTE : The color fill algorithm still need some work.
# The problem is when more than one contour line passes through
# a given triangle, it may not color the triangle correctly.
#
# When the first contour through a triangle is calculated, I want
# to store that information in "oldlist" (see proc tri_contour),
# and then use the info in "oldlist" to help determine the proper
# shading if two or more contours pass through the triangle.
#
# At present, the code just prints a message that I
#
# "Need to handle oldlist"
#
###############################################################################
source plotchart.tcl
package require Plotchart
set rv 1 ;# rv (reverse video) : used to set
# foreground and background colors
set filled_cont true ;# set to "false" if you only want
# contour lines.
# "true" will do contour color shading.
set simple_box_contour false ;# normally to "false". If set to "true"
# the normal contouring is by-passed and
# this "grid cell" is colored based on
# the average value of the four corners
set show_grid false ;# set to "true" if you want display
# the grid.
proc plot_contour {filled_cont rv {simple_box_contour false} {show_grid false} } {
global glob_var
set pi 3.1415926
set DtoR [expr $pi / 180.0]
set glob_var(simple_box_contour) $simple_box_contour
set glob_var(filled_contour) $filled_cont
if {$rv} {
set glob_var(fg) black
set glob_var(bg) white
} else {
set glob_var(fg) white
set glob_var(bg) black
}
set sWidth [winfo screenwidth .]
set sHeight [winfo screenheight .]
if {$sWidth < $sHeight} {
set cWidth [expr {$sWidth * 0.75}]
set cHeight $cWidth
} else {
set cWidth [expr {$sHeight * 0.75}]
set cHeight $cWidth
}
set c [canvas .c -background $glob_var(bg) \
-width $cWidth -height $cHeight]
pack $c -fill both -side top
set winCoords [make_grid 20 20]
foreach {xmin xmax ymin ymax} $winCoords {break}
set xtics 4
set ytics 4
set xlimits [getScaleLimits $xmin $xmax $xtics]
set ylimits [getScaleLimits $ymin $ymax $ytics]
foreach {wxmin wxmax wxstep} $xlimits {break}
foreach {wymin wymax wystep} $ylimits {break}
set chart [::Plotchart::createXYPlot $c $xlimits $ylimits]
set fmin -1.0
set fmax 1.0
set ncont 41
contour $chart $c $fmin $fmax $ncont
if {$show_grid} {
draw_grid $chart $c
}
$c raise contour_line
update
}
proc getScaleLimits { xmin xmax ntics } {
set rmin 0.5
set rmax 5.0
set ratio [expr {($xmax - $xmin) / $ntics}]
if {$ratio <= 0.0} {
set ratio 1.0
}
set irdoff 0
set iexp [expr {int(log10($ratio))}]
set aratio [expr {log10($ratio) - $iexp}]
set armax [expr {log10($rmax)}]
if { $aratio > $armax } {
set irdoff 1
}
if { $aratio < $armax } {
set irdoff -1
}
set iexp [expr {$iexp + $irdoff}]
set pexp [expr {pow(10.0,$iexp)}]
set ratio [expr {$ratio / $pexp }]
if {$ratio > 10.0} {
incr iexp
set pexp [expr {pow(10.0,$iexp)}]
set ratio [expr {$ratio / $pexp }]
}
# Find interval to be used on graph
if { $ratio <= 1.0 } {
# Interval is one
set ticint [expr {1.0 * $pexp}]
} else {
if { $ratio <= 2.0 } {
# Interval is two
set ticint [expr {2.0 * $pexp}]
} else {
# Interval is five
set ticint [expr {5.0 * $pexp}]
}
}
if { $xmin >= 0.0 } {
set smin [expr {(int($xmin/$ticint + 1.0e-4)) * $ticint}]
} else {
set smin [expr {(int($xmin/$ticint - 0.9999)) * $ticint}]
}
set smax [expr {(int(($xmax-$xmin)/$ticint + 0.999) * $ticint) + $smin }]
return [list $smin $smax $ticint]
}
proc make_grid { {imax 100} {jmax 100} } {
global glob_var
global x y f
set pi 3.1415926
set d2r [expr $pi / 180.0]
set xmin -90.0
set xmax 90.0
set ymin -90.0
set ymax 90.0
set xrange [expr {$xmax - $xmin}]
set yrange [expr {$ymax - $ymin}]
set dx [expr {$xrange / ($imax - 1)}]
set dy [expr {$yrange / ($jmax - 1)}]
for {set i 1} {$i <= $imax} {incr i} {
for {set j 1} {$j <= $jmax} {incr j} {
set xx [expr {$xmin + ($i-1)*$dx}]
set yy [expr {$ymin + ($j-1)*$dy}]
set x($i,$j) $xx
set y($i,$j) $yy
set sx [expr {sin($xx * $d2r)}]
set cx [expr {cos($yy * $d2r)}]
set f($i,$j) [expr {($sx*$cx)}]
}
}
set glob_var(imax) $imax
set glob_var(jmax) $jmax
# return [list -100.0 100.0 -100.0 100.0]
return [list $xmin $xmax $ymin $ymax]
}
proc draw_grid {chart canv} {
global glob_var
global x y f
$chart dataconfig "grid" -colour "black"
for {set i 1} {$i <= $glob_var(imax)} {incr i} {
set xylist {}
for {set j 1} {$j <= $glob_var(jmax)} {incr j} {
lappend xylist $x($i,$j) $y($i,$j)
}
c_line $chart $canv $xylist black
}
for {set j 1} {$j <= $glob_var(jmax)} {incr j} {
set xylist {}
for {set i 1} {$i <= $glob_var(imax)} {incr i} {
lappend xylist $x($i,$j) $y($i,$j)
}
c_line $chart $canv $xylist black
}
}
proc contour {chart canv fmin fmax ncont} {
global glob_var
global x y f
global cont
set df [expr {($fmax - $fmin) / ($ncont - 1)}]
set dh [expr {240.0 / ($ncont - 1)}]
puts "Fmin = $fmin : Fmax = $fmax : Ncont = $ncont"
# "ncont" is the number of contour lines. Add one
# to get the number of intervals. Therefore calculate
# the colors from zero to ncont.
#
for {set i 0} {$i <= $ncont} {incr i} {
set cont($i,fval) [expr {$fmin + ($i * $df)}]
set cont($i,hue) [expr {240.0 - ($i * $dh)}]
if {$cont($i,hue) < 0.01} {
set cont($i,hue) 0.0
}
set rgbList [hsv2rgb $cont($i,hue) 1.0 1.0]
set r [expr {int([lindex $rgbList 0] * 65535)}]
set g [expr {int([lindex $rgbList 1] * 65535)}]
set b [expr {int([lindex $rgbList 2] * 65535)}]
set cont($i,color) [format "#%.4x%.4x%.4x" $r $g $b]
}
for {set i 1} {$i < $glob_var(imax)} {incr i} {
set i1 [expr {$i + 1}]
for {set j 1} {$j < $glob_var(jmax)} {incr j} {
set j1 [expr {$j + 1}]
set x1 $x($i1,$j)
set x2 $x($i,$j)
set x3 $x($i,$j1)
set x4 $x($i1,$j1)
set y1 $y($i1,$j)
set y2 $y($i,$j)
set y3 $y($i,$j1)
set y4 $y($i1,$j1)
set f1 $f($i1,$j)
set f2 $f($i,$j)
set f3 $f($i,$j1)
set f4 $f($i1,$j1)
set xb [list $x1 $x2 $x3 $x4]
set yb [list $y1 $y2 $y3 $y4]
set fb [list $f1 $f2 $f3 $f4]
box_contour $chart $canv $xb $yb $fb $fmin $fmax $ncont
}
}
## for {set i 0} {$i <= $ncont} {incr i} {
## puts "Contour level $i : Fval = $cont($i,fval)"
## }
}
proc box_contour {chart canv xb yb fb fmin fmax ncont} {
global glob_var
global x y f
global cont
foreach {x1 x2 x3 x4} $xb {}
foreach {y1 y2 y3 y4} $yb {}
foreach {f1 f2 f3 f4} $fb {}
set xc [expr {($x1 + $x2 + $x3 + $x4) * 0.25}]
set yc [expr {($y1 + $y2 + $y3 + $y4) * 0.25}]
set fc [expr {($f1 + $f2 + $f3 + $f4) * 0.25}]
if {$glob_var(simple_box_contour)} {
set ic [expr {int( $ncont * ($fc-$fmin) / ($fmax-$fmin) )}]
if {$ic < 0} {
set ic 0
}
if {$ic > [expr {$ncont - 1}]} {
set ic [expr {$ncont - 1}]
}
set xylist [list $x1 $y1 $x2 $y2 $x3 $y3 $x4 $y4]
# canvasPlot::polygon $win $xylist -fill $cont($ic,color)
c_polygon $chart $canv $xylist $cont($ic,color)
} else {
tri_contour $chart $canv $x1 $y1 $f1 $x2 $y2 $f2 $xc $yc $fc $fmin $fmax $ncont
tri_contour $chart $canv $x2 $y2 $f2 $x3 $y3 $f3 $xc $yc $fc $fmin $fmax $ncont
tri_contour $chart $canv $x3 $y3 $f3 $x4 $y4 $f4 $xc $yc $fc $fmin $fmax $ncont
tri_contour $chart $canv $x4 $y4 $f4 $x1 $y1 $f1 $xc $yc $fc $fmin $fmax $ncont
}
}
proc tri_contour { chart canv x1 y1 f1 x2 y2 f2 x3 y3 f3 fmin fmax ncont } {
global glob_var
global x y f
global cont
set frange [expr {$fmax - $fmin}]
set nc [expr {$ncont - 1}]
set df [expr {$frange / $nc}]
# Find the min/max function values for this triangle
#
set tfmin [min $f1 $f2 $f3]
set tfmax [max $f1 $f2 $f3]
# Based on the above min/max, figure out which
# contour levels/colors that bracket this interval
#
set imin 0
for {set i 0} {$i < $ncont} {incr i} {
set ff $cont($i,fval)
if {$ff <= $tfmin} {
set imin $i
set imax $i
}
if {$ff >= $tfmin && $ff <= $tfmax} {
set imax $i
}
}
# First handle the simple case where one color can
# be used to color the entire triangle...
#
if {$glob_var(filled_contour)} {
if {$imin == $imax} {
set ictmp $imin
if {$cont($imin,fval) < $tfmax} {
incr ictmp
}
set pxylist [list $x1 $y1 $x2 $y2 $x3 $y3]
c_polygon $chart $canv $pxylist $cont($ictmp,color)
return
}
}
set oldlist {}
set vertlist {}
# Since the simple above has already been handled,
# make sure that the contour levels go past "tfmax"
#
incr imax
# Loop over all contour levels of interest for this triangle
#
for {set ic $imin} {$ic <= $imax} {incr ic} {
# Get the value for this contour level
#
set ff $cont($ic,fval)
set pxylist {}
# First check to see if the current contour value "ff"
# falls within the min/max values for this triangle
#
if {$ff >= $tfmin && $ff <= $tfmax} {
# Determine if the current contour level
# intersects each side of the triangle
#
set side12 [expr {($ff - $f1) * ($ff - $f2)}]
set side23 [expr {($ff - $f2) * ($ff - $f3)}]
set side31 [expr {($ff - $f3) * ($ff - $f1)}]
if {$side12 <= 0.0} {
# It intersects this side : between points 1 & 2
# Calculate the intersection point 1
#
set xyf1 [fintpl $x1 $y1 $f1 $x2 $y2 $f2 $ff]
# Add this point to the current polygon point list
#
set pxylist $xyf1
if {$side23 <= 0.0} {
# It intersects this side : between points 2 & 3
# Calculate the intersection point 2
#
set xyf2 [fintpl $x2 $y2 $f2 $x3 $y3 $f3 $ff]
# Add this point to the current polygon point list
#
foreach {xx yy} $xyf2 {}
lappend pxylist $xx $yy
foreach {xx1 yy1 xx2 yy2} $pxylist {break}
if {$glob_var(filled_contour)} {
if {[llength $oldlist] == 0} {
if {$f2 < $ff} {
lappend pxylist $x2 $y2
lappend vertlist 2
} else {
lappend pxylist $x3 $y3 $x1 $y1
lappend vertlist 3 1
}
c_polygon $chart $canv $pxylist $cont($ic,color)
} else {
set lst1 [lindex $oldlist 0]
set lst2 [lindex $oldlist 1]
foreach {side1 ox1 oy1} $lst1 {break}
foreach {side2 ox2 oy2} $lst2 {break}
if {$side1 == "side12" && $side2 == "side23"} {
lappend pxylist $ox2 $oy2 $ox1 $oy1
} else {
if {$side1 == "side12" && $side2 == "side31"} {
lappend pxylist $x3 $y3 $ox2 $oy2 $ox1 $oy1
} else {
lappend pxylist $ox1 $oy1 $ox2 $oy2 $x1 $y1
}
}
c_polygon $chart $canv $pxylist $cont($ic,color)
}
set oldlist {}
lappend oldlist [list "side12" $xx1 $yy1]
lappend oldlist [list "side23" $xx2 $yy2]
}
} else {
# It intersects this side : between points 3 & 1
# Calculate the intersection point 2
#
set xyf2 [fintpl $x3 $y3 $f3 $x1 $y1 $f1 $ff]
foreach {xx yy} $xyf2 {}
lappend pxylist $xx $yy
foreach {xx1 yy1 xx2 yy2} $pxylist {break}
if {$glob_var(filled_contour)} {
if {[llength $oldlist] == 0} {
if {$f1 < $ff} {
lappend pxylist $x1 $y1
lappend vertlist 1
} else {
lappend pxylist $x3 $y3 $x2 $y2
lappend vertlist 3 2
}
c_polygon $chart $canv $pxylist $cont($ic,color)
} else {
set lst1 [lindex $oldlist 0]
set lst2 [lindex $oldlist 1]
foreach {side1 ox1 oy1} $lst1 {break}
foreach {side2 ox2 oy2} $lst2 {break}
if {$side1 == "side12" && $side2 == "side31"} {
lappend pxylist $ox2 $oy2 $ox1 $oy1
} else {
if {$side1 == "side12" && $side2 == "side23"} {
lappend pxylist $x3 $y3 $ox2 $oy2 $ox1 $oy1
} else {
lappend pxylist $ox2 $oy2 $ox1 $oy1 $x2 $y2
}
}
c_polygon $chart $canv $pxylist $cont($ic,color)
}
set oldlist {}
lappend oldlist [list "side12" $xx1 $yy1]
lappend oldlist [list "side31" $xx2 $yy2]
}
}
} else {
set xyf1 [fintpl $x2 $y2 $f2 $x3 $y3 $f3 $ff]
set xyf2 [fintpl $x3 $y3 $f3 $x1 $y1 $f1 $ff]
foreach {xx1 yy1} $xyf1 {}
lappend pxylist $xx1 $yy1
foreach {xx2 yy2} $xyf2 {}
lappend pxylist $xx2 $yy2
if {$glob_var(filled_contour)} {
if {[llength $oldlist] == 0} {
if {$f3 < $ff} {
lappend pxylist $x3 $y3
lappend vertlist 3
} else {
lappend pxylist $x1 $y1 $x2 $y2
lappend vertlist 1 2
}
c_polygon $chart $canv $pxylist $cont($ic,color)
} else {
set lst1 [lindex $oldlist 0]
set lst2 [lindex $oldlist 1]
foreach {side1 ox1 oy1} $lst1 {break}
foreach {side2 ox2 oy2} $lst2 {break}
if {$side1 == "side23" && $side2 == "side31"} {
lappend pxylist $ox2 $oy2 $ox1 $oy1
} else {
if {$side1 == "side12" && $side2 == "side23"} {
lappend pxylist $x1 $y1 $ox1 $oy1 $ox2 $oy2
} else {
lappend pxylist $ox2 $oy2 $ox1 $oy1 $x2 $y2
}
}
c_polygon $chart $canv $pxylist $cont($ic,color)
}
set oldlist {}
lappend oldlist [list "side23" $xx1 $yy1]
lappend oldlist [list "side31" $xx2 $yy2]
}
}
# Construct the contour line from the intersection
# points calculated above
#
foreach {xx1 yy1} $xyf1 {}
foreach {xx2 yy2} $xyf2 {}
set xylist [list $xx1 $yy1 $xx2 $yy2]
# Draw the contour line. If we're doing a "filled contour"
# (ie coloring each triangle) then use "black" as the color
# of the line, otherwise use the "contour color" for the line.
# A tag is used so that we can "raise/lower" the line if desired.
#
if {$glob_var(filled_contour)} {
# c_line $chart $canv $xylist $cont($ic,color)
c_line $chart $canv $xylist black -tag contour_line
} else {
c_line $chart $canv $xylist $cont($ic,color)
}
} else {
# The current contour value "ff" is such that either :
# ff < tfmin
# ff > tfmax
if {$glob_var(filled_contour)} {
if {[llength $oldlist] == 0} {
set pxylist [list $x1 $y1 $x2 $y2 $x3 $y3]
c_polygon $chart $canv $pxylist $cont($ic,color)
} else {
# We should only reach this point when
# ic == imax and we need to finish the color fill
# for this triangle
#
set lst1 [lindex $oldlist 0]
set lst2 [lindex $oldlist 1]
foreach {side1 ox1 oy1} $lst1 {break}
foreach {side2 ox2 oy2} $lst2 {break}
if {$side1 == "side12" && $side2 == "side23"} {
if {[lsearch $vertlist "2"] == -1} {
set pxylist [list $ox1 $oy1 $ox2 $oy2 $x2 $y2 ]
} else {
set pxylist [list $ox1 $oy1 $ox2 $oy2 $x3 $y3 $x1 $y1 ]
}
}
if {$side1 == "side12" && $side2 == "side31"} {
if {[lsearch $vertlist "1"] == -1} {
set pxylist [list $ox1 $oy1 $ox2 $oy2 $x1 $y1 ]
} else {
set pxylist [list $ox1 $oy1 $ox2 $oy2 $x3 $y3 $x2 $y2 ]
}
}
if {$side1 == "side23" && $side2 == "side31"} {
if {[lsearch $vertlist "3"] == -1} {
set pxylist [list $ox1 $oy1 $ox2 $oy2 $x3 $y3 ]
} else {
set pxylist [list $ox1 $oy1 $ox2 $oy2 $x1 $y1 $x2 $y2 ]
}
}
c_polygon $chart $canv $pxylist $cont($ic,color)
set oldlist {}
}
}
}
}
}
proc fintpl {x1 y1 f1 x2 y2 f2 ff} {
if {[expr {$f2 - $f1}] != 0.0} {
set xx [expr {$x1 + (($ff - $f1)*($x2 - $x1)/($f2 - $f1))}]
set yy [expr {$y1 + (($ff - $f1)*($y2 - $y1)/($f2 - $f1))}]
} else {
# puts "FINTPL : f1 == f2 : x1,y1 : $x1 , $y1 : x2,y2 : $x2 , $y2"
set xx $x1
set yy $y1
}
set xmin [min $x1 $x2]
set xmax [max $x1 $x2]
set ymin [min $y1 $y2]
set ymax [max $y1 $y2]
if {$xx < $xmin} { set xx $xmin }
if {$xx > $xmax} { set xx $xmax }
if {$yy < $ymin} { set yy $ymin }
if {$yy > $ymax} { set yy $ymax }
return [list $xx $yy]
}
proc min { val args } {
set min $val
foreach val $args {
if { $val < $min } {
set min $val
}
}
return $min
}
proc max { val args } {
set max $val
foreach val $args {
if { $val > $max } {
set max $val
}
}
return $max
}
proc c_line {chart canv xylist color args} {
set wxylist {}
foreach {xx yy} $xylist {
foreach {pxcrd pycrd} [::Plotchart::coordsToPixel $canv $xx $yy] {break}
lappend wxylist $pxcrd $pycrd
}
eval "$canv create line $wxylist -fill $color $args"
}
proc c_polygon {chart canv xylist color args} {
set wxylist {}
foreach {xx yy} $xylist {
foreach {pxcrd pycrd} [::Plotchart::coordsToPixel $canv $xx $yy] {break}
lappend wxylist $pxcrd $pycrd
}
eval "$canv create polygon $wxylist -fill $color $args"
}
########################################################################
# The following two routines were borrowed from :
#
# http://mini.net/cgi-bin/wikit/666.html
########################################################################
# rgb2hsv --
#
# Convert a color value from the RGB model to HSV model.
#
# Arguments:
# r g b the red, green, and blue components of the color
# value. The procedure expects, but does not
# ascertain, them to be in the range 0 to 1.
#
# Results:
# The result is a list of three real number values. The
# first value is the Hue component, which is in the range
# 0.0 to 360.0, or -1 if the Saturation component is 0.
# The following to values are Saturation and Value,
# respectively. They are in the range 0.0 to 1.0.
#
# Credits:
# This routine is based on the Pascal source code for an
# RGB/HSV converter in the book "Computer Graphics", by
# Baker, Hearn, 1986, ISBN 0-13-165598-1, page 304.
#
proc rgb2hsv {r g b} {
set h [set s [set v 0.0]]]
set sorted [lsort -real [list $r $g $b]]
set v [expr {double([lindex $sorted end])}]
set m [lindex $sorted 0]
set dist [expr {double($v-$m)}]
if {$v} {
set s [expr {$dist/$v}]
}
if {$s} {
set r' [expr {($v-$r)/$dist}] ;# distance of color from red
set g' [expr {($v-$g)/$dist}] ;# distance of color from green
set b' [expr {($v-$b)/$dist}] ;# distance of color from blue
if {$v==$r} {
if {$m==$g} {
set h [expr {5+${b'}}]
} else {
set h [expr {1-${g'}}]
}
} elseif {$v==$g} {
if {$m==$b} {
set h [expr {1+${r'}}]
} else {
set h [expr {3-${b'}}]
}
} else {
if {$m==$r} {
set h [expr {3+${g'}}]
} else {
set h [expr {5-${r'}}]
}
}
set h [expr {$h*60}] ;# convert to degrees
} else {
# hue is undefined if s == 0
set h -1
}
return [list $h $s $v]
}
# hsv2rgb --
#
# Convert a color value from the HSV model to RGB model.
#
# Arguments:
# h s v the hue, saturation, and value components of
# the color value. The procedure expects, but
# does not ascertain, h to be in the range 0.0 to
# 360.0 and s, v to be in the range 0.0 to 1.0.
#
# Results:
# The result is a list of three real number values,
# corresponding to the red, green, and blue components
# of a color value. They are in the range 0.0 to 1.0.
#
# Credits:
# This routine is based on the Pascal source code for an
# HSV/RGB converter in the book "Computer Graphics", by
# Baker, Hearn, 1986, ISBN 0-13-165598-1, page 304.
#
proc hsv2rgb {h s v} {
set v [expr {double($v)}]
set r [set g [set b 0.0]]
if {$h == 360} { set h 0 }
# if you feed the output of rgb2hsv back into this
# converter, h could have the value -1 for
# grayscale colors. Set it to any value in the
# valid range.
if {$h == -1} { set h 0 }
set h [expr {$h/60}]
set i [expr {int(floor($h))}]
set f [expr {$h - $i}]
set p1 [expr {$v*(1-$s)}]
set p2 [expr {$v*(1-($s*$f))}]
set p3 [expr {$v*(1-($s*(1-$f)))}]
switch -- $i {
0 { set r $v ; set g $p3 ; set b $p1 }
1 { set r $p2 ; set g $v ; set b $p1 }
2 { set r $p1 ; set g $v ; set b $p3 }
3 { set r $p1 ; set g $p2 ; set b $v }
4 { set r $p3 ; set g $p1 ; set b $v }
5 { set r $v ; set g $p1 ; set b $p2 }
}
return [list $r $g $b]
}
#
# Main code
#
plot_contour $filled_cont $rv $simple_box_contour $show_grid