####################################################################
#
# dragon.tcl
#
# Draws a dragon curve
# by Keith Vetter
#
# Revisions:
# KPV May 09, 2002 - initial revision
#
# http://www.math.okstate.edu/mathdept/dynamics/lecnotes/node17.html
####################################################################
set cw 500 ; set ch 500 ;# canvas size
array set comp {R L L R}
array set turn {E,R S E,L N S,R W S,L E W,R N W,L S N,R E N,L W}
array set fill {E cyan S magenta W blue N yellow}
proc DoDisplay {} {
global cw cw2 ch ch2
canvas .c -width $cw -height $ch -bd 2 -relief ridge
set cw2 [expr {$cw / 2}]
set ch2 [expr {$ch / 2}]
.c config -scrollregion [list -$cw2 -$ch2 $cw2 $ch2]
.c yview moveto .5
.c xview moveto .5
.c create oval -5 -5 5 5 -fill yellow -tag o
.c create text -$cw2 -$ch2 -anchor nw -font bold -tag lbl
pack .c -side top
scale .deg -label Degree -orient horizontal -from 1 -to 12
.deg config -relief ridge -showvalue 1
.deg set 4
bind .deg <ButtonRelease-1> [list after 1 [list DrawDragons -1]]
pack .deg -side left
}
# DrawDragons -- draw four dragon curve of this degree
proc DrawDragons {n} {
.c config -cursor watch
if {$n == -1} {set n [.deg get]} else {.deg set $n}
.c delete dragon
.c itemconfig lbl -text "Dragon Curve: $n"
DrawDragon $n E ; update
DrawDragon $n W ; update
DrawDragon $n S ; update
DrawDragon $n N ; update
.c config -cursor {}
}
# DrawDragon -- draw one dragon curve of this degree and orientation
proc DrawDragon {n {dir E}} {
global cw2 ch2 fill
set dir [string toupper $dir]
set tag "dragon_$dir"
set coords [GetCoords $n $dir]
set coords [ScaleCoords $coords]
.c create line $coords -tag [list dragon $tag] -width 2 -fill $fill($dir)
.c raise o
.c raise lbl
}
# ScaleCoords -- scale the unit coords to fit into the window
proc ScaleCoords {coords} {
global cw2 ch2 ;# Window size
# Find max coordinate from origin
set max_x [set max_y [set min_x [set min_y 0]]]
foreach {x y} $coords {
if {$x > $max_x} {set max_x $x
} elseif {$x < $min_x} {set min_x $x}
if {$y > $max_y} {set max_y $y
} elseif {$y < $min_y} {set min_y $y}
}
set max_x [expr {-$min_x > $max_x ? -$min_x : $max_x}]
set max_y [expr {-$min_y > $max_y ? -$min_y : $max_y}]
set max [expr {$max_x > $max_y ? $max_x : $max_y}]
set sc [expr {($cw2 - 50) / $max}]
set new {}
foreach {x y} $coords {
set nx [expr {$x * $sc}] ; set ny [expr {$y * $sc}]
lappend new $nx $ny
}
return $new
}
# GetCoords -- get the unit coordinates for this degree curve
proc GetCoords {n dir} {
global turn
set turns $dir
foreach leg [MakeDragon $n] {
set dir $turn($dir,$leg)
lappend turns $dir
}
set x 0 ; set y 0
set coords [list $x $y]
foreach leg $turns {
if {$leg == "E"} { incr x
} elseif {$leg == "S"} { incr y
} elseif {$leg == "W"} { incr x -1
} elseif {$leg == "N"} { incr y -1 }
lappend coords $x $y
}
return $coords
}
# MakeDragon -- gets the turn data for this degree dragon curve
proc MakeDragon {n} {
global dragon
# Do we already have it?
if {[info exists dragon($n)]} { return $dragon($n) }
if {$n == 0} { return {}}
# dragon(n) = dragon(n-1) + "R" + reverse(complement(dragon(n-1)))
set last [MakeDragon [expr {$n - 1}]]
set dragon($n) $last
lappend dragon($n) R
set idx [llength $last]
while {[incr idx -1] >= 0} {
set item [lindex $last $idx]
lappend dragon($n) $::comp($item)
}
return $dragon($n)
}
DoDisplay
DrawDragons 4