Keith Vetter 2003-04-07: this whizzlet draws both the standard Hilbert Curve and a analogous one in 3-dimensions.
One clever aspect of this program is how it does hidden surface removal. The simple painters algorithm--drawing distant objects first so nearer objects paint over obscured surfaces (as used in Fractal Mountains)--doesn't work easily because the components of the curve can't be generated in a back-to-front ordering.
Instead, it uses tk's canvas stacking order. As each object gets generated it is tagged with its depth; then when everything is drawn, then canvas raise is used to place nearer objects before distant ones. This method won't work for complex objects but for the simple ones here it makes an effective hidden surface algorithm.
LV is there a relationship between this code and the sdarchive starkit that talks about the same thing?
In cases where wiki code is turned into starkits, perhaps the starkit's url could be added to the appropriate page, and, vice versa, the wikit page could be placed in the starkit's help information, so that someone with problems knows where to submit questions and comments?
##+########################################################################## # # hilbert3d.tcl -- draws the Hilbert Curve in both 2 and 3 dimensions # by Keith Vetter # package require Tk namespace eval 3D { variable matrix variable 3d set 3d(ex) 3 ;# Eye position set 3d(ey) 4 set 3d(ez) 3 set 3d(rx) 0 ;# Reference point set 3d(ry) 0 set 3d(rz) 0 } array set S {lvl 0 color cyan connect 1 3d 3 eye 3 3Dtype 0} array set DIRS {E {S E E N} N {W N N E} S {E S S W} W {N W W S}} array set QTRS {E {1 2 3 4} N {3 2 1 4} S {1 4 3 2} W {3 4 1 2}} array set XY {E {l t r t r b l b} N {r b r t l t l b} W {r b l b l t r t} S {l t l b r b r t}} array set hpath3d { 0,start 1 0,path {X z x Y X Z x} 0,sub { 1 2 2 20 20 4 4 5} 1,start 1 1,path {z Y Z X z y Z} 1,sub { 6 7 7 19 19 9 9 10} 2,start 1 2,path {X Y x z X y x} 2,sub {11 0 0 23 23 14 14 15} 3,start 4 3,path {x Z X Y x z X} 3,sub {16 17 17 7 7 13 13 8} 4,start 2 4,path {X y x Z X Y x} 4,sub {19 14 14 10 10 0 0 22} 5,start 7 5,path {z y Z x z Y Z} 5,sub {23 9 9 15 15 7 7 21} 6,start 1 6,path {Y X y z Y x y} 6,sub { 0 11 11 13 13 15 15 14} 7,start 1 7,path {z X Z Y z x Z} 7,sub { 2 1 1 3 3 5 5 4} 8,start 2 8,path {Z y z X Z Y z} 8,sub {21 18 18 11 11 20 20 23} 9,start 7 9,path {z x Z y z X Z} 9,sub {13 5 5 14 14 1 1 17} 10,start 4 10,path {Y x y Z Y X y} 10,sub { 3 15 15 4 4 11 11 12} 11,start 1 11,path {Y z y X Y Z y} 11,sub { 7 6 6 8 8 10 10 9} 12,start 7 12,path {x z X y x Z X} 12,sub { 5 13 13 18 18 17 17 1} 13,start 7 13,path {x y X z x Y X} 13,sub {22 12 12 6 6 3 3 19} 14,start 2 14,path {X Z x y X z x} 14,sub { 8 4 4 9 9 2 2 16} 15,start 4 15,path {Y Z y x Y z y} 15,sub {20 10 10 5 5 6 6 18} 16,start 4 16,path {Z Y z x Z y z} 16,sub {10 20 20 22 22 18 18 6} 17,start 4 17,path {x Y X Z x y X} 17,sub {15 3 3 21 21 12 12 11} 18,start 2 18,path {Z X z y Z x z} 18,sub { 4 8 8 12 12 16 16 2} 19,start 2 19,path {y Z Y X y z Y} 19,sub {18 21 21 1 1 23 23 20} 20,start 4 20,path {Z x z Y Z X z} 20,sub {17 16 16 0 0 8 8 13} 21,start 2 21,path {y X Y Z y x Y} 21,sub {14 19 19 17 17 22 22 0} 22,start 7 22,path {y z Y x y Z Y} 22,sub { 9 23 23 16 16 21 21 7} 23,start 7 23,path {y x Y z y X Y} 23,sub {12 22 22 2 2 19 19 3} } proc DoDisplay {} { global S wm title . TkHilbert option add *Scale.highlightThickness 0 option add *Scale.orient horizontal pack [frame .ctrl -relief ridge -bd 2 -padx 5 -pady 5] \ -side right -fill both -ipady 5 pack [frame .top -relief raised -bd 2] -side top -fill x pack [frame .screen -bd 2 -relief raised] -side top -fill both -expand 1 canvas .c -relief raised -borderwidth 0 -height 500 -width 500 label .msg -textvariable S(msg) -bd 2 -bg white -relief ridge pack .msg -side bottom -fill both -in .screen pack .c -side top -expand 1 -fill both -in .screen set colors {red orange yellow green blue cyan purple violet white} lappend colors [lindex [.c config -bg] 3] black foreach color $colors { radiobutton .top.b$color -width 1 -padx 0 -pady 0 -bg $color \ -variable S(color) -value $color -command ReColor bind .top.b$color <3> [list .c config -bg $color] } eval pack [winfo children .top] -side left -fill y DoCtrlFrame ReColor trace add variable S(draw) write Tracer trace add variable S(3d) write Tracer bind .sLevel <ButtonRelease-1> {if {! $S(draw)} DrawHilbertA} } proc DoCtrlFrame {} { frame .ctrl.top scale .sLevel -from 0 -to 7 -label Level -variable S(lvl) -relief ridge .sLevel configure -font "[font actual [.sLevel cget -font]] -weight bold" button .draw -text "Draw Curve" -command DrawHilbertA -bd 4 button .clear -text "Clear Curve" -command {.c delete all} -bd 4 button .stop -text "Stop Drawing" -command {set S(draw) 0} -bd 4 .draw configure -font "[font actual [.draw cget -font]] -weight bold" .clear configure -font [.draw cget -font] .stop configure -font [.draw cget -font] image create bitmap ::img::up -data { #define up_width 11 #define up_height 9 static char up_bits = { 0x00, 0x00, 0x20, 0x00, 0x70, 0x00, 0xf8, 0x00, 0xfc, 0x01, 0xfe, 0x03, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00 }} image create bitmap ::img::down -data { #define down_width 11 #define down_height 9 static char down_bits = { 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0xfe, 0x03, 0xfc, 0x01, 0xf8, 0x00,0x70, 0x00, 0x20, 0x00, 0x00, 0x00 }} button .up -image ::img::up -command {UpDown 1} button .down -image ::img::down -command {UpDown -1} checkbutton .connect -text "Show Connections" -variable S(connect) \ -relief raised -command ShowConnectors button .about -text About -command About frame .3df -bd 2 -relief ridge radiobutton .2d -text "2 Dimensions" -variable S(3d) -value 2 \ -command DrawHilbertA -relief ridge radiobutton .3d -text "3 Dimensions" -variable S(3d) -value 3 \ -command DrawHilbertA label .leye -text "Eye Position" -bd 2 -relief raised foreach q {x y z} { frame .feye$q -bd 2 -relief raised label .leye$q -text [string toupper $q] scale .eye$q -from -0 -to 10 -relief ridge -length 75 -orient v \ -variable ::3D::3d(e$q) bind .eye$q <ButtonRelease-1> {if {$S(3d)==3 && !$S(draw)} DrawHilbertA} pack .leye$q .eye$q -in .feye$q -side top -fill x } grid .ctrl.top -in .ctrl -row 0 -sticky news grid .sLevel .up -in .ctrl.top -row 0 -sticky news grid ^ .down -in .ctrl.top -row 1 -sticky news grid .draw -in .ctrl -row 21 -sticky ew grid .clear -in .ctrl -row 22 -sticky ew grid .stop -in .ctrl -row 23 -sticky ew -pady 10 grid .2d -in .ctrl -row 31 -sticky ew grid .3df -in .ctrl -row 32 -sticky ew grid rowconfigure .3df 1 -minsize 10 grid .3d - - -in .3df -row 1 -sticky ew grid .leye - - -in .3df -row 2 -sticky ew grid .feyex .feyey .feyez -in .3df -row 3 grid .connect -in .ctrl -row 101 -sticky ew grid .about -in .ctrl -row 102 -sticky ew grid rowconfigure .ctrl 10 -minsize 10 grid rowconfigure .ctrl 20 -minsize 10 grid rowconfigure .ctrl 30 -minsize 50 grid rowconfigure .ctrl 50 -weight 1 grid configure .up -ipadx 5 grid configure .down -ipadx 5 } ##+########################################################################## # # Tracer -- traces the S(draw) variable and activates widgets accordingly # proc Tracer {var1 var2 op} { global S set eyes {.leye .eyex .eyey .eyez .leyex .leyey .leyez} set ww {.up .down .connect .draw .clear .3d} if {$var1 == "S" && $var2 == "3d"} { if {$S(3d) == 3} {set state1 normal} else { set state1 disabled } foreach w $eyes {$w config -state $state1} return } if {$S(draw) == 0} { ;# Turning off drawing .stop config -state disabled .sLevel config -state normal -fg [lindex [.sLevel config -fg] 3] foreach w $ww { $w config -state normal} if {$S(3d) == 3} {foreach w $eyes {$w config -state normal}} } else { .stop config -state normal .sLevel config -state disabled -fg [.up cget -disabledforeground] foreach w $ww { $w config -state disabled} if {$S(3d) == 3} {foreach w $eyes {$w config -state disabled}} } } ##+########################################################################## # # DrawHilbert -- sets up the state and draws the Hilbert curve # proc DrawHilbertA {} {after 1 DrawHilbert} proc DrawHilbert {{lvl {}}} { global S 3D depths if {$lvl == {}} { set lvl $S(lvl) } else { set S(lvl) $lvl } .c delete all set S(draw) 1 set S(first) {} set S(ccolor) [expr {$S(connect) ? $S(color) : [.c cget -bg]}] if {$S(3d) == 3} { set S(width) [expr {$lvl < 3 ? (20 - 5*$lvl) : 5}] set n [expr {int(pow(2,3*($lvl+1)) - 1)}] set S(msg) "Hilbert 3D Curve Level $lvl ($n edges)" catch {unset depths} Hilbert3d $S(3Dtype) $lvl [GetStartCube] ZBuffer } else { set S(width) [expr {$lvl <= 4 ? (25 - 5*$lvl) : 8 - $lvl}] set n [expr {int(pow(4,$lvl+1) - 1)}] set S(msg) "Hilbert Curve Level $lvl ($n edges)" Hilbert [GetStartBox] E $lvl } set S(draw) 0 set S(first) {} if {! $::S(connect)} {.c lower connect} } ##+########################################################################## # # UpDown -- draws the curve one level up or down from current # proc UpDown {dlvl} { global S if {$dlvl < 0 && $S(lvl) == 0} return if {$dlvl > 0 && $S(lvl) >= [.sLevel cget -to]} return incr S(lvl) $dlvl DrawHilbert } ##+########################################################################## # # Hilbert -- draws a specified level Hilbert curve # proc Hilbert {box dir lvl} { global S DIRS QTRS if {! $S(draw)} return if {$lvl == 0} { Hilbert0 $box $dir return } set lvl2 [expr {$lvl - 1}] foreach quarter $QTRS($dir) newDir $DIRS($dir) { set b2 [QuarterBox $box $quarter] Hilbert $b2 $newDir $lvl2 } } ##+########################################################################## # # Hilbert0 -- draws the most basic hilbert curve inside $box facing $dir # proc Hilbert0 {box dir} { global S XY set xy $S(first) ;# Possibly connect to last set xy {} foreach {l t r b} [ShrinkBox $box] break foreach i $XY($dir) { ;# Walk coord list for this dir lappend xy [set $i] } if {$S(first) != ""} { .c create line [concat $S(first) [lrange $xy 0 1]] -width $S(width) \ -tag {hilbert connect} -fill $S(ccolor) } .c create line $xy -tag hilbert -width $S(width) -fill $S(color) \ -capstyle round set S(first) [lrange $xy end-1 end] ;# So next connects w/ this one } ##+########################################################################## # # GetStartBox -- returns coordinates of the area to draw our shape in # proc GetStartBox {} { return [list 9 9 [expr {[winfo width .c]-9}] [expr {[winfo height .c]-9}]] } ##+########################################################################## # # ShrinkBox -- shrinks a box to 1/4 of its size # proc ShrinkBox {box} { foreach {l t r b} $box break set dx [expr {($r - $l) / 4.0}] set dy [expr {($b - $t) / 4.0}] set l [expr {$l + $dx}] ; set r [expr {$r - $dx}] set t [expr {$t + $dy}] ; set b [expr {$b - $dy}] return [list $l $t $r $b] } ##+########################################################################## # # QuarterBox -- Returns coordinates of 1 of the 4 quadrants of BOX. # 1 = up/left, 2 = up/right, 3 = lower/right, 4 = lower/left # proc QuarterBox {box corner} { foreach {l t r b} $box break set hx [expr {($r - $l) / 2.0}] set hy [expr {($b - $t) / 2.0}] if {$corner == 1} { ;# Upper left set r [expr {$r - $hx}] set b [expr {$b - $hy}] } elseif {$corner == 2} { ;# Upper right set l [expr {$l + $hx}] set b [expr {$b - $hy}] } elseif {$corner == 3} { ;# Lower right set l [expr {$l + $hx}] set t [expr {$t + $hy}] } elseif {$corner == 4} { ;# Lower left set r [expr {$r - $hx}] set t [expr {$t + $hy}] } return [list $l $t $r $b] } proc ShowConnectors {} { if {$::S(connect)} { .c itemconfig connect -fill $::S(color) ZBuffer } else { .c itemconfig connect -fill [.c cget -bg] .c lower connect } } proc ReColor {} { global S foreach {S(red) S(green) S(blue)} [winfo rgb . $S(color)] break if {$S(color) == "black"} { foreach {S(red) S(green) S(blue)} [winfo rgb . white] break } if {$S(3d) == 3} { if {[.c find withtag line] != {}} { after 1 DrawHilbert } return } .c itemconfig hilbert -fill $::S(color) if {! $::S(connect)} {.c itemconfig connect -fill [.c cget -bg]} } proc About {} { set msg "TkHilbert\nby Keith Vetter, Feb 2003\n\n" append msg "Draws the Hilbert Curve and a 3-Dimensional analog.\n" append msg "This curve was discovered by David Hilbert in 1891 and\n" append msg "was one of the first plane-filling curves ever found." tk_messageBox -title "About TkHilbert" -message $msg } ##+########################################################################## # # 3D Hilbert curver routines # proc GetStartCube {} { global 3D set 3D(X) [expr {[winfo width .c] - 20}] set 3D(Y) [expr {[winfo height .c] - 20}] set 3D(X2) [expr {$3D(X) / 2.0}] ;# Mid-point set 3D(Y2) [expr {$3D(Y) / 2.0}] set 3D(X3) [expr {$3D(X2) - 100.0}] ;# Scaling factor set 3D(Y3) [expr {$3D(Y2) - 120.0}] 3D::Init return [list 1 1 1 -1 -1 -1] } ##+########################################################################## # # Hilbert3d # # Main routine to draw a 3-D Hilbert curve. If level is 0 then we just # draw the base figure (with TYPE orientation). Otherwise we divide up # the volume (CUBE) into 8 quadrants in the order of the path for TYPE. # We then draw Hilbert curves in each of the quadrants of LEVEL - 1. # proc Hilbert3d {type lvl c} { global S hpath3d if {! $S(draw)} return if {$lvl == 0} { Hilbert3d0 $type $c return } # Draw the 8 sub Hilbert shapes for this type. We start in the starting # quadrant for this type, draw the correctly oriented sub-unit based on # the info in hpath3d. We continue along the path for this type and draw # the remaining sub-units. set lvl2 [expr {$lvl - 1}] ;# Down a level set oct $hpath3d($type,start) ;# Octant to start in foreach path $hpath3d($type,path) sub $hpath3d($type,sub) { set c2 [Octant $c $oct] ;# Divide up the cube world Hilbert3d $sub $lvl2 $c2 ;# Draw next level Hilbert curve set oct [NewOctant $oct $path] ;# Move to next octant } } ##+########################################################################## # # Hilbert3d0 -- draws the lowest level of the 3-d Hilbert curve # # hpath3d specifies start corner of the cube and also the path of the curve. # Each letter in the path specifies an axis to follow with capital meaning # to go in. # proc Hilbert3d0 {type c} { global hpath3d foreach {x1 y1 z1 x2 y2 z2} [ShrinkCube $c] break # Get starting location set x [expr {$hpath3d($type,start) & 4 ? $x1 : $x2}] set y [expr {$hpath3d($type,start) & 2 ? $y1 : $y2}] set z [expr {$hpath3d($type,start) & 1 ? $z1 : $z2}] x_line $x $y $z connect ;# Start the line foreach p $hpath3d($type,path) { switch $p { X { set x $x1 } x { set x $x2 } Y { set y $y1 } y { set y $y2 } Z { set z $z1 } z { set z $z2 } } x_line $x $y $z } } proc ShrinkCube {cube} { foreach {x1 y1 z1 x2 y2 z2} $cube break set dx [expr {($x1 - $x2) / 4.0}] set dy [expr {($y1 - $y2) / 4.0}] set dz [expr {($z1 - $z2) / 4.0}] set x1 [expr {$x1 - $dx}] set x2 [expr {$x2 + $dx}] set y1 [expr {$y1 - $dy}] set y2 [expr {$y2 + $dy}] set z1 [expr {$z1 - $dz}] set z2 [expr {$z2 + $dz}] return [list $x1 $y1 $z1 $x2 $y2 $z2] } proc x_line {x y z {connect ""}} { global S foreach {sx sy} [3D::Obj2screen $x $y $z] break lappend S(xyz) $x $y $z if {$S(first) != {}} { foreach {color tag} [DepthColor $S(lastZ) $z] break if {! $S(connect) && $connect != ""} {set color $S(ccolor)} .c create line [concat $S(first) $sx $sy] -fill $color \ -width $S(width) -capstyle round -tag "line $tag $connect" } set S(first) [list $sx $sy] set S(lastZ) $z } ##+########################################################################## # # Obj2screen - converts 3-d coordinates (x,y,z) into screen coordinates. # The eye is fixed on the Z axis and we project onto a x-y plane # through the origin. It boils down to essentially dividing by Z. # proc obj2screen {x y z} { global S 3D set eye $S(eye) set x [expr {double($x) * $eye / ($eye - $z)}] ;# Depth adjustment set y [expr {double($y) * $eye / ($eye - $z)}] set sx [expr {$3D(X2) + round($x * $3D(X3))}] ;# To world coord set sy [expr {$3D(Y2) + round($y * $3D(Y3))}] return [list $sx $sy] } ##+########################################################################## # # DepthColor -- provides depth cueing by shading per depth # proc DepthColor {z1 z2} { global S depths set z [expr {($z1 + $z2) / 2.0}] ;# Mid point depth set z [expr {1 - (($z + 1) / 2)}] ;# Convert to 1-0 scale set zz [expr {1 - $z/2}] ;# .5 - 1 scale set red [expr {int($zz * $S(red))}] set green [expr {int($zz * $S(green))}] set blue [expr {int($zz * $S(blue))}] set color [format "\#%04X%04X%04X" $red $green $blue] set tag [format "Z%03d" [expr {int(999*$z)}]] set depths($tag) 1 ;# Used for Z buffering return [list $color $tag] } ##+########################################################################## # # Octant -- returns a CUBE which is one of the eight sub-quadrants of C # proc Octant {cube which} { foreach {x1 y1 z1 x2 y2 z2} $cube break set hx [expr {($x1 + $x2) / 2.0}] set hy [expr {($y1 + $y2) / 2.0}] set hz [expr {($z1 + $z2) / 2.0}] if {$which & 1} {set z2 $hz} else {set z1 $hz} if {$which & 2} {set y2 $hy} else {set y1 $hy} if {$which & 4} {set x2 $hx} else {set x1 $hx} return [list $x1 $y1 $z1 $x2 $y2 $z2] } proc NewOctant {which dir} { switch $dir { X { incr which 4} x { incr which -4} Y { incr which 2} y { incr which -2} Z { incr which 1} z { incr which -1} } return $which } ##+########################################################################## # # ZBuffer -- do a Z buffer hidden line algorithm by ordering every line # in the canvas stacking list based on the lines "Z###" tag that we # added to every line, the number in which is the depth of the line. # The hash depths keeps a list of all "Z###" tags used. # proc ZBuffer {} { foreach d [lsort [array names ::depths]] { .c lower $d } } ################################################################ ################################################################ ################################################################ ################################################################ ##+########################################################################## # # 3d Canvas # # Simple 3d canvas package. After specifying the eye, the page size and a # few other variables, this package will draw points and lines in 3d space. # # A simple example is given at the bottom. # # This is very simple. No clipping, z-buffering, or rotation is provided. # # Procedures: # 3D::Init # Generates the transformation matrix needed to map from world to screen. # Must be called after setting or changing the eye, etc. # 3D::Line canvas x1 y1 z1 x2 y2 z3 ?x3 y3 z3 ...? ?options? # Draws a line between all the 3d points specified. # # Variables: # 3d(ex) 3d(ey) 3d(ez) == eye position # 3d(rx) 3d(ry) 3d(rz) == reference point # 3d(x) 3d(y) == canvas size # 3d(cx) 3d(cy) == viewport center (reference point goes here) # 3d(sx) 3d(sy) == size of viewport # # Revision history # Keith Vetter 12/17/95 Initial revision from a C based version into tcl. # Keith Vetter 1/10/95 Finished the revision. # ##+########################################################################## # # 3D::Init # # Computes the transformation matrix for the current eye and center. # Note, calling this resets all scaling, translations, etc. # proc 3D::Init {} { variable matrix variable 3d set xy [expr {sqrt($3d(ex)*$3d(ex) + $3d(ey)*$3d(ey))}] set xyz [expr {sqrt($xy*$xy + $3d(ez)*$3d(ez))}] 3D::Ident matrix 3D::Ident t ;# T0 - Center to origin set t(3,0) [expr {-$3d(rx)}] set t(3,1) [expr {-$3d(ry)}] set t(3,2) [expr {-$3d(rz)}] 3D::M44 matrix t matrix 3D::Ident t ;# T1 -- Origin To eye set t(3,0) [expr {-$3d(ex)}] set t(3,1) [expr {-$3d(ey)}] set t(3,2) [expr {-$3d(ez)}] 3D::M44 matrix t matrix 3D::Ident t ;# T2 -- Rotate 90 around X set t(1,1) 0 set t(2,2) 0 set t(1,2) -1 set t(2,1) 1 3D::M44 matrix t matrix if {$xy != 0} { 3D::Ident t ;# T3 -- Rotate to eye line set t(0,0) [set t(2,2) [expr {-$3d(ey) / $xy}]] set t(0,2) [expr {$3d(ex) / $xy}] set t(2,0) [expr {-$t(0,2)}] 3D::M44 matrix t matrix } 3D::Ident t ;# T4 -- Rotate to eye line set t(1,1) [set t(2,2) [expr {$xy / $xyz}]] set t(1,2) [expr {$3d(ez) / $xyz}] set t(2,1) [expr {-$t(1,2)}] 3D::M44 matrix t matrix 3D::Ident t ;# T5 -- Left-Handed coords set t(2,2) -1 3D::M44 matrix t matrix 3D::Ident t ;# N - Scale By D/S set t(0,0) [set t(1,1) 4] 3D::M44 matrix t matrix set 3d(x) [winfo width .c] ;# Page size set 3d(y) [winfo height .c] set 3d(cx) [expr {$3d(x) / 2.0}] ;# Mid-point set 3d(cy) [expr {$3d(y) / 2.0}] set 3d(sx) [expr {$3d(cx) - 5.0}] ;# Viewport size set 3d(sy) [expr {$3d(cy) - 6.0}] } ##+########################################################################## # # 3D::Ident matrix # # Returns $mm as the identity matrix of size 4 # proc 3D::Ident mm { upvar 1 $mm m catch "uplevel [list unset $mm]" ;# Erase all entries foreach a {0,1 0,2 0,3 1,0 1,2 1,3 2,0 2,1 2,3 3,0 3,1 3,2} { set m($a) 0 } set m(0,0) [set m(1,1) [set m(2,2) [set m(3,3) 1.0]]] } ##+########################################################################## # # 3D::M44 ma mb mc # # Matrix multiply ma x mb => mc of size 4. mc can be either ma or mb. # proc 3D::M44 {ma mb mc} { upvar 1 $ma aa upvar 1 $mb bb upvar 1 $mc cc for {set r 0} {$r < 4} {incr r} { set result($r,0) [expr {.0 + $aa($r,0)*$bb(0,0) + $aa($r,1)*$bb(1,0) \ + $aa($r,2)*$bb(2,0) + $aa($r,3)*$bb(3,0)}] set result($r,1) [expr {.0 + $aa($r,0)*$bb(0,1) + $aa($r,1)*$bb(1,1) \ + $aa($r,2)*$bb(2,1) + $aa($r,3)*$bb(3,1)}] set result($r,2) [expr {.0 + $aa($r,0)*$bb(0,2) + $aa($r,1)*$bb(1,2) \ + $aa($r,2)*$bb(2,2) + $aa($r,3)*$bb(3,2)}] set result($r,3) [expr {.0 + $aa($r,0)*$bb(0,3) + $aa($r,1)*$bb(1,3) \ + $aa($r,2)*$bb(2,3) + $aa($r,3)*$bb(3,3)}] } catch "uplevel [list unset $mc]" foreach arr [array names result] { set cc($arr) $result($arr) } } ##+########################################################################## # # 3D::PrintMatrix matrix # # Prints out the 4x4 matrix $mm # proc m {} {::3D::PrintMatrix ::matrix} proc 3D::PrintMatrix {mm} { upvar 1 $mm m for {set r 0} {$r < 4} {incr r} { for {set c 0} {$c < 4} {incr c} { puts -nonewline [format "%.3f\t" $m($r,$c)] } puts "" } puts "" } ##+########################################################################## # # 3D::Obj2screen # # Converts a 3d position into 2d screen coordinates based on the current # transformation matrix matrix set up by 3D::Init. # proc 3D::Obj2screen {x y z} { variable matrix variable 3d set xe [expr {$x*$matrix(0,0)+$y*$matrix(1,0)+$z*$matrix(2,0)+$matrix(3,0)}] set ye [expr {$x*$matrix(0,1)+$y*$matrix(1,1)+$z*$matrix(2,1)+$matrix(3,1)}] set ze [expr {$x*$matrix(0,1)+$y*$matrix(1,2)+$z*$matrix(2,2)+$matrix(3,2)}] set sx [expr $3d(cx) + ($xe / $ze) * $3d(sx)] set sy [expr $3d(cx) - ($ye / $ze) * $3d(sy)] return [list $sx $sy] } ################################################################ ################################################################ DoDisplay bind . <Map> DrawHilbert
uniquename 2013jul29
This code could use an image to show what it produces:
(Thanks to 'gnome-screenshot', 'mtpaint', and ImageMagick 'convert' on Linux for, respectively, capturing the image to a PNG file, cropping the image, and converting the resulting PNG file to a JPEG file about 3 times smaller. Thank you FOSS developers everywhere, from someone who is also contributing FOSS code to the world --- in the form of Tk scripts.)