The base class for defining a set of geometry properties.
Each property consists of a (group, property)
name-pair and a typed value. The name pair allows for reuse of common property names (e.g., "diffuse") to be differentiated in interpretation by associating them with different groups. The only restriction on the value type is that it must be either cloneable or copy-constructible.
A set of geometry property values are defined when geometry is registered with SceneGraph by an instantiator and accessed by some downstream consumer entity. Each consumer specifies what properties it expects to find and what default values (if any) it provides. For example, the consumer could document that a particular property is always required and its absence would throw an exception. Alternatively, it could indicate that a property is optional and a default value will be used in its absence. It is the responsibility of the instantiator to make sure that the geometry property values are correctly defined according to the expected consumer's specification. Correctness includes such issues as key-value pairs placed into a correctly-spelled group, property keys being likewise correctly spelled, and values of the expected type. Correct spelling includes correct case. The instantiator uses the AddProperty() method to add new properties to the set.
To read the property (some_group
, some_property
) from a property set:
- Optionally test to see if the property exists by confirming the group
some_group
is in the set via HasGroup() and that the property some_property
is in some_group
via HasProperty(). Attempting to access a property with a non-existent (group, property) pair may lead to an exception (see API documentation below).
- Acquire a property value via the GetProperty() or GetPropertyOrDefault() methods. NOTE: Reading a property requires a compile-time declaration of the type of value being read. If the stored value is of a different type, an exception will be thrown.
Common workflows
The following examples outline a number of ways to create and consume geometry properties. By design, GeometryProperties cannot be constructed, copied, or moved directly. Only derived classes can do so. This facilitates strongly typed sets of properties associated with particular geometry roles. So, for these examples we'll exercise the derived class associated with proximity queries: ProximityProperties.
The string-based structure of GeometryProperties provides a great deal of flexibility at the cost of spelling sensitivity. It would be easy to introduce typos that would then "hide" property values in some group a consumer wouldn't look. In these examples, we avoid using string literals as group or property names (at least in the cases where the same name is used multiple times) to help avoid the possibility of typo-induced errors. That is not required and certainly not the only way to avoid such bugs.
Creating properties
Creating properties in a new group
This is a simple example in which a single group is added with properties of various types.
const std::string group_name("my_group");
ProximityProperties properties;
properties.AddProperty(group_name, "count", 7);
properties.AddProperty(group_name, "length", 7.);
properties.AddProperty(group_name, "name", "7");
Creating properties in the default group
Similar to the previous examples, the properties are added to the default group. Just be aware that if multiple sites in your code add properties to the default group, the possibility that names get repeated increases. Property names must be unique within a single group, including the default group.
ProximityProperties properties;
Aggregate properties in a struct
In some cases, there is a set of values that will always be accessed together (specified with coordinated semantics). In these cases, it makes sense to aggregate them into a struct and store that as a single value. This reduces the number of lookups required.
It's worth noting, that if the data value is a struct, calls to GetPropertyOrDefault() still operate as an "all-or-nothing" basis. If the property struct exists, it will be returned, if it's missing the default struct will be returned. There is no concept of a "partial" struct in which some undefined values in the struct will be replaced with their corresponding values in the default struct.
struct MyData {
int i{};
double d{};
std::string s;
};
ProximityProperties properties;
const std::string group_name("my_group");
MyData data{7, 7., "7"};
properties.AddProperty(group_name, "data1", data);
properties.AddProperty(group_name, "data2", MyData{6, 6., "6"});
properties.AddProperty<MyData>(group_name, "data2", {6, 6., "6"});
Reading properties
This section describes how to read properties under several different scenarios: (a) when specific properties are required, (b) when the consumer provides a default value for missing properties, and (c) when the consumer needs to inspect what properties are available.
Look up specific, required properties
In this case, the consumer of the properties is looking for one or more specific properties. It will ignore any other properties. More particularly, if those properties are missing, it is considered a runtime error and an exception is thrown.
The error can be handled in one of two ways: simply let the generic exception generated by GeometryProperties propagate upward, or detect the missing property and throw an exception with a custom message. The example below shows both approaches.
const IllustrationProperties& properties = FunctionThatReturnsProperties();
const Rgba rgba =
properties.GetProperty<Rgba>("MyGroup", "rgba");
if (!properties.HasProperty("MyGroup", "rgba")) {
throw std::logic_error(
"ThisClass: Missing the necessary 'rgba' property; the object cannot be "
"rendered");
}
const Rgba rgba =
properties.GetProperty<Rgba>("MyGroup", "rgba");
- Note
- calls to
GetProperty()
always require the return type template value (e.g., Rgba
) to be specified in the call.
Look up specific properties with default property values
As with the previous case, the consumer is looking for one or more specific properties. However, in this case, the consumer provides a default value to use in case the target property is not defined. In this invocation, the template parameter need not be explicitly declared – the inferred return type will be the same as the default value.
const IllustrationProperties& properties = FunctionThatReturnsProperties();
const Rgba default_color{0.9, 0.9, 0.9};
const Rgba rgba =
properties.GetPropertyOrDefault("MyGroup", "rgba", default_color);
Alternatively, the default value can be provided in one of the following forms:
properties.GetPropertyOrDefault("MyGroup", "rgba",
Rgba{0.9, 0.9, 0.9});
properties.GetPropertyOrDefault<Rgba>("MyGroup", "rgba",
{0.9, 0.9, 0.9});
Iterating through provided properties
Another alternative is to iterate through the properties that have been provided. This might be done for several reasons, e.g.:
- the consumer wants to validate the set of properties, giving the user feedback if an unsupported property has been provided, and/or
- the consumer has a default value for every property and allows the registering code to define only those properties that deviate from the specified default.
Working with properties in this manner requires knowledge of how to work with AbstractValue.
const IllustrationProperties& properties = FunctionThatReturnsProperties();
for (const auto& pair : properties.GetGroupProperties("MyGroup") {
const std::string& name = pair.first;
if (name == "rgba") {
const Rgba& rgba =
pair.second->GetValueOrThrow<Rgba>();
}
}
|
virtual | ~GeometryProperties () |
|
bool | HasGroup (const std::string &group_name) const |
| Reports if the given named group is part of this property set. More...
|
|
int | num_groups () const |
| Reports the number of property groups in this set. More...
|
|
const Group & | GetPropertiesInGroup (const std::string &group_name) const |
| Retrieves the indicated property group. More...
|
|
std::set< std::string > | GetGroupNames () const |
| Returns all of the defined group names. More...
|
|
template<typename ValueType > |
void | AddProperty (const std::string &group_name, const std::string &name, const ValueType &value) |
| Adds the named property (group_name , name ) with the given value . More...
|
|
template<typename ValueType > |
void | UpdateProperty (const std::string &group_name, const std::string &name, const ValueType &value) |
| Updates the named property (group_name , name ) with the given value . More...
|
|
void | AddPropertyAbstract (const std::string &group_name, const std::string &name, const AbstractValue &value) |
| Adds the named property (group_name , name ) with the given type-erased value . More...
|
|
void | UpdatePropertyAbstract (const std::string &group_name, const std::string &name, const AbstractValue &value) |
| Updates the named property (group_name , name ) with the given type-erased value . More...
|
|
bool | HasProperty (const std::string &group_name, const std::string &name) const |
| Reports if the property (group_name , name ) exists in the group. More...
|
|
template<typename ValueType > |
decltype(auto) | GetProperty (const std::string &group_name, const std::string &name) const |
| Retrieves the typed value for the property (group_name , name ) from this set of properties. More...
|
|
const AbstractValue & | GetPropertyAbstract (const std::string &group_name, const std::string &name) const |
| Retrieves the type-erased value for the property (group_name , name ) from this set of properties. More...
|
|
template<typename ValueType > |
ValueType | GetPropertyOrDefault (const std::string &group_name, const std::string &name, ValueType default_value) const |
| Retrieves the typed value for the property (group_name , name ) from the set of properties (if it exists), otherwise returns the given default value. More...
|
|
bool | RemoveProperty (const std::string &group_name, const std::string &name) |
| Removes the (group_name , name ) property (if it exists). More...
|
|