3D Insiders' Summit, September 25-26, 2012, Black Hawk, Colorado

Learn More
View Agenda
Ameristar Hotel/Casino


As I was sitting here trying to come up with a topic for this post, I was thinking that while I have a million things going on, none of them are post-worthy in and of themselves, and I'm sure nobody wants to read a general post about being busy.   Then I had an epiphany, there is something bigger going on that ties it all together.

3D InterOp is going through a paradigm shift.
The longstanding objective of InterOp has been to convert CAD data from 1 format to another while retaining the highest quality.  The interface was originally designed with this very simple objective in mind -- give the user a small, clean interface, independent of input or output format.  
This all works pretty well, the interface is certainly easy to use.  When we added the CGM modeler though, it presented us with some new challenges.  Being newly componentized, CGM doesn't have all of the somewhat clunky add-ons that we've put into ACIS to support additional types of incoming data, for instance product structure and PMI.  We were faced with a question, do we add these in the same way as we've done in the past so that we can translate all data into 1 format, even when it isn't very clean?  The question was particularly relevant because we knew we'd be adding graphical data soon, which didn't have anywhere to go in either ACIS or CGM.  
This is where we come to our paradigm shift.  We found ourselves asking how people will really use the data and how do we modify the interface with this in mind?
For geometry, this part always came for free.  You convert files into a modeler, which then provides a full range of APIs for doing something with the new data - query it, change it, whatever you want.  As long as the data is usable by the modeler, InterOp's job is done.
So we had mostly avoided this question, but faced with adding new types of data to both CGM and ACIS, we had to truly address it.  Even if we add all new data, like graphical data, into the modeler, we have to make sure there are APIs that allow the user to get it back out and use it.  That starts to make things very complicated.
We decided to go for a cleaner approach that was very focused on making sure people had a targeted way of using every type of incoming data.  Through this examination, we came to a few key realizations:





  • The objective of 3D InterOp is not simply to convert from one format to another, but rather to query the source document for different "containers" of data, converting only when necessary.
  • Rather than one size fits all, the interfaces for reading such containers should vary with their complexity and downstream use.
  • If the data is very simple, then a direct API is a great way to access the data, so, for instance, we've added new APIs for extracting product structure and graphical data in memory.  This means that applications can put the data directly into their internal representation without any file interaction, saving steps and time.  Here the interface is a little more involved because the user is exposed to more.
  • If the data is more complex, the obvious case being geometry, then you need to put it into something that knows how to represent it and that offers tools for operating on it (the modeler).  So here, InterOp's primary responsibility is getting the data into the modeler in the way it expects so it is ready for downstream usage.  The user interface for this is very simple because all the work goes on behind the scenes.
  • There will also be meta data that connects all the different containers together, e.g. attributes and PMI.  We're working on figuring out this part.
This is a really cool way of looking at things because it allows us to expand the InterOp interface to handle new data in a concise and flexible way.  That's the big picture - which means that in my smaller picture, helping to roll this all out to our customers, there is certainly a lot going on.

Below is an Example of Extracting a Single Instance from a Product Structure




While procrastinating (avoiding writing this blog entry for as long as possible), I debugged an interesting problem.  This gives me something to talk about here.  What follows might be simple or obvious, but I find that considering tiny details very carefully is a good way to improve the quality of the code I write.  Consider the following code snippets
sphere* make_sphere( double radius, double x, double y, double z);


void do_something( /* ... */ )

// ...

sphere* my_sphere = make_sphere(10,0,0,1);


class position
// ...
position( double x, double y, double z);

sphere* make_sphere( position const& p, double radius);

void do_something( /* ... */ )

// ...
position center( 0,0,1);
sphere* my_sphere = make_sphere();

With the second version of the code, you actually need to have a class structure defining your objects (which requires more code), but strong type checking can help you.  There is also an annoyance with the second version of the code that you may have to write code converting between various types of geometric operators.  This (having well thought out basic types for mathematics) is one area where CGM does particularly well.
The actual bug I looked at was closely related (class names changed to protect the guilty).
class nifty_curve_calculator


// ...


    nifty_curve_calculator( double convergence_epsilon, double fitol_desired, ...);


In nifty_curve_calculator, exact points on a curve are calculated to convergence_epsilon.  The nifty_curve_calculator then concatenates a bunch of exact points on the curve into a bspline fit for the curve.  The fitol is the requested distance of the bspline from the exact curve being calculated.  The two tolerances mean completely different things, but the compiler will happily accept code which switches the two tolerances.  In the case I looked at today, the two parameters were swapped which resulted in code that worked most of the time, but caused a hang with more poorly behaved geometry.  We should expect that convergence_epsilon is a lot smaller (10^3 times smaller or more) than the fitol_desired.
There is a whole constellation of bugs like this that can be avoided by making a careful object model.  A simple way to improve type checkability is to avoid argument lists where convertable types are right next to each other.  Avoiding void* arguments like the plague also fits into this line of design improvement.  An additional help is to only require arguments in a constructor which are absolutely mandatory and use get/set methods to control the other parameters.  
One area where I run into problems with this is writing code (e.g., for MESH_MANAGERS) where large objects are stored using arrays of indices into other arrays.  If everything has type int (or size_t if that is how you roll), then compiler type checking doesn't help much.  Pointers are slightly better for this, but then you get into ownership issues.  I really wish you could do typedefs that aren't convertable to each other but have the same operations as integers.
Does you have any suggestions or comments for improving type checking in geometric code?
Twitter Facebook LinkedIn YouTube RSS