[Keith Vetter] 2006-10-04 : Another optical illusion. To paraphrase MathWorld ''when concentric squares with rounded edges are rotated slowly, the entire pattern appears to pulsate radially.'' ---- ##+########################################################################## # # psquare.tcl -- Illusion where rotating scales seemingly pulse # by Keith Vetter, Oct 3, 2006 # # http://mathworld.wolfram.com/RotatingSquareIllusion.html package require Tk package require tile array set S {title "Pulsing Squares" n 6 animate 1 delay 10 step 3 gap 10 dir 1} set PI [expr {acos(-1)}] proc DoDisplay {} { wm title . $::S(title) pack [frame .ctrl] -side bottom -fill x pack [canvas .c -bd 2 -relief ridge -bg yellow] -side top -fill both -exp 1 bind .c {ReCenter %W %h %w} ::ttk::button .anim -text "Stop" -command StartStop ::ttk::button .rev -text "Reverse" -command {set S(dir) [expr {-$S(dir)}]} ::ttk::labelframe .lp -text "Speed" ::ttk::scale .sp -from 1 -to 10 -variable ::S(step) image create photo ::img::question -width 6 -data { R0lGODlhBQAJALMAAAQCBOTe5BcAiAAAfIgACOkAABIApwAAAPgB0HAA+hcAFQAA AACgAHHqABcSAAAAACH5BAAAAAAALAAAAAAFAAkAAwQNMIApQaU0VJ2l/l+XRQA7} ::ttk::button .? -image ::img::question -command About grid .lp .anim -in .ctrl -pady 10 grid ^ .rev -in .ctrl grid columnconfigure .ctrl {0 1} -weight 1 grid rowconfigure .ctrl 100 -minsize 10 grid config .lp -sticky ns pack .sp -in .lp -fill both -expand 1 place .? -in .ctrl -relx 1 -rely 1 -anchor se DrawSquares } proc ReCenter {W h w} { ;# Called by configure event set h2 [expr {$h / 2}] ; set w2 [expr {$w / 2}] $W config -scrollregion [list -$w2 -$h2 $w2 $h2] } proc DrawSquares {} { .c delete s set x0 [expr {$::S(gap) / 2}] for {set i 0} {$i < $::S(n)} {incr i} { roundRect .c -$x0 -$x0 $x0 $x0 $x0 -tag s -width $::S(gap) \ -fill {} -outline black incr x0 $::S(gap) incr x0 $::S(gap) } } proc About {} { set msg "$::S(title)\nby Keith Vetter, October 2006\n" tk_messageBox -message $msg -title "About $::S(title)" } proc roundRect { w x0 y0 x3 y3 radius args } { # From http://wiki.tcl.tk/DrawingRoundedRectangles set r [winfo pixels $w $radius] set d [expr {2 * $r}] # Radius of the curve must be less than 3/8 size of box set maxr 0.75 if {$d > $maxr * ($x3 - $x0)} { set d [expr {$maxr * ($x3 - $x0)}] } if {$d > $maxr * ($y3 - $y0)} { set d [expr {$maxr * ($y3 - $y0)}] } set x1 [expr {$x0 + $d}] set x2 [expr {$x3 - $d}] set y1 [expr {$y0 + $d}] set y2 [expr {$y3 - $d}] set cmd [list $w create polygon] lappend cmd $x0 $y0 $x1 $y0 $x2 $y0 $x3 $y0 lappend cmd $x3 $y1 $x3 $y2 $x3 $y3 $x2 $y3 $x1 $y3 lappend cmd $x0 $y3 $x0 $y2 $x0 $y1 lappend cmd -smooth 1 return [eval $cmd $args] } proc _RotateItem {w tagOrId Ox Oy angle} { # From http://wiki.tcl.tk/CanvasRotation set angle [expr {$angle * $::PI / 180.0}] ;# Radians foreach id [$w find withtag $tagOrId] { ;# Do each component separately set newXY {} foreach {x y} [$w coords $id] { # rotates vector (Ox,Oy)->(x,y) by angle clockwise set x [expr {$x - $Ox}] ;# Shift to origin set y [expr {$y - $Oy}] set xx [expr {$x * cos($angle) - $y * sin($angle)}] ;# Rotate set yy [expr {$x * sin($angle) + $y * cos($angle)}] set xx [expr {$xx + $Ox}] ;# Shift back set yy [expr {$yy + $Oy}] lappend newXY $xx $yy } $w coords $id $newXY } } proc StartStop {} { set ::S(animate) [expr {! $::S(animate)}] .anim config -text [expr {$::S(animate) ? "Stop" : "Start"}] Animate } proc Animate {} { foreach aid [after info] { after cancel $aid } if {! $::S(animate)} return _RotateItem .c s 0 0 [expr {$::S(dir) * $::S(step)}] after $::S(delay) Animate } ################################################################ DoDisplay Animate return ---- [Category Graphics] | [Category Optical Illusion]