Materials: External Modules Part 1

Refactored code to support local and external material sources

This is the first PR in a series to support external modules. External
modules allow materials to be stored in external data sources such as
databases or web services. No new functionality is introduced in this
PR, rather it is a refactoring of code that will allow for changes to
be introduced in future PRs. Minor performance improvements have also
been made in the model and material managers.

The Python API has been enhanced for many data types to allow for
modification within Python.
This commit is contained in:
David Carter
2025-03-07 10:13:56 -05:00
committed by Chris Hennes
parent 3c4977a2d4
commit 00c57a9d08
80 changed files with 4372 additions and 1396 deletions

View File

@@ -102,6 +102,44 @@ bool MaterialValue::operator==(const MaterialValue& other) const
return (_valueType == other._valueType) && (_value == other._value);
}
void MaterialValue::validate(const MaterialValue& other) const
{
if (_valueType != other._valueType) {
throw InvalidProperty("Material property value types don't match");
}
if (_valueType == Quantity) {
auto q1 = _value.value<Base::Quantity>();
auto q2 = other._value.value<Base::Quantity>();
if (q1.isValid()) {
if (!q2.isValid()) {
throw InvalidProperty("Invalid remote Material property quantity value");
}
if (q1.getUserString() != q2.getUserString()) {
// Direct comparisons of the quantities may have precision issues
// throw InvalidProperty("Material property quantity values don't match");
}
}
else {
if (q2.isValid()) {
throw InvalidProperty("Remote Material property quantity should not have a value");
}
}
}
else if (_valueType == Array2D) {
auto a1 = static_cast<const Materials::Array2D*>(this);
auto a2 = static_cast<const Materials::Array2D*>(&other);
a1->validate(*a2);
}
else if (_valueType == Array3D) {
auto a1 = static_cast<const Materials::Array3D*>(this);
auto a2 = static_cast<const Materials::Array3D*>(&other);
a1->validate(*a2);
}
else if (!(_value.isNull() && other._value.isNull()) && (_value != other._value)) {
throw InvalidProperty("Material property values don't match");
}
}
QString MaterialValue::escapeString(const QString& source)
{
QString res = source;
@@ -205,6 +243,11 @@ void MaterialValue::setList(const QList<QVariant>& value)
}
bool MaterialValue::isNull() const
{
return isEmpty();
}
bool MaterialValue::isEmpty() const
{
if (_value.isNull()) {
return true;
@@ -317,24 +360,24 @@ const Base::QuantityFormat MaterialValue::getQuantityFormat()
//===
TYPESYSTEM_SOURCE(Materials::Material2DArray, Materials::MaterialValue)
TYPESYSTEM_SOURCE(Materials::Array2D, Materials::MaterialValue)
Material2DArray::Material2DArray()
: MaterialValue(Array2D, Array2D)
Array2D::Array2D()
: MaterialValue(MaterialValue::Array2D, MaterialValue::Array2D)
, _columns(0)
{
// Initialize separatelt to prevent recursion
// setType(Array2D);
}
Material2DArray::Material2DArray(const Material2DArray& other)
Array2D::Array2D(const Array2D& other)
: MaterialValue(other)
, _columns(other._columns)
{
deepCopy(other);
}
Material2DArray& Material2DArray::operator=(const Material2DArray& other)
Array2D& Array2D::operator=(const Array2D& other)
{
if (this == &other) {
return *this;
@@ -348,7 +391,7 @@ Material2DArray& Material2DArray::operator=(const Material2DArray& other)
return *this;
}
void Material2DArray::deepCopy(const Material2DArray& other)
void Array2D::deepCopy(const Array2D& other)
{
// Deep copy
for (auto& row : other._rows) {
@@ -361,26 +404,55 @@ void Material2DArray::deepCopy(const Material2DArray& other)
}
}
bool Material2DArray::isNull() const
bool Array2D::isNull() const
{
return isEmpty();
}
bool Array2D::isEmpty() const
{
return rows() <= 0;
}
void Material2DArray::validateRow(int row) const
void Array2D::validateRow(int row) const
{
if (row < 0 || row >= rows()) {
throw InvalidIndex();
}
}
void Material2DArray::validateColumn(int column) const
void Array2D::validateColumn(int column) const
{
if (column < 0 || column >= columns()) {
throw InvalidIndex();
}
}
std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row) const
void Array2D::validate(const Array2D& other) const
{
if (rows() != other.rows()) {
Base::Console().Log("Local row count %d, remote %d\n", rows(), other.rows());
throw InvalidProperty("Material property value row counts don't match");
}
if (columns() != other.columns()) {
Base::Console().Log("Local column count %d, remote %d\n", columns(), other.columns());
throw InvalidProperty("Material property value column counts don't match");
}
try {
for (int i = 0; i < rows(); i++) {
for (int j = 0; j < columns(); j++) {
if (getValue(i, j) != other.getValue(i, j)) {
throw InvalidProperty("Material property values don't match");
}
}
}
}
catch (const InvalidIndex&) {
throw InvalidProperty("Material property value invalid array index");
}
}
std::shared_ptr<QList<QVariant>> Array2D::getRow(int row) const
{
validateRow(row);
@@ -392,7 +464,7 @@ std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row) const
}
}
std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row)
std::shared_ptr<QList<QVariant>> Array2D::getRow(int row)
{
validateRow(row);
@@ -404,17 +476,17 @@ std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row)
}
}
void Material2DArray::addRow(const std::shared_ptr<QList<QVariant>>& row)
void Array2D::addRow(const std::shared_ptr<QList<QVariant>>& row)
{
_rows.push_back(row);
}
void Material2DArray::insertRow(int index, const std::shared_ptr<QList<QVariant>>& row)
void Array2D::insertRow(int index, const std::shared_ptr<QList<QVariant>>& row)
{
_rows.insert(_rows.begin() + index, row);
}
void Material2DArray::deleteRow(int row)
void Array2D::deleteRow(int row)
{
if (row >= static_cast<int>(_rows.size()) || row < 0) {
throw InvalidIndex();
@@ -422,7 +494,18 @@ void Material2DArray::deleteRow(int row)
_rows.erase(_rows.begin() + row);
}
void Material2DArray::setValue(int row, int column, const QVariant& value)
void Array2D::setRows(int rowCount)
{
while (rows() < rowCount) {
auto row = std::make_shared<QList<QVariant>>();
for (int i = 0; i < columns(); i++) {
row->append(QVariant());
}
addRow(row);
}
}
void Array2D::setValue(int row, int column, const QVariant& value)
{
validateRow(row);
validateColumn(column);
@@ -436,7 +519,7 @@ void Material2DArray::setValue(int row, int column, const QVariant& value)
}
}
QVariant Material2DArray::getValue(int row, int column) const
QVariant Array2D::getValue(int row, int column) const
{
validateColumn(column);
@@ -449,7 +532,7 @@ QVariant Material2DArray::getValue(int row, int column) const
}
}
void Material2DArray::dumpRow(const std::shared_ptr<QList<QVariant>>& row)
void Array2D::dumpRow(const std::shared_ptr<QList<QVariant>>& row)
{
Base::Console().Log("row: ");
for (auto& column : *row) {
@@ -458,14 +541,14 @@ void Material2DArray::dumpRow(const std::shared_ptr<QList<QVariant>>& row)
Base::Console().Log("\n");
}
void Material2DArray::dump() const
void Array2D::dump() const
{
for (auto& row : _rows) {
dumpRow(row);
}
}
QString Material2DArray::getYAMLString() const
QString Array2D::getYAMLString() const
{
if (isNull()) {
return QString();
@@ -511,10 +594,10 @@ QString Material2DArray::getYAMLString() const
//===
TYPESYSTEM_SOURCE(Materials::Material3DArray, Materials::MaterialValue)
TYPESYSTEM_SOURCE(Materials::Array3D, Materials::MaterialValue)
Material3DArray::Material3DArray()
: MaterialValue(Array3D, Array3D)
Array3D::Array3D()
: MaterialValue(MaterialValue::Array3D, MaterialValue::Array3D)
, _currentDepth(0)
, _columns(0)
{
@@ -522,26 +605,71 @@ Material3DArray::Material3DArray()
// setType(Array3D);
}
bool Material3DArray::isNull() const
Array3D::Array3D(const Array3D& other)
: MaterialValue(other)
, _currentDepth(other._currentDepth)
, _columns(other._columns)
{
deepCopy(other);
}
Array3D& Array3D::operator=(const Array3D& other)
{
if (this == &other) {
return *this;
}
MaterialValue::operator=(other);
_columns = other._columns;
_currentDepth = other._currentDepth;
deepCopy(other);
return *this;
}
void Array3D::deepCopy(const Array3D& other)
{
// Deep copy
_rowMap.clear();
for (auto& depthTable : other._rowMap) {
auto depth = addDepth(depthTable.first);
auto rows = depthTable.second;
for (auto row : *rows) {
auto newRow = std::make_shared<QList<Base::Quantity>>();
for (auto column : *row) {
newRow->append(column);
}
addRow(depth, newRow);
}
}
}
bool Array3D::isNull() const
{
return isEmpty();
}
bool Array3D::isEmpty() const
{
return depth() <= 0;
}
void Material3DArray::validateDepth(int level) const
void Array3D::validateDepth(int level) const
{
if (level < 0 || level >= depth()) {
throw InvalidIndex();
}
}
void Material3DArray::validateColumn(int column) const
void Array3D::validateColumn(int column) const
{
if (column < 0 || column >= columns()) {
throw InvalidIndex();
}
}
void Material3DArray::validateRow(int level, int row) const
void Array3D::validateRow(int level, int row) const
{
validateDepth(level);
@@ -550,8 +678,18 @@ void Material3DArray::validateRow(int level, int row) const
}
}
void Array3D::validate(const Array3D& other) const
{
if (depth() != other.depth()) {
throw InvalidProperty("Material property value row counts don't match");
}
if (columns() != other.columns()) {
throw InvalidProperty("Material property value column counts don't match");
}
}
const std::shared_ptr<QList<std::shared_ptr<QList<Base::Quantity>>>>&
Material3DArray::getTable(const Base::Quantity& depth) const
Array3D::getTable(const Base::Quantity& depth) const
{
for (auto& it : _rowMap) {
if (std::get<0>(it) == depth) {
@@ -563,7 +701,7 @@ Material3DArray::getTable(const Base::Quantity& depth) const
}
const std::shared_ptr<QList<std::shared_ptr<QList<Base::Quantity>>>>&
Material3DArray::getTable(int depthIndex) const
Array3D::getTable(int depthIndex) const
{
try {
return std::get<1>(_rowMap.at(depthIndex));
@@ -573,7 +711,7 @@ Material3DArray::getTable(int depthIndex) const
}
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int row) const
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int depth, int row) const
{
validateRow(depth, row);
@@ -585,13 +723,13 @@ std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int ro
}
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int row) const
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int row) const
{
// Check if we can convert otherwise throw error
return getRow(_currentDepth, row);
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int row)
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int depth, int row)
{
validateRow(depth, row);
@@ -603,12 +741,12 @@ std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int ro
}
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int row)
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int row)
{
return getRow(_currentDepth, row);
}
void Material3DArray::addRow(int depth, const std::shared_ptr<QList<Base::Quantity>>& row)
void Array3D::addRow(int depth, const std::shared_ptr<QList<Base::Quantity>>& row)
{
try {
getTable(depth)->push_back(row);
@@ -618,12 +756,12 @@ void Material3DArray::addRow(int depth, const std::shared_ptr<QList<Base::Quanti
}
}
void Material3DArray::addRow(const std::shared_ptr<QList<Base::Quantity>>& row)
void Array3D::addRow(const std::shared_ptr<QList<Base::Quantity>>& row)
{
addRow(_currentDepth, row);
}
int Material3DArray::addDepth(int depth, const Base::Quantity& value)
int Array3D::addDepth(int depth, const Base::Quantity& value)
{
if (depth == this->depth()) {
// Append to the end
@@ -639,7 +777,7 @@ int Material3DArray::addDepth(int depth, const Base::Quantity& value)
return depth;
}
int Material3DArray::addDepth(const Base::Quantity& value)
int Array3D::addDepth(const Base::Quantity& value)
{
auto rowVector = std::make_shared<QList<std::shared_ptr<QList<Base::Quantity>>>>();
auto entry = std::make_pair(value, rowVector);
@@ -648,13 +786,22 @@ int Material3DArray::addDepth(const Base::Quantity& value)
return depth() - 1;
}
void Material3DArray::deleteDepth(int depth)
void Array3D::deleteDepth(int depth)
{
deleteRows(depth); // This may throw an InvalidIndex
_rowMap.erase(_rowMap.begin() + depth);
}
void Material3DArray::insertRow(int depth,
void Array3D::setDepth(int depthCount)
{
Base::Quantity dummy;
dummy.setInvalid();
while (depth() < depthCount) {
addDepth(dummy);
}
}
void Array3D::insertRow(int depth,
int row,
const std::shared_ptr<QList<Base::Quantity>>& rowData)
{
@@ -667,12 +814,12 @@ void Material3DArray::insertRow(int depth,
}
}
void Material3DArray::insertRow(int row, const std::shared_ptr<QList<Base::Quantity>>& rowData)
void Array3D::insertRow(int row, const std::shared_ptr<QList<Base::Quantity>>& rowData)
{
insertRow(_currentDepth, row, rowData);
}
void Material3DArray::deleteRow(int depth, int row)
void Array3D::deleteRow(int depth, int row)
{
auto table = getTable(depth);
if (row >= static_cast<int>(table->size()) || row < 0) {
@@ -681,23 +828,23 @@ void Material3DArray::deleteRow(int depth, int row)
table->erase(table->begin() + row);
}
void Material3DArray::deleteRow(int row)
void Array3D::deleteRow(int row)
{
deleteRow(_currentDepth, row);
}
void Material3DArray::deleteRows(int depth)
void Array3D::deleteRows(int depth)
{
auto table = getTable(depth);
table->clear();
}
void Material3DArray::deleteRows()
void Array3D::deleteRows()
{
deleteRows(_currentDepth);
}
int Material3DArray::rows(int depth) const
int Array3D::rows(int depth) const
{
if (depth < 0 || (depth == 0 && this->depth() == 0)) {
return 0;
@@ -707,7 +854,21 @@ int Material3DArray::rows(int depth) const
return getTable(depth)->size();
}
void Material3DArray::setValue(int depth, int row, int column, const Base::Quantity& value)
void Array3D::setRows(int depth, int rowCount)
{
Base::Quantity dummy;
dummy.setInvalid();
while (rows(depth) < rowCount) {
auto row = std::make_shared<QList<Base::Quantity>>();
for (int i = 0; i < columns(); i++) {
row->append(dummy);
}
addRow(depth, row);
}
}
void Array3D::setValue(int depth, int row, int column, const Base::Quantity& value)
{
validateRow(depth, row);
validateColumn(column);
@@ -721,12 +882,12 @@ void Material3DArray::setValue(int depth, int row, int column, const Base::Quant
}
}
void Material3DArray::setValue(int row, int column, const Base::Quantity& value)
void Array3D::setValue(int row, int column, const Base::Quantity& value)
{
setValue(_currentDepth, row, column, value);
}
void Material3DArray::setDepthValue(int depth, const Base::Quantity& value)
void Array3D::setDepthValue(int depth, const Base::Quantity& value)
{
try {
auto oldRows = getTable(depth);
@@ -737,13 +898,13 @@ void Material3DArray::setDepthValue(int depth, const Base::Quantity& value)
}
}
void Material3DArray::setDepthValue(const Base::Quantity& value)
void Array3D::setDepthValue(const Base::Quantity& value)
{
setDepthValue(_currentDepth, value);
}
Base::Quantity Material3DArray::getValue(int depth, int row, int column) const
Base::Quantity Array3D::getValue(int depth, int row, int column) const
{
// getRow validates depth and row. Do that first
auto val = getRow(depth, row);
@@ -757,12 +918,12 @@ Base::Quantity Material3DArray::getValue(int depth, int row, int column) const
}
}
Base::Quantity Material3DArray::getValue(int row, int column) const
Base::Quantity Array3D::getValue(int row, int column) const
{
return getValue(_currentDepth, row, column);
}
Base::Quantity Material3DArray::getDepthValue(int depth) const
Base::Quantity Array3D::getDepthValue(int depth) const
{
validateDepth(depth);
@@ -774,12 +935,12 @@ Base::Quantity Material3DArray::getDepthValue(int depth) const
}
}
int Material3DArray::currentDepth() const
int Array3D::currentDepth() const
{
return _currentDepth;
}
void Material3DArray::setCurrentDepth(int depth)
void Array3D::setCurrentDepth(int depth)
{
validateDepth(depth);
@@ -794,7 +955,7 @@ void Material3DArray::setCurrentDepth(int depth)
}
}
QString Material3DArray::getYAMLString() const
QString Array3D::getYAMLString() const
{
if (isNull()) {
return QString();