## Ulam Spiral Demo

KWJ ULAM SPIRAL DEMO ---

This is a Tcl/Tk script which illustrates a method for generating Ulam's Spiral, see also Prime Number Browser and Primal Screens. In the figure, we have the integers from 1 to 49 laid out in a spiral pattern as first described by Stanislau Ulam, see [1] http://mathworld.wolfram.com/PrimeSpiral.html . The blue Horizontal and Vertical lines passing through the number 1, are for reference purposes. The two black Diagonal lines traversing the upper left and lower right quadrants pass through integers which are perfect squares. The upper left line passes thru numbers of the form y = M*M, where M is an even integer. The lower right line passes thru values where M is odd.

Let M be 2, and wrap (2*M - 1) cells in an inverted L pattern to the right of and around the single starting cell, creating a square pattern of four cells. For the next iteration, let M be 3, and again, wrap (2*M - 1) cells in an L shaped pattern around the last four cells, creating a new pattern of nine cells. The third iteration for M = 4, wraps another (2*M - 1) cells about these last cells. The terminating cell always contains the integer M*M, and falls on one of the lines of even or odd squres. Prime numbers are printed in bold, non primes are in italics. Every time the "Click_Here" button is depressed, M is incremented by one and a new wrapping layer is added. See Primal Screens for further information about the significance of the lines of Even and Odd Squares. For further background, see the fine page by Gerard Sookahet on the Wiki, at Ulam spiral.

This script was developed on an iMac, running Leopard OS X, with TclTk 8.4.7 installed. It has a 19 inch screen, so I'm not sure how the graphics might appear, or the script behave on other systems, your results may vary. Any helpful comments or modifications are greatly appreciated.

#!/bin/sh
# \
exec wish "\$0" \${1+"\$@"}

#=====================================================================================
#
#                   Ulam Spiral Demo Program
#
#   METHOD---
#   On an integer lattice, place square cells centered on each lattice point.
#   Make cell side lengths equal to the distance between lattice points.
#   Starting with a single cell at the center of the lattice, wrap it with
#   three cells that form an inverted "L" shape starting to the right of the
#   first cell, completing a larger Square Pattern of four cells.  Label the first
#   cell with the number 1, then assign increasing integers to each new cell.
#   After the last cell has been labeled, wrap an "L" shaped pattern of five
#   more cells around the the first four cells.  Continue to repeat the
#   wrapping process with inverted and regular "L" shapes.
#
#
#   VARIANTS---
#   This scheme forms a Counter Clockwise Spiral.  Clockwise creates a
#   different pattern.  The starting integer and location of the starting
#   integer within the first group of four cells might also be varied.
#=====================================================================================

#  Version of March 16, 2009

#
#    M -- the number of cells forming the edge of a larger Square Pattern.
#    primes -- the list containing the prime numbers smaller than 121.
#    primeIndex -- The zero origin index to the list of primes.
#    runningIndex -- The current square cell number to be plotted.
#    The side of a square cell is  2 * \$delta in length.
#

#   Calling Sequence
#-------------------
#   canvasSetup
#   Click_Here
#       drawSquares
#         iterate
#           square
#       drawCoords
#       odd_evenSquares

#----------------------
#  Some initialization.
#----------------------
set bkgndColor {}
set delta 30
set fgndColor black
set nucX {}
set nucY {}
set M 2
set primeFont "Courier 20 bold"
set primeIndex 0

#   The list containing prime numbers smaller than 121.
set primes {
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101
103 107 109 113
}

set runningIndex 1
set squareColors { {gainsboro  black} {linen      black} {lavender   black} \
{gray       black} {"deep sky blue"  white} {firebrick  white} \
{aquamarine black} {green      black} {sienna     white} \
{turquoise  black} {maroon     white} {bisque4    white} \
{SlateBlue  white}
}

set textFont "Times 16 italic"

#-------------------------------------------------
#  Proc Click_Here
#  A proc which responds to the Click_Here button.
#  The next wrapping of (2*M -1) cells will
#  be added to the existing Ulam Spiral plot.
#  Also refreshes the Reference Lines.
#             Demo.tcl
#-------------------------------------------------
proc Click_Here { } {
global  cX cY M M_Value nucX nucY warnTxt

if {\$M == 2} {
#  Initial Square created here.
square \$cX \$cY
}

if {\$M > 11} {
\$warnTxt delete 1.0 end
\$warnTxt insert insert "That's It!"
} else {
drawSquares
set cX \$nucX
set cY \$nucY
}

\$M_Value delete 1.0 end
\$M_Value insert insert [expr {\$M -1}]

#  Add Reference Lines.  Do this every cycle so lines overlay numbers.
drawCoords
odd_evenSquares
}

#------------------------------------------------------
#  Proc drawCoords.
#  Overlay blue X and Y Coordinate Axes on the Ulam plot
#  for reference purposes.
#             Demo.tcl
#------------------------------------------------------
proc drawCoords {} {
global  cX0 cY0 window xMin xMax yMin yMax

#     Draw Coordinate Axes
set coords {}
set x1 \$xMin
set y1 \$cY0
set x2 \$xMax
set y2 \$cY0
lappend coords [list \$x1 \$y1 \$x2 \$y2]
eval {\$window create line} \$coords \
{-width 0 -fill blue2}

set coords {}
set x1 \$cX0
set y1 \$yMin
set x2 \$cX0
set y2 \$yMax
lappend coords [list \$x1 \$y1 \$x2 \$y2]
eval {\$window create line} \$coords \
{-width 0 -fill blue2}
}

#-------------------------------------------------------
#  Proc odd_evenSquares.
#  Draw lines through cells which are the Squares of Odd
#  and Even numbers.
#             Demo.tcl
#-------------------------------------------------------
proc odd_evenSquares {} {

#   Draw lines through the squares of the Odd and Even numbers.
plotLine 336 276  96 36
plotLine 336 336  636 636
}

#---------------------------------------------------------
#  Proc plotLine.
#  Plot a line connecting the two points, x1 y1 and x2 y2.
#             Demo.tcl
#---------------------------------------------------------
proc plotLine {x1 y1 x2 y2} {
global  window

lappend coords [list \$x1 \$y1 \$x2 \$y2]
eval {\$window create line} \$coords \
{-width 0 -fill black}
}

#-------------------------------------------------
#  Proc canvasSetup
#  Create the Ulam Spiral canvas with buttons etc.
#             Demo.tcl
#-------------------------------------------------
proc canvasSetup { } {
global cX0 cY0 cX cY delta M_Value primeFont warnTxt
global window xMin xMax yMin yMax

set window .square
catch {destroy \$window}
toplevel \$window

wm title \$window "Ulam's Spiral Demo"
wm geometry \$window +600+150
wm withdraw .
focus \$window

#  Determine screen width and height.
set screenwd [winfo screenwidth .]
set screenht [winfo screenheight .]

#  Make a canvas to contain the Ulam screen.
#   set winwd [expr {int ( 0.45*\$screenwd )}]
set winwd [expr {int ( 0.40*\$screenwd )}]
set winht \$winwd

set cX [expr {int (\$winwd / 2)}]
set cY \$cX

#  Center of the very first Cell.
set cX0 \$cX
set cY0 \$cY
set xMin [expr {\$cX0 - 10 * \$delta}]
set xMax [expr {\$cX0 + 10 * \$delta}]
set yMin [expr {\$cX0 - 10 * \$delta}]
set yMax [expr {\$cX0 + 10 * \$delta}]

set f1 [frame \$window.f1 -relief sunken -borderwidth 2 -height 30]
pack \$f1 -fill x -side top

button \$f1.bp -text "Click_Here" -width 9 -bg blue -fg white -font \$primeFont \
-command Click_Here

label \$f1.lbl1 -text "M is:" -width 15  -anchor e -font \$primeFont
text \$f1.txt -relief raised -bd 2 -width 2 -height 1 -font \$primeFont
set M_Value \$f1.txt
text \$f1.txtw -relief raised -bd 2 -width 10 -height 1 -font \$primeFont
set warnTxt \$f1.txtw
eval pack [winfo children \$f1] -side left

button  \$f1.bq -text Quit -width 5 -bg blue -fg white -font \$primeFont \
-command exit
pack \$f1.bq -side right

set window \$window.c
pack [canvas \$window -width \$winwd -height \$winht -bg "alice blue"]

raise .
}

#------------------------------------------------------
#  Proc drawSquares
#  A proc to draw a wrapping pattern of (2*\$M -1) cells
#  in an "L" or inverted "L" shape.
#             Demo.tcl
#------------------------------------------------------
proc drawSquares {} {
global bkgndColor cX cY  fgndColor M nucX nucY
global runningIndex squareColors

set nucX \$cX
set nucY \$cY

set colors [lindex \$squareColors [expr {\$M -2}] ]
set fgndColor [lindex \$colors 1]
set bkgndColor [lindex \$colors 0]

iterate

incr M
incr runningIndex
}

#--------------------------------------------------------
#  Proc iterate.
#  Create and number the individual cells in the wrapping
#  pattern.  Draw cells in Counter-ClockWise Direction.
#             Demo.tcl
#--------------------------------------------------------
proc iterate { } {
global   delta M nextOp nucX nucY runningIndex

set num_Moves [expr {\$M -1}]
set twoDelta [expr {2*\$delta}]
set nextOp "right"
if {[expr {fmod (\$M,2)}] > 0.0} {

set nextOp "left"
}

if {[string compare \$nextOp "right"] == 0} {
set nucX [expr {\$nucX + \$twoDelta}]
square \$nucX \$nucY

for {set i 1} {\$i <= \$num_Moves} {incr i 1} {
set nucY [expr {\$nucY - \$twoDelta}]
square \$nucX \$nucY
}

for {set i 1} {\$i <= \$num_Moves} {incr i 1} {
set nucX [expr {\$nucX - \$twoDelta}]
square \$nucX \$nucY
}

} else {
set nucX [expr {\$nucX - \$twoDelta}]
square \$nucX \$nucY

for {set i 1} {\$i <= \$num_Moves} {incr i 1} {
set nucY [expr {\$nucY + \$twoDelta}]
square \$nucX \$nucY
}

for {set i 1} {\$i <= \$num_Moves} {incr i 1} {
set nucX [expr {\$nucX + \$twoDelta}]
square \$nucX \$nucY
}
}

incr runningIndex -1
}

#----------------------------------------------------------------
#  Proc square
#  Draw a square cell centered at x and y.  The cell edge will be
#  (2 * \$delta) in length.  Check to see if runningIndex is a
#  member of the primes list.  If so, print it's value in blue
#  and primeFont.  Otherwise, print values in black and textFont.
#             Demo.tcl
#----------------------------------------------------------------
proc square { x y } {
global bkgndColor delta fgndColor primeFont primeIndex
global primes runningIndex textFont window

after 125

if {[lindex \$primes \$primeIndex] == \$runningIndex} {
set fgnd blue
set numberFont \$primeFont
incr primeIndex
} else {
set fgnd \$fgndColor
set numberFont \$textFont
}

#  Calculate coordinates for square cell.
set x1 [expr {\$x - \$delta}]
set y1 [expr {\$y - \$delta}]
set x2 [expr {\$x + \$delta}]
set y2 [expr {\$y + \$delta}]

#  Create cell here with fill bkgndColor.
\$window create rectangle \$x1 \$y1 \$x2 \$y2 -fill \$bkgndColor \
-outline lightblue1 -width 2
#  Label cell with runningIndex.
\$window create text \$x \$y -text \$runningIndex -fill \$fgnd -font \$numberFont
incr runningIndex
}

#--------------
#   Start Demo.
#--------------
canvasSetup
Click_Here