DeepSOIC wrote: ↑Wed Jan 23, 2019 7:22 pm
I wonder if extension mechanism is the right tool for the job. I feel like you'd be better off implementing a Sketcher::Geometry base class from scratch, that will contain a Part::Geometry object as a py pointer/smart pointer. What's the benefit behind using extensions instead? Seems overly convoluted to me...
Hi there!
Composition or public inheritance were the first options I considered. Compatibility is the major drawback. It hits the user quite hard.
Because you are right about it being convoluted, at least for the very flexible implementation I described at the beginning of the thread, I am considering splitting the implementation into two stages. Stage1 would include only c++ defined extensions. Stage2 would include the option to have user-defined Python extensions.
To partly compensate for the basic needs of Python power users, Stage1 would include two very basic extensions, one for an int and another for a string. This should allow power users to reasonably increase flexibility in their designs at a low cost.
Now replying to your question in more detail:
The main advantage of the extension approach over Sketcher::Geometry is backward and forward compatibility with the sketcher. A (minor) additional advantage is to enable Python power users / WB developers to extend their geometries. The latter may be proved interesting for the Civil Engineering WB, for example.
Creating a Sketcher::Geometry implemented as a Part::Geometry (as composition, as you describe, no public inheritance therebetween), would lead to a non compatible serialisation format. In a good design, you may be able to get away with backwards compatibility, but not with forward, i.e. you may be able to redirect the old property to the new property (this has been done before in many places), but older FC would not understand the new property at all, so all the geometry stored with a new version will not be processed when read with an old version. This means an empty sketch on load. Of course, when loading in an older version there may be limitations of functionality, but it would read the sketch within the functionality that was available at that older version.
Additionally Part::Geometry is used in the Sketcher via Part::PropertyGeometryList, that is the property that implements serialisation, by calling the virtual function that every geometry inheriting from Part::Geometry has. You would need to create a Sketcher::PropertySketcherGeometryList as a list of Sketcher::Geometry and make this new property the one in the sketch. All this is not a lesser amount of work than a stage1 implementation of Geometry Extensions, and would be sketcher specific.
Considering Python geometry extensions (stage 2) is indeed a more convoluted implementation. It offers great flexibility indeed. However, here my current level of understanding of Python and its c++ interfacing is surpassed. I have an idea that I need a special extension to store a proxy object created by the python user and redirect the virtual methods to the python proxy object. I still get lost in the internals.
At this moment, I have just finished working on stage 1, which appears to work. I will release this code so that the community can check the code, the implementation and have a saying. You are more than welcome to have a look to the code, compile it, play with it and let me know what you think.
Code:
Branch
Usage (do forgive please my crappy Python skills,
, I do it step by step, a pro can probably produce a one liner,
, not me
):
Example 1: adding an Int geometry extension to the first geometry of the sketch with a integer value 9:
>>> geometrylist = ActiveSketch.Geometry
>>> geo1 = geometrylist[0]
>>> gie = Part.GeometryIntExtension(9)
>>> geo1.setExtension(gie)
>>> geometrylist[0]=geo1
>>> ActiveSketch.Geometry = geometrylist
This can be read back:
>>> geometrylist = ActiveSketch.Geometry
>>> geo1 = geometrylist[0]
>>> geo1.getExtension('Part::GeometryIntExtension')
<GeometryIntExtension (9) >
NOTE: You may use "Value" on the extension to get the integer 9.
Example 2: modifying a string to a geometry object
>>> geometrylist = ActiveSketch.Geometry
>>> geo0 = geometrylist[0]
>>> gse = geo0.getExtension('Part::GeometryStringExtension')
>>> gse
<GeometryStringExtension (MyDearCircle) >
>>> gse.Value
'MyDearCircle'
>>> gse.Value = 'OhGoshMyPerfectCircle'
>>> geo0.setExtension(gse)
>>> geometrylist[0] = geo0
>>> ActiveSketch.Geometry = geometrylist
When saved, the xml looks like this (example with 3 extensions on a sketch having a single geometry):
Code: Select all
<Property name="Geometry" type="Part::PropertyGeometryList">
<GeometryList count="1">
<Geometry type="Part::GeomCircle">
<GeoExtensions count="3">
<GeoExtension type="Sketcher::SketchGeometryExtension" id="5"/>
<GeoExtension type="Part::GeometryIntExtension" value="9"/>
<GeoExtension type="Part::GeometryStringExtension" value="OhGoshMyPerfectCircle"/>
</GeoExtensions>
<Construction value="1"/>
<Circle CenterX="11" CenterY="0" CenterZ="0" NormalX="0" NormalY="0" NormalZ="1" AngleXU="0" Radius="30"/>
</Geometry>
</GeometryList>
</Property>
Note, the SketchGeometryExtension is just a placeholder at this time.