Continues the work of the material subsystem improvements. This merge covers the continued development of the material editor. The primary improvements are the addition of new data types, a new appearance preview UI, and changes in the array data types. New data types were added to support more advanced workflows, such as the Render Workbench.The Image datatype allows the material to embed the image in the card instead of pointing to an image in an external file. Multi-buyte strings span multiple lines as the name implies. It preserves formatting accross those lines. Also several list types are now supported, with the primary difference being the editors. List is a list of strings, FileList is a list of file path names, and ImageList is a list of embedded images. For the appearance preview, the UI now uses the same Coin library as is used in the documents, meaning the preview will look exactly the same as the material will be shown in the documents. The array data types are now more complete. The default value wasn't being used as originially envisioned and was tehrefore removed. For 3D arrays, the Python API was implemented. There were a lot of code clean ups. This involved removing logging statements used for debugging during development, reduction of lint warnings, and code refactoring. The editor can automatically convert from previous format files to the current format. This has been extended to material files generated by the Render WB. Old format files are displayed in the editor with a warning icon. Selecting one will require saving the file in the new format before it can be used.
839 lines
22 KiB
C++
839 lines
22 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
|
|
* *
|
|
* This file is part of FreeCAD. *
|
|
* *
|
|
* FreeCAD is free software: you can redistribute it and/or modify it *
|
|
* under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 2.1 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with FreeCAD. If not, see *
|
|
* <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
**************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
#include <QRegularExpression>
|
|
#endif
|
|
|
|
#include <QMetaType>
|
|
|
|
#include <App/Application.h>
|
|
#include <Base/QtTools.h>
|
|
#include <Base/Quantity.h>
|
|
#include <Gui/MetaTypes.h>
|
|
|
|
#include "Exceptions.h"
|
|
#include "MaterialValue.h"
|
|
|
|
|
|
using namespace Materials;
|
|
|
|
/* TRANSLATOR Material::MaterialValue */
|
|
|
|
TYPESYSTEM_SOURCE(Materials::MaterialValue, Base::BaseClass)
|
|
|
|
MaterialValue::MaterialValue()
|
|
: _valueType(None)
|
|
{
|
|
this->setInitialValue(None);
|
|
}
|
|
|
|
MaterialValue::MaterialValue(const MaterialValue& other)
|
|
: _valueType(other._valueType)
|
|
, _value(other._value)
|
|
{}
|
|
|
|
MaterialValue::MaterialValue(ValueType type)
|
|
: _valueType(type)
|
|
{
|
|
this->setInitialValue(None);
|
|
}
|
|
|
|
MaterialValue::MaterialValue(ValueType type, ValueType inherited)
|
|
: _valueType(type)
|
|
{
|
|
this->setInitialValue(inherited);
|
|
}
|
|
|
|
MaterialValue& MaterialValue::operator=(const MaterialValue& other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
|
|
_valueType = other._valueType;
|
|
_value = other._value;
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool MaterialValue::operator==(const MaterialValue& other) const
|
|
{
|
|
if (this == &other) {
|
|
return true;
|
|
}
|
|
|
|
return (_valueType == other._valueType) && (_value == other._value);
|
|
}
|
|
|
|
QString MaterialValue::escapeString(const QString& source)
|
|
{
|
|
QString res = source;
|
|
res.replace(QString::fromStdString("\\"), QString::fromStdString("\\\\"));
|
|
res.replace(QString::fromStdString("\""), QString::fromStdString("\\\""));
|
|
return res;
|
|
}
|
|
|
|
void MaterialValue::setInitialValue(ValueType inherited)
|
|
{
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
if (_valueType == String || _valueType == MultiLineString) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::QString));
|
|
}
|
|
else if (_valueType == Boolean) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::Bool));
|
|
}
|
|
else if (_valueType == Integer) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::Int));
|
|
}
|
|
else if (_valueType == Float) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::Float));
|
|
}
|
|
else if (_valueType == URL) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::QString));
|
|
}
|
|
else if (_valueType == Color) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::QString));
|
|
}
|
|
else if (_valueType == File) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::QString));
|
|
}
|
|
else if (_valueType == Image) {
|
|
_value = QVariant(static_cast<QVariant::Type>(QMetaType::QString));
|
|
}
|
|
#else
|
|
if (_valueType == String || _valueType == MultiLineString) {
|
|
_value = QVariant(QMetaType(QMetaType::QString));
|
|
}
|
|
else if (_valueType == Boolean) {
|
|
_value = QVariant(QMetaType(QMetaType::Bool));
|
|
}
|
|
else if (_valueType == Integer) {
|
|
_value = QVariant(QMetaType(QMetaType::Int));
|
|
}
|
|
else if (_valueType == Float) {
|
|
_value = QVariant(QMetaType(QMetaType::Float));
|
|
}
|
|
else if (_valueType == URL) {
|
|
_value = QVariant(QMetaType(QMetaType::QString));
|
|
}
|
|
else if (_valueType == Color) {
|
|
_value = QVariant(QMetaType(QMetaType::QString));
|
|
}
|
|
else if (_valueType == File) {
|
|
_value = QVariant(QMetaType(QMetaType::QString));
|
|
}
|
|
else if (_valueType == Image) {
|
|
_value = QVariant(QMetaType(QMetaType::QString));
|
|
}
|
|
#endif
|
|
else if (_valueType == Quantity) {
|
|
Base::Quantity qu;
|
|
qu.setInvalid();
|
|
_value = QVariant::fromValue(qu);
|
|
}
|
|
else if (_valueType == List || _valueType == FileList || _valueType == ImageList) {
|
|
auto list = QList<QVariant>();
|
|
_value = QVariant::fromValue(list);
|
|
}
|
|
else if (_valueType == Array2D) {
|
|
if (_valueType != inherited) {
|
|
throw InvalidMaterialType("Initializing a regular material value as a 2D Array");
|
|
}
|
|
|
|
_value = QVariant(); // Uninitialized default value
|
|
}
|
|
else if (_valueType == Array3D) {
|
|
if (_valueType != inherited) {
|
|
throw InvalidMaterialType("Initializing a regular material value as a 3D Array");
|
|
}
|
|
|
|
_value = QVariant(); // Uninitialized default value
|
|
}
|
|
else {
|
|
// Default is to set the type to None and leave the variant uninitialized
|
|
_valueType = None;
|
|
_value = QVariant();
|
|
}
|
|
}
|
|
|
|
void MaterialValue::setList(const QList<QVariant>& value)
|
|
{
|
|
_value = QVariant::fromValue(value);
|
|
}
|
|
|
|
bool MaterialValue::isNull() const
|
|
{
|
|
if (_value.isNull()) {
|
|
return true;
|
|
}
|
|
|
|
if (_valueType == Quantity) {
|
|
return !_value.value<Base::Quantity>().isValid();
|
|
}
|
|
|
|
if (_valueType == List || _valueType == FileList || _valueType == ImageList) {
|
|
return _value.value<QList<QVariant>>().isEmpty();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QString MaterialValue::getYAMLStringImage() const
|
|
{
|
|
QString yaml;
|
|
yaml = QString::fromStdString(" |-2");
|
|
QString base64 = getValue().toString();
|
|
while (!base64.isEmpty()) {
|
|
yaml += QString::fromStdString("\n ") + base64.left(74);
|
|
base64.remove(0, 74);
|
|
}
|
|
return yaml;
|
|
}
|
|
|
|
QString MaterialValue::getYAMLStringList() const
|
|
{
|
|
QString yaml;
|
|
for (auto& it : getList()) {
|
|
yaml += QString::fromStdString("\n - \"") + escapeString(it.toString())
|
|
+ QString::fromStdString("\"");
|
|
}
|
|
return yaml;
|
|
}
|
|
|
|
QString MaterialValue::getYAMLStringImageList() const
|
|
{
|
|
QString yaml;
|
|
for (auto& it : getList()) {
|
|
yaml += QString::fromStdString("\n - |-2");
|
|
QString base64 = it.toString();
|
|
while (!base64.isEmpty()) {
|
|
yaml += QString::fromStdString("\n ") + base64.left(72);
|
|
base64.remove(0, 72);
|
|
}
|
|
}
|
|
return yaml;
|
|
}
|
|
|
|
QString MaterialValue::getYAMLStringMultiLine() const
|
|
{
|
|
QString yaml;
|
|
yaml = QString::fromStdString(" >2");
|
|
auto list =
|
|
getValue().toString().split(QRegExp(QString::fromStdString("[\r\n]")), Qt::SkipEmptyParts);
|
|
for (auto& it : list) {
|
|
yaml += QString::fromStdString("\n ") + it;
|
|
}
|
|
return yaml;
|
|
}
|
|
|
|
QString MaterialValue::getYAMLString() const
|
|
{
|
|
QString yaml;
|
|
if (!isNull()) {
|
|
if (getType() == MaterialValue::Image) {
|
|
return getYAMLStringImage();
|
|
}
|
|
if (getType() == MaterialValue::List || getType() == MaterialValue::FileList) {
|
|
return getYAMLStringList();
|
|
}
|
|
if (getType() == MaterialValue::ImageList) {
|
|
return getYAMLStringImageList();
|
|
}
|
|
if (getType() == MaterialValue::MultiLineString) {
|
|
return getYAMLStringMultiLine();
|
|
}
|
|
if (getType() == MaterialValue::Quantity) {
|
|
auto quantity = getValue().value<Base::Quantity>();
|
|
yaml += quantity.getUserString();
|
|
}
|
|
else if (getType() == MaterialValue::Float) {
|
|
auto value = getValue();
|
|
if (!value.isNull()) {
|
|
yaml += QString::fromLatin1("%1").arg(value.toFloat(), 0, 'g', 6);
|
|
}
|
|
}
|
|
else if (getType() == MaterialValue::MultiLineString) {
|
|
yaml = QString::fromLatin1(">2");
|
|
auto list =
|
|
getValue().toString().split(QRegularExpression(QString::fromLatin1("[\r\n]")),
|
|
Qt::SkipEmptyParts);
|
|
for (auto& it : list) {
|
|
yaml += QString::fromLatin1("\n ") + it;
|
|
}
|
|
return yaml;
|
|
}
|
|
else if (getType() == MaterialValue::List) {
|
|
for (auto& it : getList()) {
|
|
yaml += QString::fromLatin1("\n - \"") + escapeString(it.toString())
|
|
+ QString::fromLatin1("\"");
|
|
}
|
|
return yaml;
|
|
}
|
|
else {
|
|
yaml += getValue().toString();
|
|
}
|
|
}
|
|
yaml = QString::fromLatin1("\"") + escapeString(yaml) + QString::fromLatin1("\"");
|
|
return yaml;
|
|
}
|
|
|
|
//===
|
|
|
|
TYPESYSTEM_SOURCE(Materials::Material2DArray, Materials::MaterialValue)
|
|
|
|
Material2DArray::Material2DArray()
|
|
: MaterialValue(Array2D, Array2D)
|
|
, _columns(0)
|
|
{
|
|
// Initialize separatelt to prevent recursion
|
|
// setType(Array2D);
|
|
}
|
|
|
|
Material2DArray::Material2DArray(const Material2DArray& other)
|
|
: MaterialValue(other)
|
|
, _columns(other._columns)
|
|
{
|
|
deepCopy(other);
|
|
}
|
|
|
|
Material2DArray& Material2DArray::operator=(const Material2DArray& other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
|
|
MaterialValue::operator=(other);
|
|
_columns = other._columns;
|
|
|
|
deepCopy(other);
|
|
|
|
return *this;
|
|
}
|
|
|
|
void Material2DArray::deepCopy(const Material2DArray& other)
|
|
{
|
|
// Deep copy
|
|
for (auto& row : other._rows) {
|
|
QList<QVariant> vv;
|
|
for (auto& col : *row) {
|
|
QVariant newVariant(col);
|
|
vv.push_back(newVariant);
|
|
}
|
|
addRow(std::make_shared<QList<QVariant>>(vv));
|
|
}
|
|
}
|
|
|
|
bool Material2DArray::isNull() const
|
|
{
|
|
return rows() <= 0;
|
|
}
|
|
|
|
void Material2DArray::validateRow(int row) const
|
|
{
|
|
if (row < 0 || row >= rows()) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material2DArray::validateColumn(int column) const
|
|
{
|
|
if (column < 0 || column >= columns()) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row) const
|
|
{
|
|
validateRow(row);
|
|
|
|
try {
|
|
return _rows.at(row);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row)
|
|
{
|
|
validateRow(row);
|
|
|
|
try {
|
|
return _rows.at(row);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material2DArray::addRow(const std::shared_ptr<QList<QVariant>>& row)
|
|
{
|
|
_rows.push_back(row);
|
|
}
|
|
|
|
void Material2DArray::insertRow(int index, const std::shared_ptr<QList<QVariant>>& row)
|
|
{
|
|
_rows.insert(_rows.begin() + index, row);
|
|
}
|
|
|
|
void Material2DArray::deleteRow(int row)
|
|
{
|
|
if (row >= static_cast<int>(_rows.size()) || row < 0) {
|
|
throw InvalidIndex();
|
|
}
|
|
_rows.erase(_rows.begin() + row);
|
|
}
|
|
|
|
void Material2DArray::setValue(int row, int column, const QVariant& value)
|
|
{
|
|
validateRow(row);
|
|
validateColumn(column);
|
|
|
|
auto val = getRow(row);
|
|
try {
|
|
val->replace(column, value);
|
|
}
|
|
catch (const std::out_of_range&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
QVariant Material2DArray::getValue(int row, int column) const
|
|
{
|
|
validateColumn(column);
|
|
|
|
auto val = getRow(row);
|
|
try {
|
|
return val->at(column);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material2DArray::dumpRow(const std::shared_ptr<QList<QVariant>>& row)
|
|
{
|
|
Base::Console().Log("row: ");
|
|
for (auto& column : *row) {
|
|
Base::Console().Log("'%s' ", column.toString().toStdString().c_str());
|
|
}
|
|
Base::Console().Log("\n");
|
|
}
|
|
|
|
void Material2DArray::dump() const
|
|
{
|
|
for (auto& row : _rows) {
|
|
dumpRow(row);
|
|
}
|
|
}
|
|
|
|
QString Material2DArray::getYAMLString() const
|
|
{
|
|
if (isNull()) {
|
|
return QString();
|
|
}
|
|
|
|
// Set the correct indentation. 9 chars in this case
|
|
QString pad;
|
|
pad.fill(QChar::fromLatin1(' '), 9);
|
|
|
|
// Save the array contents
|
|
QString yaml = QString::fromStdString("\n - [");
|
|
bool firstRow = true;
|
|
for (auto& row : _rows) {
|
|
if (!firstRow) {
|
|
// Each row is on its own line, padded for correct indentation
|
|
yaml += QString::fromStdString(",\n") + pad;
|
|
}
|
|
else {
|
|
firstRow = false;
|
|
}
|
|
yaml += QString::fromStdString("[");
|
|
|
|
bool first = true;
|
|
for (auto& column : *row) {
|
|
if (!first) {
|
|
// TODO: Fix for arrays with too many columns to fit on a single line
|
|
yaml += QString::fromStdString(", ");
|
|
}
|
|
else {
|
|
first = false;
|
|
}
|
|
yaml += QString::fromStdString("\"");
|
|
auto quantity = column.value<Base::Quantity>();
|
|
yaml += quantity.getUserString();
|
|
yaml += QString::fromStdString("\"");
|
|
}
|
|
|
|
yaml += QString::fromStdString("]");
|
|
}
|
|
yaml += QString::fromStdString("]");
|
|
return yaml;
|
|
}
|
|
|
|
//===
|
|
|
|
TYPESYSTEM_SOURCE(Materials::Material3DArray, Materials::MaterialValue)
|
|
|
|
Material3DArray::Material3DArray()
|
|
: MaterialValue(Array3D, Array3D)
|
|
, _currentDepth(0)
|
|
, _columns(0)
|
|
{
|
|
// Initialize separatelt to prevent recursion
|
|
// setType(Array3D);
|
|
}
|
|
|
|
bool Material3DArray::isNull() const
|
|
{
|
|
return depth() <= 0;
|
|
}
|
|
|
|
void Material3DArray::validateDepth(int level) const
|
|
{
|
|
if (level < 0 || level >= depth()) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material3DArray::validateColumn(int column) const
|
|
{
|
|
if (column < 0 || column >= columns()) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material3DArray::validateRow(int level, int row) const
|
|
{
|
|
validateDepth(level);
|
|
|
|
if (row < 0 || row >= rows(level)) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
const std::shared_ptr<QList<std::shared_ptr<QList<Base::Quantity>>>>&
|
|
Material3DArray::getTable(const Base::Quantity& depth) const
|
|
{
|
|
for (auto& it : _rowMap) {
|
|
if (std::get<0>(it) == depth) {
|
|
return std::get<1>(it);
|
|
}
|
|
}
|
|
|
|
throw InvalidIndex();
|
|
}
|
|
|
|
const std::shared_ptr<QList<std::shared_ptr<QList<Base::Quantity>>>>&
|
|
Material3DArray::getTable(int depthIndex) const
|
|
{
|
|
try {
|
|
return std::get<1>(_rowMap.at(depthIndex));
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int row) const
|
|
{
|
|
validateRow(depth, row);
|
|
|
|
try {
|
|
return getTable(depth)->at(row);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<QList<Base::Quantity>> Material3DArray::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)
|
|
{
|
|
validateRow(depth, row);
|
|
|
|
try {
|
|
return getTable(depth)->at(row);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int row)
|
|
{
|
|
return getRow(_currentDepth, row);
|
|
}
|
|
|
|
void Material3DArray::addRow(int depth, const std::shared_ptr<QList<Base::Quantity>>& row)
|
|
{
|
|
try {
|
|
getTable(depth)->push_back(row);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material3DArray::addRow(const std::shared_ptr<QList<Base::Quantity>>& row)
|
|
{
|
|
addRow(_currentDepth, row);
|
|
}
|
|
|
|
int Material3DArray::addDepth(int depth, const Base::Quantity& value)
|
|
{
|
|
if (depth == this->depth()) {
|
|
// Append to the end
|
|
return addDepth(value);
|
|
}
|
|
if (depth > this->depth()) {
|
|
throw InvalidIndex();
|
|
}
|
|
auto rowVector = std::make_shared<QList<std::shared_ptr<QList<Base::Quantity>>>>();
|
|
auto entry = std::make_pair(value, rowVector);
|
|
_rowMap.insert(_rowMap.begin() + depth, entry);
|
|
|
|
return depth;
|
|
}
|
|
|
|
int Material3DArray::addDepth(const Base::Quantity& value)
|
|
{
|
|
auto rowVector = std::make_shared<QList<std::shared_ptr<QList<Base::Quantity>>>>();
|
|
auto entry = std::make_pair(value, rowVector);
|
|
_rowMap.push_back(entry);
|
|
|
|
return depth() - 1;
|
|
}
|
|
|
|
void Material3DArray::deleteDepth(int depth)
|
|
{
|
|
deleteRows(depth); // This may throw an InvalidIndex
|
|
_rowMap.erase(_rowMap.begin() + depth);
|
|
}
|
|
|
|
void Material3DArray::insertRow(int depth,
|
|
int row,
|
|
const std::shared_ptr<QList<Base::Quantity>>& rowData)
|
|
{
|
|
try {
|
|
auto table = getTable(depth);
|
|
table->insert(table->begin() + row, rowData);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material3DArray::insertRow(int row, const std::shared_ptr<QList<Base::Quantity>>& rowData)
|
|
{
|
|
insertRow(_currentDepth, row, rowData);
|
|
}
|
|
|
|
void Material3DArray::deleteRow(int depth, int row)
|
|
{
|
|
auto table = getTable(depth);
|
|
if (row >= static_cast<int>(table->size()) || row < 0) {
|
|
throw InvalidIndex();
|
|
}
|
|
table->erase(table->begin() + row);
|
|
}
|
|
|
|
void Material3DArray::deleteRow(int row)
|
|
{
|
|
deleteRow(_currentDepth, row);
|
|
}
|
|
|
|
void Material3DArray::deleteRows(int depth)
|
|
{
|
|
auto table = getTable(depth);
|
|
table->clear();
|
|
}
|
|
|
|
void Material3DArray::deleteRows()
|
|
{
|
|
deleteRows(_currentDepth);
|
|
}
|
|
|
|
int Material3DArray::rows(int depth) const
|
|
{
|
|
if (depth < 0 || (depth == 0 && this->depth() == 0)) {
|
|
return 0;
|
|
}
|
|
validateDepth(depth);
|
|
|
|
return getTable(depth)->size();
|
|
}
|
|
|
|
void Material3DArray::setValue(int depth, int row, int column, const Base::Quantity& value)
|
|
{
|
|
validateRow(depth, row);
|
|
validateColumn(column);
|
|
|
|
auto val = getRow(depth, row);
|
|
try {
|
|
val->replace(column, value);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material3DArray::setValue(int row, int column, const Base::Quantity& value)
|
|
{
|
|
setValue(_currentDepth, row, column, value);
|
|
}
|
|
|
|
void Material3DArray::setDepthValue(int depth, const Base::Quantity& value)
|
|
{
|
|
try {
|
|
auto oldRows = getTable(depth);
|
|
_rowMap.replace(depth, std::pair(value, oldRows));
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
void Material3DArray::setDepthValue(const Base::Quantity& value)
|
|
{
|
|
setDepthValue(_currentDepth, value);
|
|
}
|
|
|
|
|
|
Base::Quantity Material3DArray::getValue(int depth, int row, int column) const
|
|
{
|
|
// getRow validates depth and row. Do that first
|
|
auto val = getRow(depth, row);
|
|
validateColumn(column);
|
|
|
|
try {
|
|
return val->at(column);
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
Base::Quantity Material3DArray::getValue(int row, int column) const
|
|
{
|
|
return getValue(_currentDepth, row, column);
|
|
}
|
|
|
|
Base::Quantity Material3DArray::getDepthValue(int depth) const
|
|
{
|
|
validateDepth(depth);
|
|
|
|
try {
|
|
return std::get<0>(_rowMap.at(depth));
|
|
}
|
|
catch (std::out_of_range const&) {
|
|
throw InvalidIndex();
|
|
}
|
|
}
|
|
|
|
int Material3DArray::currentDepth() const
|
|
{
|
|
return _currentDepth;
|
|
}
|
|
|
|
void Material3DArray::setCurrentDepth(int depth)
|
|
{
|
|
validateDepth(depth);
|
|
|
|
if (depth < 0 || _rowMap.empty()) {
|
|
_currentDepth = 0;
|
|
}
|
|
else if (depth >= static_cast<int>(_rowMap.size())) {
|
|
_currentDepth = _rowMap.size() - 1;
|
|
}
|
|
else {
|
|
_currentDepth = depth;
|
|
}
|
|
}
|
|
|
|
QString Material3DArray::getYAMLString() const
|
|
{
|
|
if (isNull()) {
|
|
return QString();
|
|
}
|
|
|
|
// Set the correct indentation. 7 chars + name length
|
|
QString pad;
|
|
pad.fill(QChar::fromLatin1(' '), 9);
|
|
|
|
// Save the array contents
|
|
QString yaml = QString::fromStdString("\n - [");
|
|
for (int depth = 0; depth < this->depth(); depth++) {
|
|
if (depth > 0) {
|
|
// Each row is on its own line, padded for correct indentation
|
|
yaml += QString::fromStdString(",\n") + pad;
|
|
}
|
|
|
|
yaml += QString::fromStdString("\"");
|
|
auto value = getDepthValue(depth).getUserString();
|
|
yaml += value;
|
|
yaml += QString::fromStdString("\": [");
|
|
|
|
QString pad2;
|
|
pad2.fill(QChar::fromLatin1(' '), 14 + value.length());
|
|
|
|
bool firstRow = true;
|
|
auto rows = getTable(depth);
|
|
for (auto& row : *rows) {
|
|
if (!firstRow) {
|
|
// Each row is on its own line, padded for correct indentation
|
|
yaml += QString::fromStdString(",\n") + pad2;
|
|
}
|
|
else {
|
|
firstRow = false;
|
|
}
|
|
yaml += QString::fromStdString("[");
|
|
|
|
bool first = true;
|
|
for (auto& column : *row) {
|
|
if (!first) {
|
|
// TODO: Fix for arrays with too many columns to fit on a single line
|
|
yaml += QString::fromStdString(", ");
|
|
}
|
|
else {
|
|
first = false;
|
|
}
|
|
yaml += QString::fromStdString("\"");
|
|
// Base::Quantity quantity = column.value<Base::Quantity>();
|
|
yaml += column.getUserString();
|
|
yaml += QString::fromStdString("\"");
|
|
}
|
|
|
|
yaml += QString::fromStdString("]");
|
|
}
|
|
yaml += QString::fromStdString("]");
|
|
}
|
|
yaml += QString::fromStdString("]");
|
|
return yaml;
|
|
}
|