[Arjen Markus] (20 May 2003) This is just another one of those little applications that may come in handy sometimes. Though it is not as flexible as it probably should be - no facilities for entering the expression in a GUI, no adjustment of the scaling nor of the viewpoint - still it can be a useful tool or the starting point of one. [http://mini.net/files/view3d.jpg] (though the original is animated) Suggestions welcome. ---- [Marco Maggi] Interesting. Will this always work? I mean: hidden lines will be hidden correctly for all the functions? [AM] It relies on the so-called "painter's algorithm" - draw the objects in the back first and work your way forwards. This is somewhat hidden in the proc drawFunction (hence the use of the vectors for calculating the corners of the rectangles) and the range of the viewpoint coordinates. Part of the work to make it more general is to decide what the two vectors should be ... But apart from that: yes, the algorithm is robust (and cheap) ---- The code below contains several examples of functions that can be viewed. ---- # view3d.tcl -- # Simple 3D viewer for functions of two independent variables # package require Tk # project2d -- # Project points in 3D space onto a 2D canvas # Arguments: # xyzcrd List of coordinate triplets # Result: # List of xy-coordinates on the canvas # Note: # Requires previous definition of viewpoint # proc project2d {xyzcrd} { variable project_params foreach {x1 y1 z1 x2 y2 z2} $project_params {break} foreach {x y z} $xyzcrd { set xc [expr {220.0+$x*$x1+$y*$y1+$z*$z1}] set yc [expr {320.0-($x*$x2+$y*$y2+$z*$z2)}] lappend result $xc $yc } return $result } # setViewpoint -- # Define the viewpoint # Arguments: # xv X-coordinate of viewpoint # yv Y-coordinate of viewpoint # zv Z-coordinate of viewpoint # Result: # None # Side effect: # Variable project_params filled # proc setViewpoint {xv yv zv} { variable project_params set x1 $yv set y1 [expr {-$xv}] set z1 0.0 set x2 [expr {$xv*$zv}] set y2 [expr {$yv*$zv}] set z2 [expr {$xv*$xv+$yv*$yv}] set r [expr {sqrt($x1*$x1+$y1*$y1+$z1*$z1)/200.0}] set x1 [expr {$x1/$r}] set y1 [expr {$y1/$r}] set z1 [expr {$z1/$r}] set r [expr {sqrt($x2*$x2+$y2*$y2+$z2*$z2)/200.0}] set x2 [expr {$x2/$r}] set y2 [expr {$y2/$r}] set z2 [expr {$z2/$r}] set project_params [list $x1 $y1 $z1 $x2 $y2 $z2] } # drawFunction -- # Draw the function using a range of -1,1 for both x and y # Arguments: # func Expression taking x and y as arguments # Result: # None # Note: # Assumes a viewpoint from the right angle (i.e. xv < 0) # proc drawFunction {func} { set nostps 20 set x10 -1.0 set y10 0.0 set dx1 [expr {2.0/$nostps}] set dy1 0.0 set x20 0.0 set y20 -1.0 set dx2 0.0 set dy2 [expr {2.0/$nostps}] for {set j 0} {$j < $nostps} {incr j} { for {set i 0} {$i < $nostps} {incr i} { set x1 [expr {$x10+$dx1*$i+$x20+$dx2*$j}] set y1 [expr {$y10+$dy1*$i+$y20+$dy2*$j}] set x2 [expr {$x1+$dx1}] set y2 [expr {$y1+$dy1}] set x3 [expr {$x2+$dx2}] set y3 [expr {$y2+$dy2}] set x4 [expr {$x3-$dx1}] set y4 [expr {$y3-$dy1}] set x $x1; set y $y1; set z1 [expr $func] set x $x2; set y $y2; set z2 [expr $func] set x $x3; set y $y3; set z3 [expr $func] set x $x4; set y $y4; set z4 [expr $func] set crds [project2d [list $x1 $y1 $z1 \ $x2 $y2 $z2 \ $x3 $y3 $z3 \ $x4 $y4 $z4 ] ] .c create polygon $crds -fill white -outline black } } } # main -- # Main code: # - Create the canvas # - Set a viewpoint # - Draw some function # canvas .c -background white -width 500 -height 500 pack .c -fill both set count 0 proc draw {} { global count set zp [expr {3.0+2.0*sin(0.02*$count)}] incr count setViewpoint -3.0 -2.0 $zp .c delete all #drawFunction {(0.7-($x*$x+$y*$y))*(0.7-($x*$x+$y*$y))} #drawFunction {$x*(0.5-$x)*(0.5-($y*$y))} drawFunction {cos(3.0*$x)*cos(3.0*$y)} after 10 draw } after 0 draw ---- [[ [Category Mathematics] | [Category Graphics] ]]