Friday, September 19, 2008

Inquire Element Location

The QUEST BCL Language has a command called "Inquire Element Location", which just takes the specified element's location and prints it to the BCL output window.  It may not seem at first to be of much use there, but we can make use of a special variable declaration in SCL to access this output text.

To illustrate using the BCL output from SCL here's an example SCL macro which will print the location of every element in the current model to a tab-delimited text file, and launch that file for viewing in Excel.  I have another macro which I may share that will let you read this format back into QUEST, modifying element locations as read from the text file.

Handling 6 Degree of Freedom data in SCL

QUEST elements are located in 3D space in the QUEST world, and can be rotated about 3 axes, giving a total of six pieces of information required to completely locate some element in 3D space in QUEST.  In order to give our SCL code some degree of modularity and elegance, we need an easy way of passing all the location information.

We can settle for just passing all six parameters around every time we want to work with QUEST locations.  Over time this can get old, especially if you work a lot with locating elements in QUEST.  Also it helps to have a good methodology in place for dealing with locations, like having a routine for pulling the location of an element, or a procedure for locating an element with little hassle.

The solution I use is to have an SCL Structure for a 3D location.  From the QUEST SCL docs:

A structure is a group of variables which can be manipulated collectively or individually. A structure is analogous to a record which consists of many fields. 

What this means to me is that I can have as much information stored in a structure as I need, and move that information around by passing just a single variable.

The name I chose for my 3D location structure was "spot", and here's how I've defined a spot in SCL:

structure spot
x : Real
y : Real 
z : Real
yaw : Real
pitch : Real
roll : Real
elem_there : Element
endstructure

Basically the spot is just keeping track of 3D location and orientation, storing these values as type real.  I also have a handle to an element, which you would set when inquiring about en element's location.  My thinking there was that some day I might need a way to populate locations in a QUEST model without overlapping items, so I added the elem_there field.

One thing to note about using structures, is that you can work with a structure variable, or a pointer to a structure variable.  Those of you with experience in C probably know the distinctions and caveats to each technique, but I usually use a pointer to a structure, just because it's a little less confusing thinking about whether or not the structure I'm using is what I think it is.

Moving forward, we now need a way to inquire the location of an element in QUEST and return a pointer to a spot structure, after populating a spot.  The routine itself makes use of a bcl command called "INQUIRE ELEMENT LOCATION", which simply places the location of the element in a comma-delimited text string on the BCL command output window.

Accessing the text in the BCL command output window is pretty simple, just declare a variable at the top of your scl file in a declaration section called "BCL_VAR".  The string to declare for the BCL command output window is "bcl_msg : String".  The only other BCL_VAR is "bcl_status : Real" which will tell you the result of a BCL command.  More on that another time.

So your BCL_VAR declaration should look like this:

BCL_VAR
   bcl_msg : String

Now for the inquire_loaction routine:
routine inquire_location( the_element : Element ) : @spot
Const
   the_delim ','
Var
   bcl_err : Integer
   the_loc : String
   result : @spot
Begin
   result = new()
   if( the_element <> NULL ) then
      bcl_err = BCL( "INQUIRE ELEMENT '" + the_element->name + "' LOCATION" )
      the_loc = bcl_msg
      parse_str( the_loc , the_delim , result->x , result->y , result->z , result->yaw , result->pitch , result->roll )
   endif
   return result
End

A few things to note:
  • result is declared as being of type @spot, which is a pointer to a spot structure
  • We make use of the new() routine to allocate memory for a spot structure.  We have to remember to free this portion of memory later using the free() procedure.
  • We make use of the parse_str routine for parsing the string into chunks for us.  This is a built in routine in SCL.
  • We use the -> symbol as a member pointer rather than a dot because we're working with a pointer to a structure.  If we were using a straight structure, we'd us a dot.
The routine basically just executes the "INQUIRE ELEMENT LOCATION" BCL command, extracts the output text from the BCL output window, and parses it to a new pointer to a spot structure, and returns the pointer to the calling function.

To tie it all together, see the write_elem_locs.scl file to see how we can build a tab-delimited text file of all our element names and locations, and launch the file in Excel.

You can execute the macro by saving it to an SCLMACROS folder in a QUEST library, or you can compile the file and execute the write_all_locations function through BCL.

No comments: