Wednesday, February 07, 2007

It keeps growing, and growing....

Some programs seem a little like Frankenstein's monster. You know, you start out to re-animate a little dead flesh in your spare time, and the next thing you know the peasants are in a uproar. Well, not quite, but it really seems like some of the smallest projects eventually take on a life of their own. (Note that in many places, this would be considered humor, poor humor, but humor. Apologies if it seems offensive where you are)

Here is an example that also shows some interesting concepts. History: One day I was doing something and thought "It sure would be nice if I could throw a north arrow on the screen any time I like to remind me which way north is". Just a simple little thought, easily enough accomplished with a couple of lines of python, nothing stored in the file, just displayed and then erased at the next refresh.. Later I thought, "Sometimes when I'm adjusting a display it would be nice to know where the exact center of the display and it's edges are". Then because we still do the majority of work on analytical instruments, I have always missed the ability in Vr's predecessor to show me where the instrument was in relation to the screen. Well as long as I'm showing North, and the centers of all the edges, why not draw a line from the screen center to the current cursor (instrument) location that would indicate where the instrument is.

See how it goes, next thing you know there are lines and symbols all over the screen, but each one helpful in it's own way. I call this ugly duck screen_center.py (for reasons which now escape me).


def Rad2DecDeg(Radians=0):
..return Radians*(180.00/3.1415926535897931)

Gui = PyVrGui()
Gr=PyVrGr()
Line=PyVrLine()
Sym=PyVrSym()

LineLayer=105
LineGraphic=1
NorthGraphic=58
TickGraphic=41
SymLayer=102
Radius=.75

Gui.DspMsg0('Rotation = %.2f'%(Rad2DecDeg(Gr.GetWinRot()[2])))
Sym.SetCoord(Gr.GetWinOrg()[0],Gr.GetWinOrg()[1],Gr.GetWinOrg()[2])
Sym.SetLayer(SymLayer)
Sym.SetGpoint(NorthGraphic)
Sym.SetRad(Gr.GetWinScale()*Radius)
Sym.SetRot(0)
Sym.Plot()
x,y,z,key = Gr.GetCursor ()
Line.SetLayer (LineLayer)
Line.SetGpoint (LineGraphic)
Line.AddPoint(Gr.GetWinOrg()[0],Gr.GetWinOrg()[1],Gr.GetWinOrg()[2])
Line.AddPoint (x, y, z)
Line.Plot()
Sym.SetGpoint(TickGraphic)
Sym.SetRad(Gr.GetWinScale()*.25)
for corner in range(4):
..x1,y1,z1=Gr.GetWinCorner (corner,0)
..Sym.SetCoord(x1,y1,z1)
..Sym.Plot()
..if corner==3:x2,y2,z2=Gr.GetWinCorner (0,0)
..else:x2,y2,z2=Gr.GetWinCorner (corner+1,0)
..Sym.SetCoord(((x1+x2)/2.0),((y1+y2)/2.0),z1)
..Sym.Plot()

And away we go...

def Rad2DecDeg(Radians=0):
..return Radians*(180.00/3.1415926535897931)
We'll be grabbing some parameters later that will be returned in radians. In the US we (or at least I) visualize decimal degrees more easily. The rotation is just going to be displayed in a message area, so we might as well make it readable. In the actual program I import math, and use math.pi instead of the 3.14 number but I'm trying harder to make these posts independent of python.

Gui = PyVrGui()
Gr=PyVrGr()
Line=PyVrLine()
Sym=PyVrSym()
All this is typical, except this may be the first time that PyVrGr has been mentioned. This is a class that allows us to interact with the graphics window. In this case we'll mostly be getting information from it.

LineLayer=105
LineGraphic=1
NorthGraphic=58
TickGraphic=41
SymLayer=102
Radius=.75
Once it is all said and done there will be a north arrow in the exact center of the display, ticks (simple cross) in the center of the edges, and a line from the screen center to the current cursor position. Radius is in inches and will be set based on the screen scale later. All the symbols will go in the same layer, though there will be a different graphic for the side ticks, and the center north arrow.

Gui.DspMsg0('Rotation = %.2f'%(Rad2DecDeg(Gr.GetWinRot()[2])))
If we end it here, there will just be a message in the Vr message area that give us the screen rotation, helpful in it's own right, but let's add a graphic display.

Sym.SetCoord(Gr.GetWinOrg()[0],Gr.GetWinOrg()[1],Gr.GetWinOrg()[2])
Here is a python concept, GetWinOrg returns a 3 number list with x,y, and z as the values. In the above line SetCoord will set the symbols x,y,z location values with the list values returned by GetWinOrg. If the list returned would be (1,2,3) then list member [0] (the first member) would be 1, member [1] (second) would be 2 and so on. So Gr.GetWinOrg()[0], means the first member of a list returned by the function GetWinOrg() which is a part of the PyVrGr class of which Gr is an instance.

Sym.SetLayer(SymLayer)
Sym.SetGpoint(NorthGraphic)
Sym.SetRad(Gr.GetWinScale()*Radius)
Sym.SetRot(0)
Sym.Plot()
Just set the center symbols visual display based on the attributes decided on above and plot it, again; since GetWinScale returns the scale in units per inch (or mm) we will multiply it out so the display will be set in screen inches.

x,y,z,key = Gr.GetCursor ()
Get the current position of the cursor. This function will really come in handy later, but for now we just want a one time reading of where the current input device is located whether it is a stereo instrument or the mouse.

Line.SetLayer (LineLayer)
Line.SetGpoint (LineGraphic)
Line.AddPoint(Gr.GetWinOrg()[0],Gr.GetWinOrg()[1],Gr.GetWinOrg()[2])
Start by setting the display properties then using the method illustrated above, set the xyz to the window center.

Line.AddPoint (x, y, z)
Line.Plot()
Then add the second point based on the cursor location grabbed earlier, and plot the line.

Sym.SetGpoint(TickGraphic)
Sym.SetRad(Gr.GetWinScale()*.25)
Change the symbol attributes to those used for the corner tics.

for corner in range(4):
There are four corners, numbered 0-3 and the range function will just create a list starting at 0 and ending at the number before whatever is entered. In this case it would return (0,1,2,3). range() actually has a lot more flexibility including setting start point, end point, and increment, but for now we just need a list from 0-3.

..x1,y1,z1=Gr.GetWinCorner (corner,0)
GetWinCorner needs a corner number and a window number, in case there are multiple graphics windows open.
Corner numbers are LL=0, LR=1,UR=2, UL=3

..Sym.SetCoord(x1,y1,z1)
..Sym.Plot()
Plot a symbol in whichever corner is currently up in the list.

..if corner==3:x2,y2,z2=Gr.GetWinCorner (0,0)
There will also be tics at the midpoints between corners, to do this we'll get the average between current corner and next, but if current corner is 3 (UL) then next would be 0 (LL).

..else:x2,y2,z2=Gr.GetWinCorner (corner+1,0)
If the current isn't 3 then get the values from the next corner.
..Sym.SetCoord(((x1+x2)/2.0),((y1+y2)/2.0),z1)
set a symbol at the average between current and next.
..Sym.Plot()
and plot it.

Here are the end results.

No comments:

For anyone interested in trying VrPython for the first time or if you are early in the game, I suggest going to the earliest posts and working forward. I use VrPython every day for many wonderful things, needless to say it will change and could potentially damage a file. Any risk associated with using VrPython or any code or scripts mentioned here lies solely with the end user.

The "Personal VrPython page" in the link section will contain many code examples and an organized table of contents to this blog in a fairly un-attractive (for now) form.