Extended attribute modeling is one of the things that came up in the SMTK 3.1/4.0 planning topic:
I’m starting this discussion to work out the implications of some of the alternative designs below. Note that they are not mutually exclusive.
Variant 1: Extensible Items (and ItemDefinitions)
Being able to subclass smtk::attribute::Item would allow people to model more complex constraints, to have functor-like item values, and to add additional constraints to attributes – such as closer ties between model entities and values stored on them. This would involve the following changes
Replace the smtk::attribute::Item::Type enum with something like smtk::resource::Resource::Index (a size_t value that hashes each subclass’s type name).
We could also allow subclasses of Attribute to implement application-specific logic/constraints. In hydrology, bed layers might be a candidate for this.
Potential Issues
Each Item and ItemDefinition would be required to
serialize/deserialize itself, which could cause problems with versioning.
implement its own GUI representation, which could be a lot of work.
Variant 2: All attribute edits must be operations
Any changes to attributes would be required to use the operation framework. This would allow changes to be monitored via the operation manager (assuming one is used) and could thus signal user interface components to respond. The operation system would allow complex constraints (via observers). Unlike Variant 1, the constraints would not need to be local to the new item class.
A twist on this would be to allow some items to “be operations.” In this case, an item might compute its value (say, the mass properties of some associated model entities) rather than be assigned values by a user.
Potential issues
By forcing users to rely on the operation system’s observers (and thus relying on having an operation manager), the usual observer-responder problems could exist but they might also be limited in scope because the operation framework might be used to track operations and detect cycles.
Variant 3: Observers (a.k.a. Crazy Town)
All (or at least most) methods in the current attribute system that make changes would emit changes as they occurred to any observers that care to monitor them.
Potential Issues
There are a lot:
debugging observers/responders can be difficult since the call stack can be unpredictable.
fragile/brittle, since observers don’t know of other observers and/or ordering of responders
unexpected behaviors can result from interactions between responses to the same event
threaded/asynchronous operations could make it problematic for observers that need to be invoked on a specific thread (i.e., specifically the GUI thread).
Variant 4: Special cases
Rather than trying to satisfy a large set of requirements, we could adopt a number of smaller, targeted solutions to specific problems brought up by use cases:
One common use case (RGG, IBAMR, …) was operations that need to update their parameters (attributes) as changes are made to the set of associated model/mesh entities. We could add a method to smtk::operation::Operation like configure() that would be called by the application (GUI or script) to give operations a chance to update their parameters (or their defaults). This would let us pass default bounds to ParaView widgets that make sense for a given context.
Potential Issues
We might not get everything we want and yet be stuck with a lot of maintenance problems where small changes create corner cases that must be handled.
Categories
In addition to the changes above to deal with extending the functionality of items, we will allow categories to be assigned directly to attributes. Child attributes/items with categories assigned will “percolate” up to views so that all of the categories appear in the “limit view by category” combo box.
If we are diligent, I think having items serialize/deserialize themselves could result in a cleaner implementation. I don’t see this is an issue, but it will be a bit of work to establish a good pattern (we should look at boost archive either to use or for inspiration).
The GUI representation could be daunting (as it spans dependency layers), but we may be able to mitigate some of the difficulties with CMake macros (like we have for operations and plugins).
I am concerned that this issue could be “doubling down” on the challenge of maintaining callback-laden operation code. It would be a nice way to put attributes on the server, though (if that’s where we think they should be).
I think this could solve a large number of our current pain points. In balance with Variant 1, it could reduce the number of custom items we need to create (while custom items could satisfy our remaining needs).
Versioning is a problem we should fix. I oversee it when implementing the Json code. A good entry point would be at smtk::attribute::Resource level we add a version string when releasing smtk 3.1. I do not know enough about its usage in smtk. I will bring it up in next week’s meeting.
If we are careful about the GUI, it can be an acceptable work. An operator which uses several custom items only needs a custom ui view. Good documentation/boilerplate code would also help.
So in terms of Variant 2 and qtOperatorView if all operators had a bool update(attribute::ItemPtr) method then the qtOperatorView could listen to qtItem’s modified() and call the update method to enforce the Operator’s “constraints”. If the update did change the operator’s attribute resource then it would return true - indicating that the View’s widget would need to be recreated.
This has been merged and has a working example: the oscillator’s EditSource operation now populates its center and radius parameters whenever the associated model entity is changed (by overriding the new configure() method). When the associated model entity is an auxiliary geometry with the appropriate properties marking it as an oscillator, then the center and radius are copied from floating-point properties on the model entity into the operator’s attribute. When the associated model entity is a model, then the bounding box center is used for the source center and the bounding box size is scaled down and used as an initial radius.