Attributes are a feature of Onshape's FeatureScript that seem simple at face value, but are incredibly powerful. In the simplest terms, attributes are data attached to entities in Onshape. An attribute requires an entity (Any type of geometry from bodies to faces, edges, etc), a name, and data.
To add an attribute, simply:
setAttribute(context, {
"entities": entities,
"name": "myName",
"attribute": "myValue"
});
To retrieve an attribute:
getAttribute(context, {
"entity": entity,
"name": name
});
But that's not the only option. For multiple entities, getAttributes(). To get all the attributes: getAllAttributes().
Attributes can also be used to generate queries. qHasAttribute(name) will look for entities that have the named attribute, allowing you to essentially tag entities and filter for them in a way that no other query supports. That functionality can get even more specific with qHasAttributeWithValue(name, value). Can't match all of the data (because its a map), use qHasAttributeWithValueMatching, which will match all of the key-value pairs provided.
Attributes remain on an entity until that entity is destroyed by another operation (or the feature creating them is suppressed). Attributes can even be set on entities like the origin or default planes (ensuring they don't get deleted by mistake!).
What type of data can I store in an attribute? Pretty much anything that can be made into a variable in FeatureScript can be stored as an attribute. There is a string size limit in Onshape, but in their words "640k ought to be enough".
Why are they so useful?
Attributes solve a few common problems that new FeatureScript developers face when trying to solve a workflow.
Passing Information Between Features
FeatureScript is setup in such a way that it can't look at feature definition of other features in the tree. FeatureScript can't see what the model looked like at other points in time. A FeatureScript is its own black box that it cannot see out of. So how do you make multi step features work together? Store data as attributes! In one feature, you can store the entire definition as an attribute, or maybe results of a process, or label entities without having to find them again. In another feature, those results, inputs, queries, etc, can then be retrieved and used again.
This is useful for:
Features that allow a user to "edit" in the middle of a feature. Think a feature that generates a tool body, and then does stuff with it
Features that have a long and difficult first step. Features that do a lot of calculation can be split up to make the long regen time happen in one feature - store the result - and then the process which requires more user modification can be done in a second step. The overall regen time of the model won't improve, but it will make editing and designing much easier as the entire feature won't be doing a full regen for each edit.
Simplifying feature interfaces. Making a single feature do everything can be powerful, but it can make the UI turn to chaos. Breaking out portions of features into different steps can simplify the process and make it a bit easier to understand. Instead of an array input, try multiple features in the tree connected with named attributes.
Automated naming. Simply store a counter, and retrieve it in each feature.. +1... and then you have sequentially named parts/features/etc.
Integrated Applications
One thing that Onshape really gets right is that FeatureScript and the Onshape API aren't completely separated. Features can be added to the tree through the API, but arbitrary FeatureScript functions can also be run with the API (making no changes to the model context). The EvaluateFeatureScript endpoint can be used to retrieve attributes from models, connecting a FeatureScript process to an API process. This is especially useful for fully automated solutions where there is no user to make a selection.
The most common use case for me is to label geometry with setAttribute, giving a name and a unique ID, and then in my API application - I use qHasAttributeWithValue to retrieve the specific entity.
Super charging Custom Tables
If you have ever made a custom table in Onshape, you've found that the selection of inputs for tables are not nearly the same as those for custom features. Need a more complex set of options? Attributes can be used as a work around for this. First we create a custom feature to define any of our input data for our custom table. This can include any kind of input that a custom feature has. Next we can build our entire custom table in our custom feature. By storing the entire table as an attribute on the origin, we can then make a custom table that simple retrieves the stored table and displays it. The only note here is that custom tables run after regeneration is complete, where-as with this method the table will be generated where the linked feature in the tree is. So make sure it doesn't get buried at the wrong point in the feature tree!
How do you use attributes?
This certainly isn't an exhaustive list of use cases for attributes. Taking a look through the Onshape standard code - you'll find a lot of creative uses for attributes. From Sheet metal, to frames, to holes. Onshape makes extensive use of attributes for their own internal features. Knowing about the power of attributes can help solve problems that you come across in FeatureScript development. Have you used attributes in creative ways?
Comments