if 0 {Richard Suchenwirth 2007-09-16 - Graphical timetables visualize operations on a transit line, typically a railway. The X axis represents time of day, the Y axis the sequence of stations in distance scale. Lines indicate the movement of trains, with the slope indicating direction and speed, horizontal being stand-still.
The public API is just an overloaded canvas named gratt, which accepts some extra configuration parameters, and has the extra add method. A global variable named like the widget is created, and upvar-ed to the "anonymous array" in all functions. }
proc gratt {w args} { upvar #0 $w "" set (from) 0 set (to) 24 set (offset) 80 canvas $w foreach {key value} $args { switch -- $key { -line {set line $value} -from {set (from) $value} -to {set (to) $value} -offset {set (offset) $value} default {$w configure $key $value} } } gratt'draw $w $line rename $w _$w proc $w {cmd args} { set self [lindex [info level 0] 0] switch -- $cmd { add {eval gratt'add $self $args} default {eval _$self $cmd $args} } } return $w }
#-- Internal routines: draw grid, label stations and hours
proc gratt'draw {w line} { upvar #0 $w "" set x0 $(offset) set width [$w cget -width] set (dx) [expr {($width - $x0) / ($(to) - $(from))}] set height [$w cget -height] for {set h $(from); set x $x0} {$h <= $(to)} {incr h; incr x $(dx)} { $w create text $x 8 -text $h $w create line $x 16 $x [expr {$height-8}] -fill gray } incr x -$(dx) set yfac [expr {double($height-24)/[lindex $line end]}] foreach {name distance} $line { set ($name) [expr {$distance*$yfac+16}] $w create text 5 $($name) -text "$name $distance" -anchor w $w create line $x0 $($name) $x $($name) } }
#-- Add a train
proc gratt'add {w number schedule args} { upvar #0 $w "" set coords {} foreach {name times} $schedule { foreach time $times { lappend coords [gratt'x $w $time] $($name) } } eval $w create line $coords $args }
#-- convert a "military" time (HHMM) to x coordinate
proc gratt'x {w time} { upvar #0 $w "" scan $time %d time ;# strip off leading zero set mins [expr {($time/100-$(from))*60+$time%100}] expr {double($(offset)) + $mins * $(dx)/60}
}
if 0 {Here's a testing example: the fictional "Mason - Dixon line". Assume it is single-tracked, and trains can only cross in Mason-East and Springfield. We have regular trains (blue) and expresses (red). First, to declare the line with mileposts:}
set md {Mason 0 Mason-East 4 Springfield 12 Dixon-West 18 Dixon 20} pack [gratt .g -line $md -from 5 -to 19 -bg white -width 720 -height 240]
#-- And now for the timetable. Westbound trains:
.g add 1201 { Mason 0550 Mason-East {0556 0558} Springfield {0612 0618} Dixon 0639 } -fill blue .g add 1203 { Mason 0655 Mason-East {0701 0703} Springfield {0714 0720} Dixon-West {0728 0730} Dixon 0741 } -fill blue .g add X303 {Mason 0730 Springfield {0748 0750} Dixon 0811} -fill red .g add 1205 { Mason 0800 Mason-East {0806 0810} Springfield {0824 0830} Dixon 0853 } -fill blue .g add 1207 { Mason 1000 Mason-East {1006 1008} Springfield {1022 1026} Dixon 1051 } -fill blue .g add 1401 {Springfield 1200 Dixon-West {1210 1212} Dixon 1221} -fill blue .g add 1209 { Mason 1200 Mason-East {1206 1208} Springfield 1222 Dixon 1251 } -fill blue .g add X305 {Mason 1330 Springfield {1348 1350} Dixon 1411} -fill red .g add 1211 { Mason 1500 Mason-East {1506 1508} Springfield {1522 1524} Dixon 1553 } -fill blue .g add 1213 { Mason 1700 Mason-East {1706 1708} Springfield {1722 1724} Dixon 1753 } -fill blue
#---------------------- Eastbound trains
.g add 1200 { Dixon 0550 Dixon-West {0556 0558} Springfield {0612 0619} Mason-East {0628 0630} Mason 0643 } -fill blue .g add 1202 { Dixon 0650 Dixon-West {0656 0658} Springfield {0712 0719} Mason-East {0728 0740} Mason 0753 } -fill blue .g add 1204 { Dixon 0900 Dixon-West {0906 0908} Springfield {0922 0929} Mason-East {0936 0938} Mason 0951 } -fill blue .g add 1206 { Dixon 1000 Dixon-West {1006 1008} Springfield {1022 1029} Mason-East {1036 1038} Mason 1051 } -fill blue .g add 1408 {Dixon 1130 Dixon-West {1136 1138} Springfield 1146} -fill blue .g add X306 {Dixon 1100 Springfield {1122 1125} Mason 1143} -fill red .g add 1208 { Dixon 1330 Dixon-West {1336 1338} Springfield {1342 1353} Mason-East {1408 1410} Mason 1423 } -fill blue .g add 1210 { Dixon 1600 Dixon-West {1606 1608} Springfield {1622 1629} Mason-East {1636 1638} Mason 1651 } -fill blue .g add X308 {Dixon 1730 Springfield {1752 1755} Mason 1813} -fill red .g add 1212 { Dixon 1800 Dixon-West {1806 1808} Springfield {1822 1829} Mason-East {1836 1838} Mason 1851 } -fill blue
if 0 {Looking at the diagram, one can deduce that at least three trainsets are needed: a regular one that stays in Dixon overnight (which does two full round-trips and the short Springfield runs around noon), and a regular and an express (if they differ in type) stationed in Mason. It's an interesting pastime to modify the timetable and see how the diagram changes ... :^)
}