Materials: Physical property attributes
Add dynamic attributes to report the physical attributes of a part that are automatically recalculaated when the shape or material changes. These values are accessible from the part data display and as attributes within Python
This commit is contained in:
committed by
Yorik van Havre
parent
223877873b
commit
6af113bc4f
@@ -62,10 +62,22 @@ TYPESYSTEM_SOURCE(Materials::MaterialFilter, Base::BaseClass)
|
||||
MaterialFilter::MaterialFilter()
|
||||
: _required()
|
||||
, _requiredComplete()
|
||||
, _requirePhysical(false)
|
||||
, _requireAppearance(false)
|
||||
{}
|
||||
|
||||
bool MaterialFilter::modelIncluded(const std::shared_ptr<Material>& material) const
|
||||
{
|
||||
if (_requirePhysical) {
|
||||
if (!material->hasPhysicalProperties()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_requireAppearance) {
|
||||
if (!material->hasAppearanceProperties()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto& complete : _requiredComplete) {
|
||||
if (!material->isModelComplete(complete)) {
|
||||
return false;
|
||||
|
||||
@@ -172,6 +172,14 @@ public:
|
||||
void addRequired(const QString& uuid);
|
||||
void addRequiredComplete(const QString& uuid);
|
||||
|
||||
/* Require that the materials have physical properties defined.
|
||||
*/
|
||||
void requirePhysical(bool required) { _requirePhysical = required; }
|
||||
|
||||
/* Require that the materials have appearance properties defined.
|
||||
*/
|
||||
void requireAppearance(bool required) { _requireAppearance = required; }
|
||||
|
||||
/* These functions shouldn't normally be called directly. They are
|
||||
* for use by conversion methods, such as MaterialFilterPy
|
||||
*/
|
||||
@@ -190,6 +198,8 @@ private:
|
||||
QString _name;
|
||||
QSet<QString> _required;
|
||||
QSet<QString> _requiredComplete;
|
||||
bool _requirePhysical;
|
||||
bool _requireAppearance;
|
||||
};
|
||||
|
||||
} // namespace Materials
|
||||
|
||||
@@ -1108,6 +1108,16 @@ bool Material::hasLegacyProperties() const
|
||||
return !_legacy.empty();
|
||||
}
|
||||
|
||||
bool Material::hasPhysicalProperties() const
|
||||
{
|
||||
return !_physicalUuids.isEmpty();
|
||||
}
|
||||
|
||||
bool Material::hasAppearanceProperties() const
|
||||
{
|
||||
return !_appearanceUuids.isEmpty();
|
||||
}
|
||||
|
||||
bool Material::isInherited(const QString& uuid) const
|
||||
{
|
||||
if (_physicalUuids.contains(uuid)) {
|
||||
|
||||
@@ -335,6 +335,8 @@ public:
|
||||
bool hasNonLegacyProperty(const QString& name) const;
|
||||
bool hasLegacyProperty(const QString& name) const;
|
||||
bool hasLegacyProperties() const;
|
||||
bool hasPhysicalProperties() const;
|
||||
bool hasAppearanceProperties() const;
|
||||
|
||||
// Test if the model is defined, and if values are provided for all properties
|
||||
bool hasModel(const QString& uuid) const;
|
||||
|
||||
@@ -83,13 +83,11 @@ DlgMaterialImp::DlgMaterialImp(bool floating, QWidget* parent, Qt::WindowFlags f
|
||||
|
||||
d->floating = floating;
|
||||
|
||||
// // Create a filter to only include current format materials
|
||||
// // that contain the basic render model.
|
||||
// auto filter = std::make_shared<Materials::MaterialFilter>();
|
||||
// filter->setIncludeEmptyFolders(false);
|
||||
// filter->setIncludeLegacy(false);
|
||||
// filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
|
||||
// d->ui.widgetMaterial->setFilter(filter);
|
||||
// Create a filter to only include current format materials
|
||||
// that contain physical properties.
|
||||
auto filter = std::make_shared<Materials::MaterialFilter>();
|
||||
filter->requirePhysical(true);
|
||||
d->ui.widgetMaterial->setFilter(filter);
|
||||
|
||||
std::vector<App::DocumentObject*> objects = getSelectionObjects();
|
||||
setMaterial(objects);
|
||||
|
||||
@@ -89,8 +89,34 @@ Feature::Feature()
|
||||
{
|
||||
ADD_PROPERTY(Shape, (TopoDS_Shape()));
|
||||
auto mat = Materials::MaterialManager::defaultMaterial();
|
||||
// ADD_PROPERTY_TYPE(ShapeMaterial, (mat), osgroup, App::Prop_None, "Shape material");
|
||||
ADD_PROPERTY(ShapeMaterial, (*mat));
|
||||
|
||||
// Read only properties based on the material
|
||||
static const char* group = "PhysicalProperties";
|
||||
ADD_PROPERTY_TYPE(MaterialName,
|
||||
(""),
|
||||
group,
|
||||
static_cast<App::PropertyType>(App::Prop_ReadOnly | App::Prop_Output
|
||||
| App::Prop_NoRecompute | App::Prop_NoPersist),
|
||||
"Feature material");
|
||||
ADD_PROPERTY_TYPE(Density,
|
||||
(0.0),
|
||||
group,
|
||||
static_cast<App::PropertyType>(App::Prop_ReadOnly | App::Prop_Output
|
||||
| App::Prop_NoRecompute | App::Prop_NoPersist),
|
||||
"Feature density");
|
||||
ADD_PROPERTY_TYPE(Mass,
|
||||
(0.0),
|
||||
group,
|
||||
static_cast<App::PropertyType>(App::Prop_ReadOnly | App::Prop_Output
|
||||
| App::Prop_NoRecompute | App::Prop_NoPersist),
|
||||
"Feature mass");
|
||||
ADD_PROPERTY_TYPE(Volume,
|
||||
(1.0),
|
||||
group,
|
||||
static_cast<App::PropertyType>(App::Prop_ReadOnly | App::Prop_Output
|
||||
| App::Prop_NoRecompute | App::Prop_NoPersist),
|
||||
"Feature volume");
|
||||
}
|
||||
|
||||
Feature::~Feature() = default;
|
||||
@@ -1477,11 +1503,40 @@ void Feature::onChanged(const App::Property* prop)
|
||||
}
|
||||
}
|
||||
}
|
||||
updatePhysicalProperties();
|
||||
} else if (prop == &this->ShapeMaterial) {
|
||||
updatePhysicalProperties();
|
||||
}
|
||||
|
||||
GeoFeature::onChanged(prop);
|
||||
}
|
||||
|
||||
void Feature::updatePhysicalProperties()
|
||||
{
|
||||
MaterialName.setValue(ShapeMaterial.getValue().getName().toStdString());
|
||||
if (ShapeMaterial.getValue().hasPhysicalProperty(QString::fromLatin1("Density"))) {
|
||||
Density.setValue(ShapeMaterial.getValue()
|
||||
.getPhysicalQuantity(QString::fromLatin1("Density"))
|
||||
.getValue());
|
||||
} else {
|
||||
Base::Console().Log("Density is undefined\n");
|
||||
Density.setValue(0.0);
|
||||
}
|
||||
|
||||
auto topoShape = Shape.getValue();
|
||||
if (!topoShape.IsNull()) {
|
||||
GProp_GProps props;
|
||||
BRepGProp::VolumeProperties(topoShape, props);
|
||||
Volume.setValue(props.Mass());
|
||||
Mass.setValue(Volume.getValue() * Density.getValue());
|
||||
} else {
|
||||
// No shape
|
||||
Base::Console().Log("No shape defined\n");
|
||||
Volume.setValue(0.0);
|
||||
Mass.setValue(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const std::vector<std::string>& Feature::searchElementCache(const std::string& element,
|
||||
Data::SearchOptions options,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <App/FeaturePython.h>
|
||||
#include <App/GeoFeature.h>
|
||||
#include <App/PropertyUnits.h>
|
||||
#include <Mod/Material/App/PropertyMaterial.h>
|
||||
#include <Mod/Part/PartGlobal.h>
|
||||
|
||||
@@ -60,6 +61,12 @@ public:
|
||||
PropertyPartShape Shape;
|
||||
Materials::PropertyMaterial ShapeMaterial;
|
||||
|
||||
// Convenience properties set when material or shape changes
|
||||
App::PropertyString MaterialName;
|
||||
App::PropertyDensity Density;
|
||||
App::PropertyMass Mass;
|
||||
App::PropertyVolume Volume;
|
||||
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
short mustExecute() const override;
|
||||
@@ -172,6 +179,9 @@ protected:
|
||||
void copyMaterial(Feature* feature);
|
||||
void copyMaterial(App::DocumentObject* link);
|
||||
|
||||
/// Update the mass and volume properties
|
||||
void updatePhysicalProperties();
|
||||
|
||||
void registerElementCache(const std::string &prefix, PropertyPartShape *prop);
|
||||
|
||||
/** Helper function to obtain mapped and indexed element name from a shape
|
||||
|
||||
Reference in New Issue
Block a user