Version 9 of Vector rotations with Bwise

Updated 2004-03-25 18:29:51

by Theo Verelst

A common thing in math and science is the rotation of a vector over an angle, as most with a math basis knowledge will know, a matrix can be used for the purpose of performing the rotation on a vector.

A rotation matrix is even a very well formed matrix in general with normally excellent invertability and eigenvalue computability.

It is not hard to make a orthogonal and even orthonomal basis for both the column and row space of a 2 dimensional matrix, and we can do so symbolically with simple geometic functions with a single rotation angle as a parameter for each of the 2x2 matrix elements which determines the rotation the matrix performs when multiplied to a vector.

Clear application is computer graphics, but other computations, for instance where complex numbers are used such as in electronics or other solutions of differential equations, also can become more insightfull by clear (simple) vector representations.

Bwise is about BlockWise programming, and also about Tk canvasses with interactions and graphical feedback, so this is a nice example.

In tcl, without using anything specifically from the Bwise package, except I used the function window, and will use the 'create block' facility of it, the rotation of a 2 dimensional (2 real components obviously quantized to some computer number resolution) vector can be performed by:

 proc vec2rot { x y a } {
   set     rotor.out [ expr cos($a) *$x  - sin($a) *$y ]
   lappend rotor.out [ expr sin($a) *$x  + cos($a) *$y ]
 }

When this procedure is defined, procs_window from bwise can be used to blockify our procedure. First, refresh the list of procs, then double click vec2rot, and then press the ' block' button on the bottom. First, a block on the canvas will be generated with name 'vec2rot'. When pressed again, serial numbers are added to the name, based on the highest appearing serial number on the canvas thus far. I double clicked and 'del sel' (deleted) the first instance of the block after clicking into existence two more, so I have vec2rot1 and vec2rot2 .

The procedure, like any, has only one return value, which is a list.

So we define for the sake of the Bwise canvas we have in mind a seperate block, which splits a 2-long list into elements for to two output pins. We use the newproc bwise proc to generate a 2 output block, possibly using the pro_args functions, which automatically puts arguments in place for the newproc, which gets default arguments except for name and output names x and y.

   pro_args  newproc {{name {sep1}}  {out {x y}}}

Gives:

   newproc {} sep1 in {x y}

Either using eval on the pro_args or simply issuing the newproc command a seperator block is created which is given list-split block function code, and connected to the ouput of the rotation block by either command or UI:

   set sep1.bfunc {set sep1.x [lindex ${sep1.in} 0] ; set sep1.y [lindex ${sep1.in} 1] }

http://82.168.209.239/Wiki/rotor1.jpg

The above Bwise representation was made by doing the above twice to create two rotator blocks and list splitters, and by connecting the blocks up and dragging them in place.

Lets define PI, and see if we can make the block sequence work for us by feeding it a vector, specifying angle, activating the block chain, and inspecting the results at the output pins.

   set pi [expr 2.0*acos(0)]

The input vector can be set, for instance to (1,0) by editing the fields in the vec2rot1 blocks' data window, called up by right clicking on the block and selecting 'Data'. After also entering an angle, for instance 0 (rad), and also entering an angle in the vec2rot2's data window, we can apply 'Funprop' (short for functional propagate, which makes all blocks 'fire' which at some point in the process have all their input pins fed with new data over a connection) to run the whole sequence, which results in the following:

http://82.168.209.239/Wiki/rotor2.jpg

Does that add up? Easily. Graphical inspection makes clear we have entered x1 basis vector into our computation, and requested two 0 rotations of rotations matrix/vector multiplication blocks, and we see at the overall output at sep2 that we get the same vector as x1 as a result. Now lets add some rotation to the chain, for instance a 180 degree rotation in the first block by entering the (numerical) value of PI into the field:

   set vec2rot1.a $pi     
   # 3.14159265359

which after issueing

   net_funprop vec2rot1

To 'run' the network again results in an output vector of (-1.0 -2.06823111155e-13) which is very close to the vector (-1,0) which is indeed -x1 , the half circle rotated input vector.

We may also want to check how we can rotate followed by inverse rotate by setting

   set vec2rot2.a -$pi

and again funpropping the net starting somewhere before or at the vec2rot2 block, which should give us back the first basis vector (1 0), and appears to give (on p4 RH9 linux, but probably pretty much exactly the same on windows, which I used yesterday without any script changes) {1.0 2.53066578395e-25} which is close enough.

Of course other angles work just the same. I've reconstructed this example in a short time from something I'd done before. Now I'll see if I can add some vector graphics, which should look nice on the canvas.


For the next stuff BWise of the last 4 year or so is needed, because of some assumptions on the procedure library.

First we'll add a vector display block, which I did like this:

 newproc {} vecdis1 {x y} {} 100 100
 $mc create line 60 60 105 60 -tag {vecdis1 proc vecdis vector} -fill darkgreen -arrow last

The vector is created assuming the yellow block is created at its default position, and is dragged along when the block is dragged over the canvas. To make sure the vector gets updated when the inputs of the block change, we give the vecdis block a block function which reads the x and Y and redraw the vector (a single line with one arrow point):

 set vecdis1.bfunc { \
  set co [$mc coords [tag_and {vecdis1 block}]] ; \
  set co [list [expr ([lindex $co 0]+[lindex $co 2])/2] [expr ([lindex $co 1]+[lindex $co 3])/2]] ; \
  eval $mc coords [tag_and {vecdis1 vector}]  \
    [lrange $co 0 1] \
    [expr 45.0*${vecdis1.x}+[lindex $co 0]] \
    [expr -45.0*${vecdis1.y}+[lindex $co 1] ] \
 }

this drawing routine assumes the vector to be of length one or smaller, the length is multiplied by 45 to fit in the 100x100 block.

We can make the block transparent (as in the windowdump) by setting the yellow basis block to have no fill:

 $mc itemco [tag_and {vecdis1 block}] -fill {}

When our new vector display block is connected up with x and y of the output of the rotators and the list split, we can visualize the result of the rotations of the vector at the input of the first rotator (1,0):

http://82.168.209.239/Wiki/rotor3.jpg

On the left of the image a seperate window with 2 sliders can be seen, which can be generated from the bwise library, and which I coupled with the 2 angle inputs of the rotation blocks. The slider send routine (part of bwise) is changed to not send over a socket channel, but update the variables, while the update which is triggered by the release of any slider activated the chain of blocks with a net_funprop procedure call.

 proc slidsendupdate { } {
 #   send update
   net_funprop vec2rot1
 } 

 proc slidsend { {n} {v} } {
  set v [expr $v*2.0*2.0*acos(0)/360]
  switch $n phi {
     uplevel #0 "set vec2rot1.a $v"
  } theta {
     uplevel #0 "set vec2rot2.a $v"
  }
 } 


 sliders {phi theta}