Monday, August 31, 2009

Converting SketchUp models to QUEST pdb files

SketchUp is a 3D modeling tool which was originally developed by @Last Software, which was purchased by Google. Google now provides the SketchUp tool for free, to encourage modelers to create real-life geometry for its Google Earth tool, as well as populating their 3D Warehouse, which is a huge repository of 3D models.

SketchUp is very easy to use, easy to learn, and very powerful for being a free tool. It's much easier to use than the QUEST CAD world, though the free version of SketchUp has some limitations which make it difficult to get SketchUp models into a format that QUEST will recognize. The free version will only export to the Collada format, which is not supported by QUEST. This means you have to use some intermediary program that can read Collada files and export to something QUEST understands (i.e. VRML 1.0, DXF, OpenFlight Obj).

Until now, the easiest way to get from SketchUp into QUEST was to shell out $495 for a license of SketchUp Pro, which includes a DXF exporter.

I say until now, because luckily the Guitar-List.com website has put together a Ruby plugin for SketchUp that exports from SketchUp to DXF, for free.

That's only half the battle, so with permission from Guitar-List.com I was able to modify their script to use QUEST's built-in DXF to PDB conversion tool so you can automatically export from QUEST to DXF to PDB.

You can download the ruby script here. Download it to your Program Files/Google/Google Sketchup/Plugins folder.

One pre-requisite for this script to work, however, is that you must have your dwg2pdb.bat file configured so that DENEB_PATH is set to whatever your Deneb or Delmia path is. You also must set the DENEB_PROD_DIR to point to the quest folder (default was vmap for me) and make sure LM_LICENSE_FILE is pointing to the right place. These settings should more or less mimic some of the settings from your quest.bat file.

Next copy the dwg_cmd file from your quest folder to the bin folder, and set your default units to inches.

The last thing you'll have to do is edit line 64 of the skp_to_pdb.rb file to point to your dwg2pdb.bat and dwg_cmd files.

It may take a bit of work to get set up, but I've been using it for a few weeks and it's nice to be able to use a modern 3D drawing tool and easily load the geometry in QUEST.

A few notes on usage, make sure you draw to the proper scale in SketchUp, and be mindful of the origin. If your geometry is far from the origin in SketchUp it'll be far from the origin in QUEST, too.

Again if you want to contact me directly just hit me up here or leave a comment.

Tuesday, August 25, 2009

How to run QUEST from Excel VBA and wait for QUEST to finish

This post is not meant to show you how you can use VBA to run BCL commands in QUEST. It's just a small tweak to how we run QUEST in this previous post. In that previous VBA solution, I used the baked-in Shell command to run a QUEST.bat file in BCL mode.

One reason I can think of for using this functionality is being able to automatically run some experimentation in QUEST, without having to mess with sockets (until we can get an easy to use library for QUEST sockets). This way we can have some Excel worksheet with accompanying VBA code that will run QUEST, wait for QUEST to exit, then check some output file from QUEST before advancing in the experiment. I am working on a project where this is pretty much exactly what I'll be doing.

To make this work, we just need to use a different Shell command, that will effectively halt execution of our VBA code while QUEST runs, and resume execution once the QUEST process we started ends.

Basically, we will re-use the code found here to replace our basic Shell call. All the linked code does is start QUEST using the Shell command, and uses a few Windows API calls to monitor the QUEST.exe process we started, looping until that process ends. Once the process ends, we exit the loop and VBA continues executing code.

I've updated the QUEST_BCL_Addin.xla file which can be downloaded here to include this new ShellAndWait functionality. If you call the SaveABCL function with LaunchQUEST = True and TransferToMenu = False then your code will wait for QUEST to exit before resuming.

I'd like to start adding some QUEST BCL User Defined Functions to this addin in the near future. I have some written already, but would like to get them properly formatted for release. If you have any you'd like to donate drop me a line using this contact form.

Split function in SCL

VBA has a function called Split that is handy for parsing strings to an array based on some delimiter.  All you have to do is dim out an array variable and say:
ArrayVar = Split( "the,split,string" , "," ) and your ArrayVar will contain three strings, "the" , "split", and "string".
There is no in-built function in SCL to do this, so I have put together something that should work in a similar fashion.  I have named the function the same, though the difference here is because SCL's arrays are fixed in size, where with VBA you can dynamically size arrays.
To get around this, you must first declare a String array in the Var section of your calling procedure (or globally), and pass that into the Split function as the last argument.  You must also provide the upper bound of the array to the function.  This upper bound is not the highest index of the array (which is one minus the size of the array), but just the size of the array.  One easy way to use this information is to declare your arrays using Const's denoting the size of the array.  For example:
procedure some_proc()
Const
arr_size 10
Var
my_array : Array[ arr_size ] of String
........
So, in this case, if you wanted to split a string into my_array, you would do this:
num_elems = split( 'some delimited line' , ' ' , arr_size , my_array )
where num_elems is the number of chunks of text split into the array.  If there are more chunks than there are bins in your array, any trailing chunks will be ignored, and no error will be raised.
I have tested this routine a bit, so it should work fairly well, even with multi-character delimiters.  On a side note, you should put this routine into your always available routines in QUEST.

routine split( the_line : String ; the_delim : String ; max_elements : Integer ; Var the_array : Array[] of String ) : Integer
Var
write_idx , delim_idx : Integer
check_char : String
Begin
while( len( the_line ) > 0 ) do
if( write_idx < max_elements ) then
delim_idx = index( the_line , the_delim , 1 )
--write( 'delim_idx=' , delim_idx , cr )
if( delim_idx > 0 ) then
the_array[ write_idx ] = leftstr( the_line , delim_idx - 1 )
the_line = rightstr( the_line , len( the_line ) - delim_idx - len( the_delim ) + 1 )
else
-- no more delimeters left - write the last of the_line to the array
the_array[ write_idx ] = the_line
the_line
= '' -- empty the_line so we break from the while loop
endif
write_idx = write_idx + 1
else
the_line = '' -- empty the_line so we break from the while loop
endif
endwhile
return write_idx -- return number of pieces written to array
End

Tuesday, August 18, 2009

Evaluating string expressions in SCL

I have found a few cases where it would be better to allow a user to enter not just numbers, but some expression that can be evaluated to a number. An simple example of this is just evaluating calculations, like "10+2*(8/5)". Ordinarily, if someone entered this and you were expecting a number, you may have a real problem. But luckily QUEST provides a means for evaluating strings to Real values, or even to String values.

The method involves calling out to BCL to evaluate an SCL expression, returning either a String or Real value. The associated BCL commands are SCL_NUMERIC_EXPR and SCL_STRING. You just pass in the SCL expression and read the resulting BCL_MSG variable as the result.

Here are two SCL routines that make these BCL commands a bit easier to use:

BCL_VAR
bcl_msg : String
routine evaluate_scl_expr( the_string : String ) : Real
Var
bcl_err : Integer
Begin
bcl_err = bcl( "SCL_NUMERIC_EXPR('" + the_string + "')" )
return val( bcl_msg )
End

routine eval_scl_string( the_expr : String ) : String
Var
bcl_err : Integer
Begin
bcl_err = bcl( "SCL_STRING_EXPR('" + the_expr + "')" )
return bcl_msg
End

Tuesday, August 4, 2009

Quest_Geom

I've used the same solution Martin described, and clumsy is an understatement, but it has worked fairly well for me in the past.  From what I've seen of the format, it's similar to the wavefront obj format, if that helps...

 

Here's an overview of how a basic QUEST geometry file is laid out, just from my messing around with these files...I have no expertise in how the format works, this is all based on observation and guesses.

 

All these observations are based on a basic rectangular block I created in QUEST D5R18.

 

Basically the file starts with the number 12, probably a version number or something, then some vector transformation information, probably for locating and rotating the geometry, though I've never messed around with that.  After all this information there is an asterisk indicating the geometry section is beginning.  I've found that older geometry files don't have this asterisk to delineate between geometry sections.

 

For a basic rectangular box, there are 8 points, for each of the 8 corners on the box.

After the first asterisk, there is a number indicating the number of points in the geometry.  Points serve as vertices for lines.  After listing the number of vertices, you list out the xyz location of each vertex.

 

After the vertices you list out something, and I either never knew or forgot what it is, but the geometry I'm looking at now has a zero.

 

Next we indicate the number of and list out all the lines in the geometry.  So if we have a rectangular box we have a total of 12 lines, as you can see in the diagram below.

To describe a line, we have to provide the index of each of the two vertices in the line.  QUEST basically reads the initial list of points in as a zero-indexed array, which means the first entry is point number zero in the array.

In this case, line 1 is composed of points 1 and 2, so our first line is represented as "1 2"

Next we indicate the number of and list out all the polygon faces in the geometry.  So if we have a rectangular box we have a total of 6 faces, as you can see in the diagram below.

The definition of a polygon is simply a list of the line indices composing that face preceded by the number of lines composing the face.  The direction of the line makes a difference, here.  The lines of a polygon must all go in the same direction - either clockwise around the face or counter-clockwise.  So, if a line is defined backwards from the direction of the other lines in the polygon, you must place a minus sign before that line number, and QUEST will interpret that and represent the polygon properly.

The last section we'll discuss is text.  I'm not entirely sure where the text section goes, but with the simple polygon block example, it seems we can just put the text section after the polygon face section.

To define the text, you must first provide the number of text objects to place on the geometry.  Next, start listing out the text values.  The first line of a text definition is the text string value.  The next four lines after that represent the location/rotation transform for the text placement.  To be honest, I'm not really sure how to interpret this.  At some point I'll try to post some routines I built for reading and writing these rotation matrices, but in the mean time, to work with text, there is an easier way.  Basically, just build your basic geometry, and manually place the text, save that out, and copy the transform matrix and use that.

So what we end up with with all this information should be a simple plain text file that we can feed into QUEST to build some simple rectangular box.  Here it is:

 

I hope this has helped give some basic idea of how to build simple geometry files without using QUEST.  Leave a comment if you have any questions about QUEST geometry that you'd like to discuss.