As a late night project, I converted some VB code I wrote years ago to create an animated starfield. My original VB code can be found here [L1 ]. I must say that the job was much easier, and required less code in TK than my original VB version. Anyway, it's not perfect, and not *quite* as complete as the original, but it's kind of interesting... Try clicking and dragging the mouse in canvas area. - Jeff Godfrey
proc main {} { initVars buildUI getNewVortex [expr {$::global(sfWidth) / 2}] [expr {$::global(sfHeight) / 2}] initStars # --- give us a way out... bind . <Control-c> {destroy .} animLoop } proc initVars {} { set ::global(sfWidth) 800 set ::global(sfHeight) 600 set ::global(maxStars) 100 } proc initStars {} { set xl $::global(xlVortex) set xh $::global(xhVortex) set yl $::global(ylVortex) set yh $::global(yhVortex) for {set i 1} {$i <= $::global(maxStars)} {incr i} { set xs [expr {($xh - $xl - 1) * rand() + $xl}] set ys [expr {($yh - $yl - 1) * rand() + $yl}] set me [.c1 create line $xs $ys $xs $ys -width 2 -fill black -tag "star"] # --- calculate a random star speed from .1 to 1.1 set ::global(speed,$me) [expr {rand() + 0.1}] } } proc animLoop {} { set xCen $::global(xCenVortex) set yCen $::global(yCenVortex) set sfWidth $::global(sfWidth) set sfHeight $::global(sfHeight) set xlVortex $::global(xlVortex) set xhVortex $::global(xhVortex) set ylVortex $::global(ylVortex) set yhVortex $::global(yhVortex) foreach star [.c1 find withtag "star"] { set starCoords [.c1 coords $star] set xs [lindex $starCoords 0] set ys [lindex $starCoords 1] set xe [lindex $starCoords 2] set ye [lindex $starCoords 3] set speed $::global(speed,$star) # --- calculate the X and Y distances from the current vortex center set xVector [expr {abs($xCen - $xe)}] set yVector [expr {abs($yCen - $ye)}] # --- calculate the star's X direction and length based on # the current vortex X center if {$xe > $xCen} { set newXe [expr {$xe + int($xVector * 0.2) * $speed}] } else { set newXe [expr {$xe - int($xVector * 0.2) * $speed}] } # --- calculate the star's Y direction and length based on # the current vortex Y center if {$ye > $yCen} { set newYe [expr {$ye + int($yVector * 0.2) * $speed}] } else { set newYe [expr {$ye - int($yVector * 0.2) * $speed}] } # --- if new start coord is off the screen, reset it "near" the # vortex center if {$xe < 0 || $xe > $sfWidth || $ye < 0 || $ye > $sfHeight} { set xs [expr {($xhVortex - $xlVortex - 1) * rand() + $xlVortex}] set ys [expr {($yhVortex - $ylVortex - 1) * rand() + $ylVortex}] .c1 coords $star $xs $ys $xs $ys .c1 itemconfigure $star -fill black } else { set range [getStarRange $xe $ye] set colorVector [expr {$range * 25}] set color [format "#%x%x00" $colorVector $colorVector] .c1 coords $star $xe $ye $newXe $newYe .c1 itemconfigure $star -fill $color } } update after 1 animLoop } # --- Calculate a star's brightness based on its distance from the vortex. # Since this is called within the animation loop for *every* star, it is # computationally expensive. This should be improved, but it'll do for # now... proc getStarRange {x y} { set xVector [expr {abs($::global(xCenVortex) - $x)}] set yVector [expr {abs($::global(yCenVortex) - $y)}] # --- Calculate the distance from vortex center set dist [expr {sqrt($xVector * $xVector + $yVector * $yVector)}] # --- return a value in the range of 1-10 set range [expr {int(($dist / $::global(maxRad)) * 10)}] if {$range < 1} { set range 1 } elseif {$range > 10} { set range 10 } return $range } proc getNewVortex {xVortexCen yVortexCen} { set xCen $xVortexCen set yCen $yVortexCen # --- calculate a range distance from teh vortex center set xOffset [expr {int($::global(sfWidth) * 0.1)}] set yOffset [expr {int($::global(sfHeight) * 0.1)}] # --- calculate the GLOBAL actual range for both axis' # a new star will always be "born" within this area... set xlVortex [expr {$xCen - $xOffset}] set xhVortex [expr {$xCen + $xOffset}] set ylVortex [expr {$yCen - $yOffset}] set yhVortex [expr {$yCen + $yOffset}] # --- Calculate a "maximum screen radius". This is used in the # star's brightness calculation if {$::global(sfWidth) < $::global(sfHeight)} { set maxRad [expr {$::global(sfWidth) / 2}] } else { set maxRad [expr {$::global(sfHeight) / 2}] } set ::global(xCenVortex) $xCen set ::global(yCenVortex) $yCen set ::global(xlVortex) $xlVortex set ::global(xhVortex) $xhVortex set ::global(ylVortex) $ylVortex set ::global(yhVortex) $yhVortex set ::global(maxRad) $maxRad } proc buildUI {} { canvas .c1 \ -width $::global(sfWidth) \ -height $::global(sfHeight) \ -background black \ -highlightthickness 0 \ -borderwidth 0 pack .c1 -fill both -expand 1 bind .c1 <B1-Motion> {getNewVortex %x %y} bind .c1 <ButtonPress-1> {getNewVortex %x %y} wm title . "StarField Demo" } main