Coupled math of OpenCascade
That’s a post without a purpose, just a few notes on the things that were not crystal clear to me before. So, let’s go.
OpenCascade is full of mathematical gems that have not seen any changes for decades. That’s completely fine as the math is basically right or wrong. We do not expect changes in the fundamental things as otherwise how fundamental are they? The French developers of CAS.CADE left us a great legacy of numerical approaches to solving different geometric problems. However, adopting them for production use is sometimes hard. Here’s why:
- What if your project does not have OpenCascade as a dependency and you’d like to avoid it as much as possible? Having an extra dep could be quite an obstacle, given that OpenCascade is a big set of interconnected libraries. If you’d like to use just a sole algorithm out of it, you’ll unlikely want to charge a full clip of OpenCascade tools into your software.
- You might be not aware that one or another algorithm simply exists in OpenCascade.
- The interfaces that OpenCascade provides might not play well with your own interfaces.
The strong coupling between CAD kernel’s algorithms is not unusual. Although all computations are often finely modularized, they tend to share the common ecosystem, including base classes, resource management (including memory), error reporting, journaling, etc. The entire architecture of a CAD kernel is designed for a purpose: to provide a math engine energizing a typical CAD system. This whole thing is not just a bucket with geometric algorithms from which you can randomly pick up an item at your pleasure. At the same time, since we have OpenCascade in sources why not try to decouple some algorithms and use them out of the kernel? You know, just like a brain in a vat.
Here’s the thing. Whenever you need some workable algorithm, you can get into something like github (or whatever) and grab the piece of code you find useful. If the license terms permit this, you integrate the picked-up sources to your solution and forget about the original repository forever. With OpenCascade, such tricks are insanely hard.
Btw, OpenCascade does follow this approach itself: have a look at BRepMesh/delabella.cpp or Standard/Standard_Strtod.cxx. All these are foreigners.
The main complexity of decoupling is due to the usage of non-standard collections and memory management tools. Take Intf package as an example. This package contains a decent algorithm for intersecting 2D polygons. If you wanna grab it, it will go almost smoothly until you face the dependency on NCollection_Sequence. That “sequence” is a custom collection type of OpenCascade that brings a hell of stuff from such packages as Standard and NCollection with itself. As a result, if you want to take out a single algorithm you’ll essentially have to copy and paste it with all its guts, so it will start looking as if you were copying and pasting the entire library. I’m far from saying it’s a bad practice as, returning to the beginning, the old math is just right or wrong. That especially concerns the classes developed somewhere in 1992 for which you can be sure they are no longer evolving. But even though it’s not impossible, it really looks graceless. To get to the point where Intf feels good in your codebase, you’ll have to have several very intimate hours with the sources. Once I did that, I ended up having 156 files (!) copied instead of just one or two files expected. You got it.
It’s a no-brainer to conclude that avoiding in-house implementations of standard collections contributes to a lesser coupling between the algorithms. From this perspective, such a thing as a Sequence (or DataMap, List, you name it) would better have not existed at all. Again, the concern here is not the efficiency of such collections (still, what are the chances that you’ll come up with a better-than-standard implementation?), but the level of modularity. If all the algorithms used only the standard tools, we could have more easily extracted them out from the library and copy & paste them wherever and whenever reasonable. That would be a very sweet option for a number of projects that bring OpenCascade to their dependency lists while exploiting 1% of its functionality. At that would truly exploit open source to its max extent.