starting with the discussions about the new CoordinateSystem and Container handling it became obvious that freecads current object modeling system is, while very powerful in general, limited to linear inheritance. Over the last weeks I tried to find away around that.
The Problem
Currently every Object in FreeCAD can have exactly one Parent from which it inherits functionality, Properties and Methods. This means that functionality can only be shared if the Objects in question can have the same base class. This is limiting for cases where one can clearly seperate a certain kind of functionality into a interface, but still needs some other functionality and hence must decide which base to use. Best example is the Coordinate System Discussion, but one can think about:
- Attacher: Some Part::Features should be attachable, some not
- Groups: Some Arch tools should be Groups, some not or some even OriginGroups
- Expressions: Some objects may want to be able to define Variables, some not?
I extended the FreeCAD type system with a new paradigm called "Extensions". They allow to create special objects, called extensions, which have properties and methods to achieve their functionality. Those extensions can than be added to absolutely any FreeCAD object, and it can be done from c++ and python! In c++ one simply uses multiple inheritance, in python one simply calls "addExtension".
- All Extension properties are added to the object and are accessible from c++ and python
- All Extension methods are added to the object (in c++ really all, in python all methods of the ExtensionPy type, so the designed python interface)
- It is possible to override Extensions methods in c++ and python when they are prepared for it in the extension class. This allow extensions to be customizable by the extended objects.
Code: Select all
class AppExport Part : public App::DocumentObject, public App::OriginGroupExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(App::Part);
};
PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::DocumentObject, (App::OriginGroupExtension))
Part::Part(void){
GroupExtension::initExtension(this);
}
Code: Select all
class Part:
def __init__(self, obj):
obj.addExtension("App::OriginGroupExtensionPython", self)
obj = doc.addObject("App::DocumentObject")
part = Part(obj);
Now every document object can also be for example a group. Hence one cannot anymore check for the GroupObject by type to get all groups. Therefore new functions have been introduced to iterate over all Extensions of an Object and get the wanted extension if available:
Code: Select all
App::DocumentObject* obj = ...;
if(obj.hasExtension(App::GroupExtension::getClassTypeId())) {
App::GroupExtension* group = dynamic_cast<App::GroupExtension*>(obj.getExtension(App::GroupExtension::getClassTypeId()));
group->addObject(...);
}
The Status
The current Implementation is not perfect, but the fine tuning I like to to when I know it is something that can be included. The Proof of concept is done: https://github.com/ickby/FreeCAD_sf_mas ... Extensions
There you can find the basic extension infrastructure as well as the porting of the app part of group/coordinatesystem classes to the new system. ViewProviders are still old schema.
The Question
Is this something to include into FreeCAD? It does touch the inner workings for sure, and it is not the most subtle change. But IMHO very powerful.
Love to hear your opinions!