Keith Vetter 2010-04-20 -- Here's another extension to the tklib Plotchart package. It draws a graph similar to an xy-graph but by utilizing the 3d capabilities of Plotchart it turn the graph line into a 3d ribbon. This is a more robust version of my Ribbon Graphs package.
I'm using this code in a bike mapping program to draw route profile graphs.
I wrote one new routine, ::Plotchart::Draw3DRibbon, which takes as input a list of x,y duples (actually y,z due to 3d axis orientation). I slightly modified the 3d-axes routine, ::Plotchart::Draw3DAxes, to skip drawing any axis with an X compenent when xstep is 0.
One problem is that the graph isn't centered correctly, but my fix for that required tromping on too much of Plotchart's internals, so I left it out of this code.
AM (21 april 2010) Nice, I will add it to Plotchart
AM (18 june 2010) Incorporated it in my copy of the source code. To appear in Plotchart 1.9.0 (together with some other new plot types)
##+########################################################################## # # Plotchart extension--3d ribbon graph # by Keith Vetter, April 2010 # package require Plotchart ##+########################################################################## # # Draw3DRibbon -- new plotchart type which combines normal xy line # graph but with a 3d ribbon affect. # # yzData consists of list of duples, each of which is y,z pair # (y is left-to-right, z is up-and-down, x is front-to-back). # proc ::Plotchart::Draw3DRibbon { w yzData } { variable scaling set nxcells 1 set nycells [llength $yzData] incr nxcells -1 incr nycells -1 set x1 $scaling($w,xmin) set x2 [expr {($scaling($w,xmax) - $x1)/10.0}] lassign $scaling($w,colours) fill border # # Draw the quadrangles making up the data in the right order: # first y from minimum to maximum # then x from maximum to minimum # for { set j 0 } { $j < $nycells } { incr j } { set jj [expr {$j+1}] set y1 [lindex $yzData $j 0] set y2 [lindex $yzData $jj 0] set z1 [lindex $yzData $j 1] set z2 [lindex $yzData $jj 1] lassign [::Plotchart::coords3DToPixel $w $x1 $y1 $z1] px11 py11 lassign [::Plotchart::coords3DToPixel $w $x1 $y2 $z2] px12 py12 lassign [::Plotchart::coords3DToPixel $w $x2 $y1 $z1] px21 py21 lassign [::Plotchart::coords3DToPixel $w $x2 $y2 $z2] px22 py22 $w create polygon $px11 $py11 $px21 $py21 $px22 $py22 \ $px12 $py12 $px11 $py11 \ -fill $fill -outline $border } } ##+########################################################################## # # A slight revision of Plotchart::Draw3DAxes differing only # if xstep is 0 then we don't draw any axis that has an X # component. # # Draw3DAxes -- # Draw the axes in a 3D plot # Arguments: # w Name of the canvas # xmin Minimum x coordinate # xmax Maximum x coordinate # xstep Step size # ymin Minimum y coordinate # ymax Maximum y coordinate # ystep Step size # zmin Minimum z coordinate # zmax Maximum z coordinate # zstep Step size # names List of labels for the x-axis (optional) # Result: # None # Note: # To keep the axes in positive orientation, the x-axis appears # on the right-hand side and the y-axis appears in front. # This may not be the most "intuitive" presentation though. # Side effects: # Axes drawn in canvas # proc ::Plotchart::Draw3DAxes { w xmin ymin zmin xmax ymax zmax xstep ystep zstep {names {}}} { variable scaling $w delete axis3d # # Create the support lines first # lassign [coords3DToPixel $w \ $scaling($w,xmin) $scaling($w,ymin) $scaling($w,zmin)] pxxmin pyxmin lassign [coords3DToPixel $w \ $scaling($w,xmax) $scaling($w,ymin) $scaling($w,zmin)] pxxmax pyxmax lassign [coords3DToPixel $w \ $scaling($w,xmax) $scaling($w,ymax) $scaling($w,zmin)] pxymax pyymax lassign [coords3DToPixel $w \ $scaling($w,xmax) $scaling($w,ymin) $scaling($w,zmax)] pxzmax pyzmax lassign [coords3DToPixel $w \ $scaling($w,xmin) $scaling($w,ymin) $scaling($w,zmax)] pxzmx2 pyzmx2 lassign [coords3DToPixel $w \ $scaling($w,xmin) $scaling($w,ymax) $scaling($w,zmin)] pxymx2 pyymx2 foreach [coords3DToPixel $w \ $scaling($w,xmax) $scaling($w,ymax) $scaling($w,zmax)] pxzymx pyzymx if {$xstep > 0} { $w create line $pxxmax $pyxmax $pxxmin $pyxmin -fill black -tag axis3d $w create line $pxxmax $pyxmax $pxymax $pyymax -fill black -tag axis3d $w create line $pxymax $pyymax $pxymx2 $pyymx2 -fill black -tag axis3d $w create line $pxzmax $pyzmax $pxzymx $pyzymx -fill black -tag axis3d $w create line $pxxmax $pyxmax $pxzmax $pyzmax -fill black -tag axis3d $w create line $pxzmax $pyzmax $pxzmx2 $pyzmx2 -fill black -tag axis3d $w create line $pxymax $pyymax $pxzymx $pyzymx -fill black -tag axis3d } $w create line $pxxmin $pyxmin $pxymx2 $pyymx2 -fill black -tag axis3d $w create line $pxxmin $pyxmin $pxzmx2 $pyzmx2 -fill black -tag axis3d # # Numbers to the z-axis # set z $zmin while { $z < $zmax+0.5*$zstep } { lassign [coords3DToPixel $w $xmin $ymin $z] xcrd ycrd set xcrd2 [expr {$xcrd-3}] set xcrd3 [expr {$xcrd-5}] $w create line $xcrd2 $ycrd $xcrd $ycrd -tag axis3d $w create text $xcrd3 $ycrd -text $z -tag axis3d -anchor e set z [expr {$z+$zstep}] } # # Numbers or labels to the x-axis (shown on the right!) # if {$xstep > 0} { if { $names eq "" } { set x $xmin while { $x < $xmax+0.5*$xstep } { lassign [coords3DToPixel $w $x $ymax $zmin] xcrd ycrd set xcrd2 [expr {$xcrd+4}] set xcrd3 [expr {$xcrd+6}] $w create line $xcrd2 $ycrd $xcrd $ycrd -tag axis3d $w create text $xcrd3 $ycrd -text $x -tag axis3d -anchor w set x [expr {$x+$xstep}] } } else { set x [expr {$xmin+0.5*$xstep}] foreach label $names { lassign [coords3DToPixel $w $x $ymax $zmin] xcrd ycrd set xcrd2 [expr {$xcrd+6}] $w create text $xcrd2 $ycrd -text $label -tag axis3d -anchor w set x [expr {$x+$xstep}] } } } # # Numbers to the y-axis (shown in front!) # set y $ymin while { $y < $ymax+0.5*$ystep } { lassign [coords3DToPixel $w $xmin $y $zmin] xcrd ycrd set ycrd2 [expr {$ycrd+3}] set ycrd3 [expr {$ycrd+5}] $w create line $xcrd $ycrd2 $xcrd $ycrd -tag axis3d $w create text $xcrd $ycrd3 -text $y -tag axis3d -anchor n set y [expr {$y+$ystep}] } set scaling($w,xstep) $xstep set scaling($w,ystep) $ystep set scaling($w,zstep) $zstep # # Set the default grid size # GridSize3D $w 10 10 } ################################################################ # # Demo code package require Tk package require Plotchart set yscale [list 0 40 5] ;# Y axis is miles along route set zscale [list 900 1300 100] ;# Z axis is altitude set xscale [list 0 .1 .1] ;# X axis is 3-d depth # Each duple is distance along route (Y) and its altitude (Z) set YZ { { 0 971} { 0.2 977} { 0.3 981} { 0.8 1010} { 1.0 1022} { 1.6 1060} { 1.7 1059} { 1.7 1060} { 1.8 1059} { 1.8 1059} { 1.8 1054} { 1.9 1071} { 2.0 1073} { 2.0 1060} { 2.0 1060} { 2.1 1050} { 2.2 1051} { 2.2 1049} { 2.3 1040} { 2.3 1039} { 2.4 1030} { 2.4 1050} { 2.5 1050} { 2.5 1059} { 2.6 1055} { 2.6 1052} { 2.6 1050} { 2.7 1041} { 2.7 1034} { 2.8 1025} { 2.9 1020} { 3.0 1049} { 3.2 1017} { 3.6 1010} { 3.9 1019} { 4.0 1029} { 4.1 1019} { 4.4 1049} { 4.6 1059} { 4.7 1067} { 4.8 1084} { 4.9 1029} { 5.2 1047} { 5.2 1058} { 5.7 1085} { 5.9 1087} { 6.4 1120} { 6.9 1169} { 7.3 1225} { 7.6 1265} { 8.0 1263} { 8.5 1189} { 8.8 1238} { 8.9 1245} { 9.1 1202} { 9.3 1199} {10.2 1148} {10.3 1148} {10.4 1141} {10.5 1135} {10.7 1159} {11.1 1157} {11.3 1159} {11.5 1139} {11.6 1139} {11.8 1141} {12.6 1160} {12.9 1150} {13.5 1150} {13.9 1150} {14.0 1129} {14.1 1120} {14.4 1128} {14.5 1155} {14.7 1161} {14.7 1154} {14.8 1116} {14.9 1106} {15.0 1110} {15.1 1113} {15.2 1104} {15.3 1108} {15.3 1118} {15.3 1131} {15.3 1148} {15.4 1161} {15.5 1146} {15.6 1160} {15.9 1141} {16.1 1121} {16.4 1100} {16.5 1102} {16.7 1097} {16.9 1088} {17.1 1091} {17.2 1082} {17.2 1080} {17.4 1129} {17.5 1120} {17.6 1115} {17.7 1090} {18.0 1105} {18.1 1109} {18.1 1104} {18.1 1084} {18.2 1071} {18.2 1091} {18.4 1073} {18.5 1067} {18.5 1065} {18.6 1069} {18.8 1071} {19.2 1051} {19.2 1050} {19.5 1051} {19.6 1059} {19.8 1046} {19.9 1048} {20.0 1053} {20.1 1039} {20.3 1068} {20.7 1053} {20.8 1066} {21.0 1053} {21.1 1047} {21.3 1049} {21.6 1048} {22.4 1019} {22.6 1026} {22.7 1030} {23.1 1026} {23.4 1054} {24.1 1029} {24.9 1025} {25.1 1011} {25.2 1002} {25.3 1042} {25.3 1047} {25.3 1048} {25.4 1049} {25.6 1080} {25.7 1090} {25.8 1089} {25.9 1107} {26.0 1103} {27.0 1165} {27.0 1171} {27.1 1170} {27.2 1159} {27.2 1169} {27.4 1180} {27.7 1154} {27.8 1139} {28.0 1139} {28.5 1130} {28.7 1139} {29.1 1139} {29.3 1140} {29.4 1146} {29.5 1141} {29.5 1148} {29.6 1148} {30.5 1199} {30.6 1202} {30.9 1245} {31.1 1238} {31.3 1189} {31.8 1263} {32.2 1265} {32.6 1225} {32.9 1169} {33.4 1120} {33.9 1087} {34.2 1085} {34.6 1058} {34.7 1047} {35.2 1012} {35.5 1001} {35.7 993} {35.8 989} {35.9 1019} {36.2 1050} {36.2 1029} {36.3 1051} {36.4 1034} {36.8 1086} {36.9 1067} {37.1 1121} {37.5 1053} {37.8 1059} {37.9 1060} {38.4 1022} {38.6 1010} {39.1 981} {39.2 977} {39.5 971} } wm title . "3D Ribbon Plotchart" canvas .c1 -width 800 -height 400 -bg #aaeeff -bd 0 -highlightthickness 0 pack .c1 -side top -fill both -expand 1 lassign $xscale xmin xmax xstep lassign $yscale ymin ymax ystep lassign $zscale zmin zmax zstep # Example with full 3d axes set s [::Plotchart::create3DPlot .c1 $xscale $yscale $zscale] ::Plotchart::Draw3DRibbon .c1 $YZ # Example with minimal axes canvas .c2 -width 800 -height 400 -bg #aaeeff -bd 0 -highlightthickness 0 pack .c2 -side top -fill both -expand 1 -pady {1 0} set s [::Plotchart::create3DPlot .c2 $xscale $yscale $zscale] # Redraw axis with xstep of 0 so it skips all X stuff .c2 delete axis3d ::Plotchart::Draw3DAxes .c2 \ $xmin $ymin $zmin \ $xmin $ymax $zmax \ 0 $ystep $zstep ::Plotchart::Draw3DRibbon .c2 $YZ return