GS 2012-002-18:
Vogel spiral is a variant of Ulam spiral. Integers are ploted on an Fermat's spiral instead of a square spiral with polar coordinates:
The first screenshot represents prime factors and the second the divisors of n.
PYK 2013-08-21:
Started editing code to remove [update] from loops, but then it turned into a project to practice the art of programming in Tk. Any tips and/or productive modifications to the code are certainly welcome! This revision runs noticeably faster than the previous one, probably due to changes that take better advantage of byte compilation. It also illustrates a method for distinguishing true key events from auto-repeated ones, which is used to make the accelerating spinbox work with arrow keys. See revision 4 of this page for the previous version.
Improvements
New features
# vogel-spiral.tcl # Author: Gerard Sookahet # Modified by: Poor Yorick, 2013-08 # Date: 2012-02-28 # Description: Plot Vogel prime spiral and Vogel divisor spiral package require Tk package require platform namespace import ::msgcat::* ### Utilities ### proc nsproc {name args vars body} { foreach var $vars { lappend pre [list variable $var] } lappend pre $body proc $name $args [join $pre \n] } ### UI Utilities ### proc truekeypress {w serial arrayname} { upvar $arrayname state set state(press) $serial if {$serial ne $state(release)} { event generate $w <<TrueKeyPress>> } } proc truekeyrelease {w serial arrayname} { upvar $arrayname state set state(release) $serial after 0 [list apply [list {w arrayname} { upvar $arrayname state if {$state(release) ne $state(press)} { event generate $w <<TrueKeyRelease>> } } [namespace current]] $w $arrayname] } # Arbitrary color table proc colormap n { set lcolor { #030303 #CD0000 #CD4F39 #EE4000 #EE6A50 #FF7F00 #EE9A00 #FF8C69 #FFC125 #EEEE00 #EED5B7 #D2691E #BDB76B #00FFFF #7FFFD4 #FFEFD5 #AB82FF #E066FF } return [lindex $lcolor $n] } ### UI Control ### nsproc languageSet w windows { set locale [$w.p.choice.language get] mclocale $locale wm title [winfo toplevel $w] [mc %vogelSpiral] foreach varname [array names windows $w,*] { set windows($varname) $windows($varname) } foreach {path text} {bu %vogelSpiral bd %divisorSpiral bq %quit br %reset} { $w.f1.$path configure -text [mc $text] } $w.p.choose.label configure -text [mc %parameter] set selected {} if {[llength [array names windows $w,parameters]] != 0} { set selected [dict get $windows($w,parameters) [$w.p.choose.from get]] } set windows($w,parameters) [dict create] foreach parameter [info procs [namespace current]::param_*] { set param [string range $parameter [expr {[string first _ $parameter] + 1}] end] set text [mc %$param] dict set windows($w,parameters) $text $param lappend parameters $text } if {$selected ne {}} { $w.p.choose.from set [mc %$selected] } $w.p.choose.from configure -values $parameters } proc param_refresh {w} { pack {*}[winfo children $w.p.choice.refresh] -side left } proc param_rscript {w} { set wrscript $w.p.choice.rscript $wrscript.presets configure -values { {$refresh + 1} {$refresh + 10} {$refresh + 100} {$refresh - 100} {entier($refresh + ($refresh * .20))} } pack {*}[winfo children $wrscript] -anchor w } proc param_language w { set wrscript $w.p.choice.language } nsproc spin w windows { set rate [$w.p.choice.refresh.set get] if {$rate eq $windows($w,refresh)} { ::ttk::style configure TSpinbox -background blue -foreground white ::ttk::style configure TSpinbox -fieldbackground blue } else { ::ttk::style configure TSpinbox -background pink ::ttk::style configure TSpinbox -fieldbackground $windows($w,warnbackground) msg $w %confirm warn if {[string is entier -strict $rate] && [scan $rate %d rate] > 0} { set increment $windows($w,spinincr) if {$increment == 0} { set increment 1 } elseif {$rate % ($increment * 10) == 0} { set increment [expr {$increment * 10}] } $w.p.choice.refresh.set configure -increment $increment $w.p.choice.refresh.set configure -from [expr {$rate - $increment}] $w.p.choice.refresh.set configure -to [expr {$rate + $increment}] set windows($w,spinincr) $increment } } } nsproc refreshChange w windows { ::ttk::style configure TSpinbox -background blue ::ttk::style configure TSpinbox -fieldbackground blue set rate [$w.p.choice.refresh.set get] if {[string is entier -strict $rate] && [scan $rate %d rate] > 0} { msg $w %confirmed normal set windows($w,refresh) $rate } } nsproc msg {w msg {level normal}} windows { set windows($w,status) $msg ::ttk::style configure Status.TLabel -foreground $windows($w,msg$level) $w.status configure -text [mc $msg] } nsproc reset w {tasks windows} { $windows($w,pix) blank foreach task $windows($w,tasks) { set tasks($task,alive) 0 } } nsproc spinSet {w val} windows { set windows($w,spinincr) $val $w.p.choice.refresh.set configure -increment 1 } nsproc rscriptSet w windows { set windows($w,rscript) [$w.p.choice.rscript.presets get] msg $w %confirmed normal } nsproc spiral {w N} {windows} { foreach {name val} { rscript {} spinincr 0 refresh 0 tasks {} N $N primecolor #00FFFF refreshtask {} warnbackground pink msgnormal black msgwarn pink4 nonprimecolor #606060 pix {[image create photo]} } { array set windows [list $w,$name [subst $val]] } if {[tk windowingsystem] ni [list aqua win32]} { ::ttk::style configure TButton -background blue -foreground white ::ttk::style configure TCombobox -background blue -fieldbackground blue -foreground white } ::ttk::style configure TEntry -background blue -foreground white ::ttk::style configure TSpinbox -background blue -foreground white ::ttk::style configure ComboboxPopdownFrame -fieldbackground blue -background blue set dim [expr {int(sqrt($N) + 10)}] set windows($w,mid) [expr {$dim/2}] canvas $w.c -width $dim -height $dim -bg black $w.c create image 0 0 -anchor nw -image $windows($w,pix) pack $w.c set f1 [frame $w.f1 -relief sunken -borderwidth 2] pack $f1 -fill x ::ttk::button $f1.bu -width 12 -command [list PlotVogel $w prime] ::ttk::button $f1.bd -width 12 -command [list PlotVogel $w divisor] ::ttk::button $f1.bq -width 5 -command [list spiralDestroy $w] ::ttk::button $f1.br -width 5 -command [list reset $w] pack {*}[winfo children $f1] -side left frame $w.p frame $w.p.choose ::ttk::label $w.p.choose.label -text [mc %parameters] ::ttk::combobox $w.p.choose.from -width 15 bind $w.p.choose.from <<ComboboxSelected>> [list apply [list w { variable windows pack forget {*}[winfo children $w.p.choice] set param [dict get $windows($w,parameters) [$w.p.choose.from get]] param_$param $w pack $w.p.choice.$param } [namespace current]] $w] pack {*}[winfo children $w.p.choose] -side left frame $w.p.choice frame $w.p.choice.refresh ::ttk::spinbox $w.p.choice.refresh.set -width 8 -from 0 -to 100 -increment 1 -command [list spin $w] ::ttk::label $w.p.choice.refresh.current -width 8 bind $w.p.choice.refresh.set <ButtonRelease> [list spinSet $w 0] bind $w.p.choice.refresh.set <<TrueKeyRelease>> [bind $w.p.choice.refresh.set <ButtonRelease>] bind $w.p.choice.refresh.set <Return> [list refreshChange $w] bind $w.p.choice.refresh.set <<TrueKeyPress>> [list spin $w] trace add variable windows($w,refresh) write [list apply [list {w args} { variable windows $w.p.choice.refresh.current configure -text $windows($w,refresh) } [namespace current]] $w] $w.p.choice.refresh.set set 0 frame $w.p.choice.rscript ::ttk::combobox $w.p.choice.rscript.presets bind $w.p.choice.rscript.presets <Return> [list rscriptSet $w] bind $w.p.choice.rscript.presets <<ComboboxSelected>> [list [namespace which msg] $w %%confirm warn] ::ttk::combobox $w.p.choice.language -values {en ru} $w.p.choice.language set en bind $w.p.choice.language <<ComboboxSelected>> [list [namespace which languageSet] $w] pack $w.p -fill x pack {*}[winfo children $w.p] -side left -anchor w ::ttk::label $w.status -style Status.TLabel -justify left -relief sunken trace add variable windows($w,status) write [list apply [list {w args} { variable windows $w.status configure -text [mc $windows($w,status)] } [namespace current]] $w] set windows($w,status) %intro pack $w.status -fill x languageSet $w } nsproc spiralDestroy w windows { reset $w image delete $windows($w,pix) array unset windows $w,* destroy $w } ### Control ### nsproc ticktock task {tasks windows} { if {!$tasks($task,alive)} { #time for task to die return -level 2 } set w $tasks($task,w) if {$windows($w,refreshtask) eq {} && $windows($w,refresh) != 0} { refreshNew $w } } nsproc refresh task {tasks windows} { ticktock $task set w $tasks($task,w) set refresh $windows($w,refresh) after idle [list after 0 [list PlotVogel $w $windows($w,type)]] if {$refresh != 0} { if {$windows($w,rscript) ne {}} { set windows($w,refresh) [expr $windows($w,rscript)] } after idle [list after $windows($w,refresh) [list refresh $task]] } } nsproc refreshNew w {tasks windows} { set task [task w $w alive 1] lappend windows($w,tasks) $task set windows($w,refreshtask) $task after idle [list after $windows($w,refresh) [list refresh $task]] return $task } nsproc task args tasks { set task [info cmdcount] foreach {name val} $args { set tasks($task,$name) $val } return $task } ### Math ### nsproc PlotVogel {w type} {tasks windows} { set windows($w,type) $type set N $windows($w,N) set mid $windows($w,mid) $windows($w,pix) blank set task [task w $w alive 1] lappend windows($w,tasks) $task set xo $mid set yo $mid set M [expr {$N/4}] if {$type == {prime}} { after idle [list after 0 [list PlotVogelPrime $task 1 $M $xo $yo]] } else { #set cmap #030303 after idle [list after 0 [list PlotVogelDivisor $task 1 $M $xo $yo]] } } nsproc PlotVogelPrime {task n M xo yo} {tasks windows 2pi phi} { ticktock $task if {$n < $M} { set sqn [expr {sqrt($n)}] set 2piphi [expr {$2pi*$n/$phi/$phi}] set x [expr {int(cos($2piphi)*$sqn) + $xo}] set y [expr {int(sin($2piphi)*$sqn) + $yo}] set pix $windows($tasks($task,w),pix) if {[IsPrime $n]} { $pix put $windows($tasks($task,w),primecolor) -to $x $y } else { $pix put $windows($tasks($task,w),nonprimecolor) -to $x $y } after idle [list after 0 \ [list [namespace which [lindex [info level 0] 0]] $task [incr n] $M $xo $yo]] } } nsproc PlotVogelDivisor {task n M xo yo} {tasks windows 2pi phi} { ticktock $task if {$n < $M} { set sqn [expr {sqrt($n)}] set 2piphi [expr {$2pi*$n/$phi/$phi}] set x [expr {int(cos($2piphi)*$sqn) + $xo}] set y [expr {int(sin($2piphi)*$sqn) + $yo}] $windows($tasks($task,w),pix) put [colormap [NbDivisor $n]] -to $x $y after idle [list after 0 \ [list [namespace which [lindex [info level 0] 0]] $task [incr n] $M $xo $yo]] } } # Primality testing proc IsPrime n { if {$n == 1} {return 0} set max [expr {int(sqrt($n))}] set d 2 while {$d <= $max} { if {$n % $d == 0} {return 0} incr d } return 1 } # Return the number of divisors of an integer proc NbDivisor n { set max [expr {int(sqrt($n))}] set nd 0 for {set i 2} {$i <= $max} {incr i} { if {$n % $i == 0} {incr nd} } return $nd } bind all <Escape> {exit} bind all <KeyPress> +[list [namespace which truekeypress] %W %# [namespace current]::truepress] bind all <KeyRelease> +[list [namespace which truekeyrelease] %W %# [namespace current]::truepress] variable 2pi 6.28318531 variable phi [expr {(1+sqrt(5))/2.0}] variable truepress array set [namespace current]::truepress [list press 0 release 0] # Set up localisation variable messages { %confirm {en {press enter to confirm the setting} ru {нажать enter чтобы подвердить выбор}} %confirmed {en {setting confirmed} ru {выбор подверждён}} %intro {en ... ru ...} %divisorSpiral {en {divisor spiral} ru {спираль делителя}} %language {en language ru язык} %parameter {en parameter ru параметр} %quit {en Quit ru выход} %refresh {en refresh ru обновить} %reset {en reset ru сброс} %rscript {en {refresh routine} ru {режим обновления}} %vogelSpiral {en {Vogel spiral} ru {спираль Вогеля}} } foreach {name languages} $messages { foreach {language message} $languages { mcset $language $name $message } } proc main {count N} { set w .[info cmdcount] toplevel $w wm withdraw . while {[incr i] <= $count} { wm geometry $w +[incr x 200]+10 frame $w.spiral$i spiral $w.spiral$i $N } pack {*}[winfo children $w] } # The maximum integer. The canvas is sized from its square root main 1 100000 main 2 100000