These two features are an attempt to introduce a more efficient way of cloning objects in FC for 3D presentation purpose. Yes, for presentation only, not geometry modeling, which may be able get supported in future development if it is feasible at all. App::Link enables cheap clone of the 3D representation (Coin3D nodes) of any DocumentObject within the same document, while XLink supports cross document link. XLink will not actively load any external referenced document, but will monitor document opening and closing, and will update its visual if the referred object is found/lost. Both Link and XLink are extremely cheap in storage, which are essentially just text references to document and object names.
The actual heavy load lifting is done by Gui::ViewProviderLink. Link and XLink are just demonstration of its effectiveness. Any object with a PropertyLink called 'Source' can use ViewProviderLink for in-document linking, and PropertyString named 'SourceFile' and 'ObjectName' for cross-document linking. ViewProviderLink will replace the Placement property of its linked object, which enables independent object positioning. It will include the linked object name (and optionally document name if it's in another document) when selecting/pre-selecting object/subelements. The link has shared DisplayMode of its linked object, meaning that if you changed DisplayMode (or any other ViewProvider property for that matter) in linked object, the visual of all links will be updated simultaneously.
Here is piece of code for demo
Code: Select all
import Part doc = FreeCAD.newDocument() box = doc.addObject('Part::Feature','box') box.Shape = Part.makeBox(10,10,10) part = doc.addObject('App::Part','part') part.addObject(box) part.Placement.Base.y += 20 for i in range(4): link = doc.addObject('App::Link','link') link.Placement.Base.x += i*12 link.Source = part link.ViewObject.Visibility = True doc.recompute() doc.saveAs('link.fcstd') doc = FreeCAD.newDocument() xlink = doc.addObject('App::XLink','xlink') xlink.SourceFile = 'link.fcstd' xlink.ObjectName = 'part' xlink.ViewObject.Visibility = True doc.recompute() doc.saveAs('xlink.fcstd')
A few notes of the implementation.
FreeCAD relies on Coin3D/OpenInventor for 3D presentation. The scene graph is organised using trees of nodes. Each nodes can be a property node, like color, transformation, or shape node. Coin3D conveniently supports node sharing, which means the same shape node can be added to multiple parents to be rendered in different colors, positions, etc. This is the foundation of what make ViewProviderLink works. I think FreeCAD's design doesn't have node sharing in mind, which causes a few challenges during implementation. First, there is no enforced subtree organization of the ViewProvider. There is a somewhat template, but derived classes are free to override, which makes ViewProviderLink possible in the first place, but also makes node sharing a bit unpredictable. The assumption of ViewProviderLink is, that there is a fixed root node, a direct child SoTransform node to handle the placement, and a SoSwitch child node to hold different display mode of the actual shape nodes. It is further complicated by the special ViewProviderGeometryGroup, which directly groups other child object's root node under its SoSwith node.
The other big challenge is how to handle selection correctly. There are two ways of handling selection/preselection in FC. For OCC based shapes, FC uses a single SoFCUnifiedSelection root node to handle picking. And send action to the picked node to let it handle its own selection state. The problem is that now the node can be added to different parents, so there must be a way to store multiple selection state. ViewProviderLink uses a specilized SoFCSelectionRoot node as a selection context storage for other shape node to get/set selection state information. This mechanism works in most of the time, but have a few glitches, because of the way Coin3D handles render cache, which I don't fully understand. For now, I took the simplest approach, by touching the picked shape node, which may effectively force redraw all appearance of that shape node. I don't know if there is any way to trigger cache update based on traversal path in Coin3D. OpenInventor gurus feel free to comment. During render cache building stage, it seems the selection state information may leak to other link instance node. The other way of selection handling in FC, is for the view provider to use SoFCSelection node to handle its own selection. I think this is obsoleted, and can be converted to the new selection model, so only limited support is offered in ViewProviderLink. This affects objects like VRML, mesh, etc. Also a new function, getElementPicked, is added to ViewProvider to give derived class the full path of the picked node to resolve linked object. And once again, special handling is required for ViewProviderGeometryGroup.
A side note. Previously, an object can only appear inside one App::Part (which uses view provider derived from ViewProviderGeometryGroup). This is precisely because there is no straight forward way of handling selection resolution of its children. Now, with ViewProviderLink, you can add as many links to the same object as you want to App::Part. You can create links to an App::Part, and added them to other App::Part, as well. And also, with the capability to cross link between documents, a whole new world is opening. That is, If everything works well, of course. This is just an exploratory attempt at the moment.