diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj index a7798fa..c3eee0b 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj @@ -110,6 +110,7 @@ + @@ -222,6 +223,7 @@ + diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters index dcbccc4..cf15c33 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters @@ -288,6 +288,9 @@ Source Files + + Source Files + @@ -359,6 +362,9 @@ Header Files + + Header Files + diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp index d612699..00c3691 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidget.cpp @@ -1,6 +1,7 @@ #include "EditorWidget.h" #include "EditorWidgetItem.h" #include "Properties/ProjectPropertyDialog.h" +#include "Properties/MaterialReplaceDialog.h" #include #include #include @@ -22,11 +23,11 @@ EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) initProjectMenu(); connect(this->tabWidget, &QTabWidget::tabCloseRequested, [this](int index) { - const int prevCount = this->tabWidget->count(); - this->tabWidget->removeTab(index); - const int nowCount = this->tabWidget->count(); + const int prevCount = this->tabWidget->count(); + this->tabWidget->removeTab(index); + const int nowCount = this->tabWidget->count(); - emit tabCountChanged(prevCount, nowCount); + emit tabCountChanged(prevCount, nowCount); }); } @@ -149,8 +150,10 @@ void EditorWidget::initProjectMenu() projectMenuButton->setDisabled(true); } - auto* actionProjectSettings = new QAction(QStringLiteral("项目设置"), projectMenuButton); + auto actionProjectSettings = new QAction(QStringLiteral("项目设置"), projectMenuButton); + auto actionMaterialReplace = new QAction(QStringLiteral("材质替换"), projectMenuButton); projectMenuButton->addMenuAction(actionProjectSettings); + projectMenuButton->addMenuAction(actionMaterialReplace); connect(actionProjectSettings, &QAction::triggered, [this] { @@ -160,9 +163,9 @@ void EditorWidget::initProjectMenu() connect(dialog, &ProjectPropertyDialog::projectNameChanged, currentEditorWidgetItem, &EditorWidgetItem::handleProjectNameChange); - connect(dialog, &ProjectPropertyDialog::backgroundColorChanged, + connect(dialog, &ProjectPropertyDialog::backgroundColorChanged, currentEditorWidgetItem, &EditorWidgetItem::handleBackgroundColorChange); - connect(dialog, &ProjectPropertyDialog::canvasSizeChanged, + connect(dialog, &ProjectPropertyDialog::canvasSizeChanged, currentEditorWidgetItem, &EditorWidgetItem::handleCanvasSizeChange); connect(dialog, &ProjectPropertyDialog::canvasRoughnessChanged, currentEditorWidgetItem, &EditorWidgetItem::handleCanvasRoughnessChange); @@ -170,4 +173,10 @@ void EditorWidget::initProjectMenu() currentEditorWidgetItem, &EditorWidgetItem::handleCanvasMetallicChange); dialog->exec(); }); + connect(actionMaterialReplace, &QAction::triggered, [this] + { + auto* currentEditorWidgetItem = static_cast(this->tabWidget->currentWidget()); + const auto dialog = new MaterialReplaceDialog(currentEditorWidgetItem, this); + dialog->exec(); + }); } diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp index a50f89e..4e7220f 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.cpp @@ -274,6 +274,11 @@ void EditorWidgetItem::handleCanvasMetallicChange(const float& metallic) previewWindow->canvasMetallic = metallic; } +void EditorWidgetItem::handleMaterialReplace(const Renderer::Material& origin, const Renderer::Material& target) +{ + +} + QSize EditorWidgetItem::getCanvasReferSize() const { return previewWindow->referSize; diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h index b95c0df..7a7c858 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetItem.h @@ -50,6 +50,7 @@ class EditorWidgetItem : public QWidget void handleCanvasSizeChange(const QSize& size); void handleCanvasRoughnessChange(const float& roughness); void handleCanvasMetallicChange(const float& metallic); + void handleMaterialReplace(const Renderer::Material& origin, const Renderer::Material& target); QSize getCanvasReferSize() const; float getCanvasRoughness() const; float getCanvasMetallic() const; diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp index 6492544..8db6d14 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp @@ -16,421 +16,441 @@ std::vector StrokeElementLayerStyle::toBaseStyles() const { - std::vector baseStyles; - if (enableEachSideIndependent) - { - if (!radialStroke(strokePair.first)->materialMap.empty()) - { - baseStyles.push_back({ std::make_shared(), strokePair.first }); - } - if (!radialStroke(strokePair.second)->materialMap.empty()) - { - baseStyles.push_back({ std::make_shared(), strokePair.second }); - } - } - else if (!radialStroke(strokePair.first)->materialMap.empty()) - { - const auto material = std::shared_ptr(std::move(strokePair.first->clone())); - std::static_pointer_cast(material)->strokeType = Renderer::StrokeType::kBothSides; + std::vector baseStyles; + if (enableEachSideIndependent) + { + if (!radialStroke(strokePair.first)->materialMap.empty()) + { + baseStyles.push_back({ std::make_shared(), strokePair.first }); + } + if (!radialStroke(strokePair.second)->materialMap.empty()) + { + baseStyles.push_back({ std::make_shared(), strokePair.second }); + } + } + else if (!radialStroke(strokePair.first)->materialMap.empty()) + { + const auto material = std::shared_ptr(std::move(strokePair.first->clone())); + std::static_pointer_cast(material)->strokeType = Renderer::StrokeType::kBothSides; - baseStyles.push_back({ std::make_shared(), material }); - } - return baseStyles; + baseStyles.push_back({ std::make_shared(), material }); + } + return baseStyles; } QWidget* StrokeElementLayerStyle::getInputWidget() { - auto* w = new QWidget; + auto* w = new QWidget; - auto* layout = new QVBoxLayout(w); - layout->setMargin(0); + auto* layout = new QVBoxLayout(w); + layout->setMargin(0); - auto* leftStrokeView = new StrokeStyleWidget(this->strokePair.first, w); - layout->addWidget(leftStrokeView); - - auto* checkEachSideIndependent = new QtMaterialCheckBox(w); - checkEachSideIndependent->setText(QStringLiteral("右侧独立描边")); - checkEachSideIndependent->setChecked(enableEachSideIndependent); - layout->addWidget(checkEachSideIndependent); + auto* leftStrokeView = new StrokeStyleWidget(this->strokePair.first, w); + layout->addWidget(leftStrokeView); - auto* rightStrokeView = new StrokeStyleWidget(this->strokePair.second, w); - layout->addWidget(rightStrokeView); - rightStrokeView->setDisabled(!this->enableEachSideIndependent); + auto* checkEachSideIndependent = new QtMaterialCheckBox(w); + checkEachSideIndependent->setText(QStringLiteral("右侧独立描边")); + checkEachSideIndependent->setChecked(enableEachSideIndependent); + layout->addWidget(checkEachSideIndependent); - QObject::connect(checkEachSideIndependent, &QtMaterialCheckBox::toggled, [this, rightStrokeView](bool toggled) { - this->enableEachSideIndependent = toggled; - rightStrokeView->setDisabled(!toggled); - }); - return w; + auto* rightStrokeView = new StrokeStyleWidget(this->strokePair.second, w); + layout->addWidget(rightStrokeView); + rightStrokeView->setDisabled(!this->enableEachSideIndependent); + + QObject::connect(checkEachSideIndependent, &QtMaterialCheckBox::toggled, [this, rightStrokeView](bool toggled) { + this->enableEachSideIndependent = toggled; + rightStrokeView->setDisabled(!toggled); + }); + return w; } QWidget* StrokeElementLayerStyle::getListDisplayWidget() const { - auto* w = new QWidget; - auto* name = new QLabel(w); - name->setText(QStringLiteral("描边")); - auto* layout = new QHBoxLayout(w); - layout->setMargin(0); - layout->addWidget(name); - return w; + auto* w = new QWidget; + auto* name = new QLabel(w); + name->setText(QStringLiteral("描边")); + auto* layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->addWidget(name); + return w; } void LayerStyleContainer::computeNewHash() { - hash = 0; - for (auto& f : styles - | std::views::values - | std::views::transform(&LayerStyle::toBaseStyles) - | std::views::join - | std::views::transform(&Renderer::BaseStyle::material) - | std::views::transform(&MaterialStyle::encoded) - | std::views::join) - { - const unsigned int u = *reinterpret_cast(&f); - hash ^= u + 0x9e3779b9 + (hash << 6) + (hash >> 2); - } + hash = 0; + for (auto& f : styles + | std::views::values + | std::views::transform(&LayerStyle::toBaseStyles) + | std::views::join + | std::views::transform(&Renderer::BaseStyle::material) + | std::views::transform(&MaterialStyle::encoded) + | std::views::join) + { + const unsigned int u = *reinterpret_cast(&f); + hash ^= u + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } } LayerStyleContainer LayerStyleContainer::fromJson(ElementType::ElementType elementType, const QJsonArray& jsonArray) { - LayerStyleContainer container(elementType); - for (const auto& style : jsonArray) - { - container.useStyle(LayerStyle::fromJson(style.toObject())); - } - return container; + LayerStyleContainer container(elementType); + for (const auto& style : jsonArray) + { + container.useStyle(LayerStyle::fromJson(style.toObject())); + } + return container; } LayerStyleContainer::LayerStyleContainer(ElementType::ElementType elementType) : elementType(elementType), hash(0) { - for (const auto& style : commonStyles) - { - unusedStyles.insert(style); - } - if (elementType & ElementType::TYPE_UNCLOSED) - { - for (const auto& style : unclosedOnlyStyles) - { - unusedStyles.insert(style); - } - } - if (elementType & ElementType::TYPE_CLOSED) - { - for (const auto& style : closedOnlyStyles) - { - unusedStyles.insert(style); - } - } + for (const auto& style : commonStyles) + { + unusedStyles.insert(style); + } + if (elementType & ElementType::TYPE_UNCLOSED) + { + for (const auto& style : unclosedOnlyStyles) + { + unusedStyles.insert(style); + } + } + if (elementType & ElementType::TYPE_CLOSED) + { + for (const auto& style : closedOnlyStyles) + { + unusedStyles.insert(style); + } + } } std::vector LayerStyleContainer::toBaseStyles() const { - std::vector result; - for (const auto& style : styles | std::views::values) - { - auto baseStyles = style->toBaseStyles(); - result.insert(result.end(), - std::make_move_iterator(baseStyles.begin()), - std::make_move_iterator(baseStyles.end())); - } - return result; + std::vector result; + for (const auto& style : styles | std::views::values) + { + auto baseStyles = style->toBaseStyles(); + result.insert(result.end(), + std::make_move_iterator(baseStyles.begin()), + std::make_move_iterator(baseStyles.end())); + } + return result; } QJsonArray LayerStyleContainer::toJson() const { - QJsonArray json; - for (const auto& style : styles | std::views::values) - { - json.append(style->toJson()); - } - return json; + QJsonArray json; + for (const auto& style : styles | std::views::values) + { + json.append(style->toJson()); + } + return json; +} + +void LayerStyleContainer::replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) +{ + for (auto& style : styles | std::views::values) + style->replaceMaterial(origin, target); + computeNewHash(); } QStringList LayerStyleContainer::unusedStyleNames() const { - QStringList result; - for(const auto& name : unusedStyles | std::views::keys) - { - result << name; - } - return result; + QStringList result; + for(const auto& name : unusedStyles | std::views::keys) + { + result << name; + } + return result; } std::unique_ptr LayerStyleContainer::makeUnusedStyle(const QString& styleName) const { - return unusedStyles.at(styleName)(); + return unusedStyles.at(styleName)(); } bool LayerStyleContainer::empty() const { - return styles.empty(); + return styles.empty(); } bool LayerStyleContainer::full() const { - return unusedStyles.empty(); + return unusedStyles.empty(); } std::map>::iterator LayerStyleContainer::begin() { - return styles.begin(); + return styles.begin(); } std::map>::iterator LayerStyleContainer::end() { - return styles.end(); + return styles.end(); } std::map>::const_iterator LayerStyleContainer::cbegin() const { - return styles.cbegin(); + return styles.cbegin(); } std::map>::const_iterator LayerStyleContainer::cend() const { - return styles.cend(); + return styles.cend(); } bool LayerStyleContainer::useStyle(const std::shared_ptr& style, bool forceOverride) { - const auto styleDisplayName = style->getDisplayName(); - auto styleNode = unusedStyles.extract(styleDisplayName); - if (styleNode.empty() && !forceOverride) - { - return false; - } - auto uFittedStyle = style->fitForElementType(this->elementType); - auto fittedStyle = std::shared_ptr(std::move(uFittedStyle)); - if (!fittedStyle) - { - return false; - } - if (!styleNode.empty()) - { - usedStyles.insert(std::move(styleNode)); - } - styles[styleDisplayName] = fittedStyle; - return true; + const auto styleDisplayName = style->getDisplayName(); + auto styleNode = unusedStyles.extract(styleDisplayName); + if (styleNode.empty() && !forceOverride) + { + return false; + } + auto uFittedStyle = style->fitForElementType(this->elementType); + auto fittedStyle = std::shared_ptr(std::move(uFittedStyle)); + if (!fittedStyle) + { + return false; + } + if (!styleNode.empty()) + { + usedStyles.insert(std::move(styleNode)); + } + styles[styleDisplayName] = fittedStyle; + return true; } bool LayerStyleContainer::overrideStyle(const std::shared_ptr& style) { - auto fittedStyle = std::shared_ptr(std::move(style->fitForElementType(this->elementType))); - if (!fittedStyle) - { - return false; - } - styles[style->getDisplayName()] = fittedStyle; - return true; + auto fittedStyle = std::shared_ptr(std::move(style->fitForElementType(this->elementType))); + if (!fittedStyle) + { + return false; + } + styles[style->getDisplayName()] = fittedStyle; + return true; } bool LayerStyleContainer::dropStyle(const QString& styleName) { - auto styleNode = usedStyles.extract(styleName); + auto styleNode = usedStyles.extract(styleName); if (styleNode.empty()) - { - return false; - } - styles.erase(styleName); - unusedStyles.insert(std::move(styleNode)); + { + return false; + } + styles.erase(styleName); + unusedStyles.insert(std::move(styleNode)); return true; } std::shared_ptr LayerStyleContainer::getStyle(const QString& styleName) const { - return styles.at(styleName); + return styles.at(styleName); } float LayerStyleContainer::boundingBoxAffectValue() const { - float maxLineWidth = 0; - const auto strokeStyle = styles.find(StrokeElementLayerStyle::displayName()); - if (strokeStyle != styles.end()) - { - if (const auto strokeElementLayerStyle = - std::dynamic_pointer_cast(strokeStyle->second); - strokeElementLayerStyle != nullptr) - { - const auto& leftStyleStroke = strokeElementLayerStyle->strokePair.first; - const auto& rightStyleStroke = strokeElementLayerStyle->strokePair.second; - if (leftStyleStroke != nullptr) - { - maxLineWidth = std::max(maxLineWidth, leftStyleStroke->halfWidth); - } - if (rightStyleStroke != nullptr) - { - maxLineWidth = std::max(maxLineWidth, rightStyleStroke->halfWidth); - } - } - } - return maxLineWidth; + float maxLineWidth = 0; + const auto strokeStyle = styles.find(StrokeElementLayerStyle::displayName()); + if (strokeStyle != styles.end()) + { + if (const auto strokeElementLayerStyle = + std::dynamic_pointer_cast(strokeStyle->second); + strokeElementLayerStyle != nullptr) + { + const auto& leftStyleStroke = strokeElementLayerStyle->strokePair.first; + const auto& rightStyleStroke = strokeElementLayerStyle->strokePair.second; + if (leftStyleStroke != nullptr) + { + maxLineWidth = std::max(maxLineWidth, leftStyleStroke->halfWidth); + } + if (rightStyleStroke != nullptr) + { + maxLineWidth = std::max(maxLineWidth, rightStyleStroke->halfWidth); + } + } + } + return maxLineWidth; } size_t LayerStyleContainer::getHash() const { - return hash; + return hash; } bool LayerStyleContainer::operator==(const LayerStyleContainer& other) const { - if (getHash() != other.getHash() || unusedStyleNames() != other.unusedStyleNames()) - { - return false; - } - return std::ranges::equal(styles | std::views::values, other.styles | std::views::values); + if (getHash() != other.getHash() || unusedStyleNames() != other.unusedStyleNames()) + { + return false; + } + return std::ranges::equal(styles | std::views::values, other.styles | std::views::values); } LayerStyleContainer LayerStyleContainer::operator|(const LayerStyleContainer& other) const { - LayerStyleContainer result = other; - for (const auto& style : std::ranges::subrange(this->cbegin(), this->cend()) - | std::views::values) - { - result.useStyle(style); - } - result.computeNewHash(); - return result; + LayerStyleContainer result = other; + for (const auto& style : std::ranges::subrange(this->cbegin(), this->cend()) + | std::views::values) + { + result.useStyle(style); + } + result.computeNewHash(); + return result; } LayerStyleContainer& LayerStyleContainer::operator<<(const LayerStyleContainer& other) { - for (auto& style : std::ranges::subrange(other.cbegin(), other.cend()) - | std::views::values) - { - this->useStyle(style, true); - } - computeNewHash(); - return *this; + for (auto& style : std::ranges::subrange(other.cbegin(), other.cend()) + | std::views::values) + { + this->useStyle(style, true); + } + computeNewHash(); + return *this; } std::unique_ptr StrokeElementLayerStyle::fromJson(const QJsonObject& json) { - auto ptr = std::make_unique( - std::static_pointer_cast( - std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["left"].toString())))) - ), - std::static_pointer_cast( - std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["right"].toString())))) - ) - ); - ptr->enableEachSideIndependent = json["enableEachSideIndependent"].toBool(); - return ptr; + auto ptr = std::make_unique( + std::static_pointer_cast( + std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["left"].toString())))) + ), + std::static_pointer_cast( + std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["right"].toString())))) + ) + ); + ptr->enableEachSideIndependent = json["enableEachSideIndependent"].toBool(); + return ptr; } StrokeElementLayerStyle::StrokeElementLayerStyle(bool isClosed) { - const auto materialMap = std::map(); - this->strokePair.first = std::make_shared( - 7, - Renderer::StrokeType::kLeftSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat, - std::make_shared(materialMap, false) - ); + const auto materialMap = std::map(); + this->strokePair.first = std::make_shared( + 7, + Renderer::StrokeType::kLeftSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat, + std::make_shared(materialMap, false) + ); + + this->strokePair.second = std::make_shared( + 7, + Renderer::StrokeType::kRightSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat, + std::make_shared(materialMap, false) + ); - this->strokePair.second = std::make_shared( - 7, - Renderer::StrokeType::kRightSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat, - std::make_shared(materialMap, false) - ); - } StrokeElementLayerStyle::StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right) { - this->strokePair.first = left; - this->strokePair.second = right ? right : std::static_pointer_cast( - std::shared_ptr(std::move(left->clone())) - ); + this->strokePair.first = left; + this->strokePair.second = right ? right : std::static_pointer_cast( + std::shared_ptr(std::move(left->clone())) + ); } StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& other) { - strokePair.first = std::static_pointer_cast( - std::shared_ptr(std::move(other.strokePair.first->clone())) - ); - strokePair.second = std::static_pointer_cast( - std::shared_ptr(std::move(other.strokePair.second->clone())) - ); - enableEachSideIndependent = other.enableEachSideIndependent; + strokePair.first = std::static_pointer_cast( + std::shared_ptr(std::move(other.strokePair.first->clone())) + ); + strokePair.second = std::static_pointer_cast( + std::shared_ptr(std::move(other.strokePair.second->clone())) + ); + enableEachSideIndependent = other.enableEachSideIndependent; } QJsonObject StrokeElementLayerStyle::toJson() const { - auto json = LayerStyle::toJson(); - json["enableEachSideIndependent"] = enableEachSideIndependent; - json["left"] = EncodeUtil::toBase64(strokePair.first->encoded()); - json["right"] = EncodeUtil::toBase64(strokePair.second->encoded()); - return json; + auto json = LayerStyle::toJson(); + json["enableEachSideIndependent"] = enableEachSideIndependent; + json["left"] = EncodeUtil::toBase64(strokePair.first->encoded()); + json["right"] = EncodeUtil::toBase64(strokePair.second->encoded()); + return json; +} + +void StrokeElementLayerStyle::replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) +{ + auto styles = { strokePair.first, strokePair.second }; + for (auto& style : styles) + { + if (!style) continue; + + for (auto& material : std::dynamic_pointer_cast(style->materialStroke)->materialMap | std::views::values) + if (material == origin) + material = target; + } } std::unique_ptr StrokeElementLayerStyle::clone() const { - return std::make_unique(StrokeElementLayerStyle(*this)); + return std::make_unique(StrokeElementLayerStyle(*this)); } std::unique_ptr StrokeElementLayerStyle::fitForElementType(ElementType::ElementType type) const { - auto result = std::unique_ptr(reinterpret_cast(this->clone().release())); - const bool isThisClosed = strokePair.first->endType == Renderer::StrokeEndType::kClosed; - const bool isRequiredTypeClosed = (type & ElementType::TYPE_CLOSED) == ElementType::TYPE_CLOSED; - if (isThisClosed == isRequiredTypeClosed) - { - return result; - } - if (type == ElementType::TYPE_CLOSED) - { - result->strokePair.first->endType = Renderer::StrokeEndType::kClosed; - result->strokePair.second->endType = Renderer::StrokeEndType::kClosed; - } - else if (isThisClosed) - { - result->strokePair.first->endType = Renderer::StrokeEndType::kFlat; - result->strokePair.second->endType = Renderer::StrokeEndType::kFlat; - } - return result; + auto result = std::unique_ptr(reinterpret_cast(this->clone().release())); + const bool isThisClosed = strokePair.first->endType == Renderer::StrokeEndType::kClosed; + const bool isRequiredTypeClosed = (type & ElementType::TYPE_CLOSED) == ElementType::TYPE_CLOSED; + if (isThisClosed == isRequiredTypeClosed) + { + return result; + } + if (type == ElementType::TYPE_CLOSED) + { + result->strokePair.first->endType = Renderer::StrokeEndType::kClosed; + result->strokePair.second->endType = Renderer::StrokeEndType::kClosed; + } + else if (isThisClosed) + { + result->strokePair.first->endType = Renderer::StrokeEndType::kFlat; + result->strokePair.second->endType = Renderer::StrokeEndType::kFlat; + } + return result; } bool StrokeElementLayerStyle::operator==(const LayerStyle& other) const { if (!LayerStyle::operator==(other)) { - return false; + return false; } - const auto otherStyle = dynamic_cast(&other); + const auto otherStyle = dynamic_cast(&other); if (!otherStyle) { - return false; + return false; } - return this->strokePair.first == otherStyle->strokePair.first && this->strokePair.second == otherStyle->strokePair.second; + return this->strokePair.first == otherStyle->strokePair.first && this->strokePair.second == otherStyle->strokePair.second; } std::unique_ptr FillElementLayerStyle::fromJson(const QJsonObject& json) { - auto ptr = std::make_unique( - std::static_pointer_cast( - std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["material"].toString())))) - ) - ); - return ptr; + auto ptr = std::make_unique( + std::static_pointer_cast( + std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64(json["material"].toString())))) + ) + ); + return ptr; } std::vector FillElementLayerStyle::toBaseStyles() const { - return { {std::make_shared(), fillMaterialStyle} }; + return { {std::make_shared(), fillMaterialStyle} }; } QWidget* FillElementLayerStyle::getInputWidget() { - return new FillStyleWidget(fillMaterialStyle); + return new FillStyleWidget(fillMaterialStyle); } QWidget* FillElementLayerStyle::getListDisplayWidget() const { - auto* w = new QWidget; - auto* name = new QLabel(w); - name->setText(QStringLiteral("填充")); - auto* layout = new QHBoxLayout(w); - layout->setMargin(0); - layout->addWidget(name); - return w; + auto* w = new QWidget; + auto* name = new QLabel(w); + name->setText(QStringLiteral("填充")); + auto* layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->addWidget(name); + return w; } FillElementLayerStyle::FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle) @@ -438,76 +458,83 @@ FillElementLayerStyle::FillElementLayerStyle(const PMaterialStyleFill& fillMater { if (!fillMaterialStyle) { - this->fillMaterialStyle = std::make_shared( - std::make_shared(ColorHelper::instance().getPrimary1()) - ); + this->fillMaterialStyle = std::make_shared( + std::make_shared(ColorHelper::instance().getPrimary1()) + ); } } FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other) { - this->fillMaterialStyle = std::static_pointer_cast( - std::shared_ptr(std::move(other.fillMaterialStyle->clone())) - ); + this->fillMaterialStyle = std::static_pointer_cast( + std::shared_ptr(std::move(other.fillMaterialStyle->clone())) + ); } QJsonObject FillElementLayerStyle::toJson() const { - auto json = LayerStyle::toJson(); - json["material"] = EncodeUtil::toBase64(fillMaterialStyle->encoded()); - return json; + auto json = LayerStyle::toJson(); + json["material"] = EncodeUtil::toBase64(fillMaterialStyle->encoded()); + return json; +} + +void FillElementLayerStyle::replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) +{ + auto materialFill = std::dynamic_pointer_cast(fillMaterialStyle->materialFill); + if (materialFill->material == origin) + materialFill->material = target; } std::unique_ptr FillElementLayerStyle::clone() const { - return std::make_unique(FillElementLayerStyle(*this)); + return std::make_unique(FillElementLayerStyle(*this)); } std::unique_ptr FillElementLayerStyle::fitForElementType(ElementType::ElementType type) const { - if (type & ElementType::TYPE_CLOSED) - { - return this->clone(); - } - return nullptr; + if (type & ElementType::TYPE_CLOSED) + { + return this->clone(); + } + return nullptr; } bool FillElementLayerStyle::operator==(const LayerStyle& other) const { - if (!LayerStyle::operator==(other)) - { - return false; - } - const auto otherStyle = dynamic_cast(&other); - if (!otherStyle) - { - return false; - } - return this->fillMaterialStyle == otherStyle->fillMaterialStyle; + if (!LayerStyle::operator==(other)) + { + return false; + } + const auto otherStyle = dynamic_cast(&other); + if (!otherStyle) + { + return false; + } + return this->fillMaterialStyle == otherStyle->fillMaterialStyle; } std::unique_ptr LayerStyle::fromJson(const QJsonObject& json) { - QString type = json["type"].toString(); - if (type == StrokeElementLayerStyle::typeName()) - { - return StrokeElementLayerStyle::fromJson(json); - } - if (type == FillElementLayerStyle::typeName()) - { - return FillElementLayerStyle::fromJson(json); - } + QString type = json["type"].toString(); + if (type == StrokeElementLayerStyle::typeName()) + { + return StrokeElementLayerStyle::fromJson(json); + } + if (type == FillElementLayerStyle::typeName()) + { + return FillElementLayerStyle::fromJson(json); + } return nullptr; } QJsonObject LayerStyle::toJson() const { - QJsonObject json; - json["type"] = this->getTypeName(); - return json; + QJsonObject json; + json["type"] = this->getTypeName(); + return json; } bool LayerStyle::operator==(const LayerStyle& other) const { - return this->getTypeName() == other.getTypeName(); + return this->getTypeName() == other.getTypeName(); } diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.h b/ArchitectureColoredPainting/src/Editor/LayerStyle.h index 3e29ae5..04d2b42 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.h +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.h @@ -23,79 +23,82 @@ using Renderer::MaterialStyleFill; namespace ElementType { - using ElementType = unsigned short; - constexpr ElementType TYPE_ALL = 0xffff; - constexpr ElementType TYPE_CLOSED = 0b01; - constexpr ElementType TYPE_UNCLOSED = 0b10; + using ElementType = unsigned short; + constexpr ElementType TYPE_ALL = 0xffff; + constexpr ElementType TYPE_CLOSED = 0b01; + constexpr ElementType TYPE_UNCLOSED = 0b10; }; class LayerStyle : public Renderer::ElementStyle { public: - static std::unique_ptr fromJson(const QJsonObject& json); - virtual ~LayerStyle() = default; + static std::unique_ptr fromJson(const QJsonObject& json); + virtual ~LayerStyle() = default; - virtual QString getDisplayName() const = 0; - virtual QString getTypeName() const = 0; + virtual QString getDisplayName() const = 0; + virtual QString getTypeName() const = 0; - virtual QWidget* getInputWidget() = 0; - virtual QWidget* getListDisplayWidget() const = 0; + virtual QWidget* getInputWidget() = 0; + virtual QWidget* getListDisplayWidget() const = 0; - virtual QJsonObject toJson() const; - virtual std::unique_ptr clone() const = 0; - virtual std::unique_ptr fitForElementType(ElementType::ElementType type) const = 0; + virtual QJsonObject toJson() const; + virtual void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) = 0; + virtual std::unique_ptr clone() const = 0; + virtual std::unique_ptr fitForElementType(ElementType::ElementType type) const = 0; - virtual bool operator==(const LayerStyle& other) const; + virtual bool operator==(const LayerStyle& other) const; }; class StrokeElementLayerStyle : public LayerStyle { - using PMaterialStyleStroke = std::shared_ptr; + using PMaterialStyleStroke = std::shared_ptr; public: - STYLE_NAME("描边", "stroke") - static std::unique_ptr fromJson(const QJsonObject& json); + STYLE_NAME("描边", "stroke") + static std::unique_ptr fromJson(const QJsonObject& json); - StrokeElementLayerStyle(bool isClosed); - StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr); - StrokeElementLayerStyle(const StrokeElementLayerStyle& other); - ~StrokeElementLayerStyle() override = default; + StrokeElementLayerStyle(bool isClosed); + StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr); + StrokeElementLayerStyle(const StrokeElementLayerStyle& other); + ~StrokeElementLayerStyle() override = default; - std::vector toBaseStyles() const override; - QWidget* getInputWidget() override; - QWidget* getListDisplayWidget() const override; - QJsonObject toJson() const override; - std::unique_ptr clone() const override; - std::unique_ptr fitForElementType(ElementType::ElementType type) const override; + std::vector toBaseStyles() const override; + QWidget* getInputWidget() override; + QWidget* getListDisplayWidget() const override; + QJsonObject toJson() const override; + void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) override; + std::unique_ptr clone() const override; + std::unique_ptr fitForElementType(ElementType::ElementType type) const override; - bool operator==(const LayerStyle& other) const override; + bool operator==(const LayerStyle& other) const override; - std::pair strokePair; - bool enableEachSideIndependent = false; + std::pair strokePair; + bool enableEachSideIndependent = false; }; class FillElementLayerStyle : public LayerStyle { - using PMaterialStyleFill = std::shared_ptr; + using PMaterialStyleFill = std::shared_ptr; public: - STYLE_NAME("填充", "fill") - static std::unique_ptr fromJson(const QJsonObject& json); + STYLE_NAME("填充", "fill") + static std::unique_ptr fromJson(const QJsonObject& json); - FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle = nullptr); - FillElementLayerStyle(const FillElementLayerStyle& other); - ~FillElementLayerStyle() override = default; + FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle = nullptr); + FillElementLayerStyle(const FillElementLayerStyle& other); + ~FillElementLayerStyle() override = default; - std::vector toBaseStyles() const override; - QWidget* getInputWidget() override; - QWidget* getListDisplayWidget() const override; - QJsonObject toJson() const override; - std::unique_ptr clone() const override; - std::unique_ptr fitForElementType(ElementType::ElementType type) const override; + std::vector toBaseStyles() const override; + QWidget* getInputWidget() override; + QWidget* getListDisplayWidget() const override; + QJsonObject toJson() const override; + void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) override; + std::unique_ptr clone() const override; + std::unique_ptr fitForElementType(ElementType::ElementType type) const override; - bool operator==(const LayerStyle& other) const override; + bool operator==(const LayerStyle& other) const override; - PMaterialStyleFill fillMaterialStyle; + PMaterialStyleFill fillMaterialStyle; }; /** @@ -103,62 +106,63 @@ public: */ class LayerStyleContainer : public Renderer::ElementStyle { - using DisplayNameWithSupplier = std::map()>>; + using DisplayNameWithSupplier = std::map()>>; private: - inline const static DisplayNameWithSupplier commonStyles = { }; - inline const static DisplayNameWithSupplier closedOnlyStyles = { - { - FillElementLayerStyle::displayName(), - [] { return std::make_unique(); } + inline const static DisplayNameWithSupplier commonStyles = { }; + inline const static DisplayNameWithSupplier closedOnlyStyles = { + { + FillElementLayerStyle::displayName(), + [] { return std::make_unique(); } }, { - StrokeElementLayerStyle::displayName(), - [] { return std::make_unique(true); } + StrokeElementLayerStyle::displayName(), + [] { return std::make_unique(true); } } - }; - inline const static DisplayNameWithSupplier unclosedOnlyStyles = { { - StrokeElementLayerStyle::displayName(), - [] { return std::make_unique(false); } - } }; + }; + inline const static DisplayNameWithSupplier unclosedOnlyStyles = { { + StrokeElementLayerStyle::displayName(), + [] { return std::make_unique(false); } + } }; - ElementType::ElementType elementType; - DisplayNameWithSupplier unusedStyles; - DisplayNameWithSupplier usedStyles; - std::map> styles; - size_t hash; + ElementType::ElementType elementType; + DisplayNameWithSupplier unusedStyles; + DisplayNameWithSupplier usedStyles; + std::map> styles; + size_t hash; public: - static LayerStyleContainer fromJson(ElementType::ElementType elementType, const QJsonArray& jsonArray); + static LayerStyleContainer fromJson(ElementType::ElementType elementType, const QJsonArray& jsonArray); - LayerStyleContainer(ElementType::ElementType elementType); - [[nodiscard]] std::vector toBaseStyles() const override; - [[nodiscard]] QJsonArray toJson() const; + LayerStyleContainer(ElementType::ElementType elementType); + [[nodiscard]] std::vector toBaseStyles() const override; + [[nodiscard]] QJsonArray toJson() const; + void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target); - [[nodiscard]] bool empty() const; - [[nodiscard]] bool full() const; - [[nodiscard]] std::map>::iterator begin(); - [[nodiscard]] std::map>::iterator end(); - [[nodiscard]] std::map>::const_iterator cbegin() const; - [[nodiscard]] std::map>::const_iterator cend() const; + [[nodiscard]] bool empty() const; + [[nodiscard]] bool full() const; + [[nodiscard]] std::map>::iterator begin(); + [[nodiscard]] std::map>::iterator end(); + [[nodiscard]] std::map>::const_iterator cbegin() const; + [[nodiscard]] std::map>::const_iterator cend() const; - [[nodiscard]] QStringList unusedStyleNames() const; - [[nodiscard]] std::unique_ptr makeUnusedStyle(const QString& styleName) const; - bool useStyle(const std::shared_ptr& style, bool forceOverride = false); - bool overrideStyle(const std::shared_ptr& style); - bool dropStyle(const QString& styleName); - [[nodiscard]] std::shared_ptr getStyle(const QString& styleName) const; - [[nodiscard]] float boundingBoxAffectValue() const; - [[nodiscard]] size_t getHash() const; + [[nodiscard]] QStringList unusedStyleNames() const; + [[nodiscard]] std::unique_ptr makeUnusedStyle(const QString& styleName) const; + bool useStyle(const std::shared_ptr& style, bool forceOverride = false); + bool overrideStyle(const std::shared_ptr& style); + bool dropStyle(const QString& styleName); + [[nodiscard]] std::shared_ptr getStyle(const QString& styleName) const; + [[nodiscard]] float boundingBoxAffectValue() const; + [[nodiscard]] size_t getHash() const; - [[nodiscard]] bool operator==(const LayerStyleContainer& other) const; - /** - * 类管道操作,后者覆盖前者,会返回一个新的LayerStyleContainer - */ - [[nodiscard]] LayerStyleContainer operator|(const LayerStyleContainer& other) const; - LayerStyleContainer& operator<<(const LayerStyleContainer& other); + [[nodiscard]] bool operator==(const LayerStyleContainer& other) const; + /** + * 类管道操作,后者覆盖前者,会返回一个新的LayerStyleContainer + */ + [[nodiscard]] LayerStyleContainer operator|(const LayerStyleContainer& other) const; + LayerStyleContainer& operator<<(const LayerStyleContainer& other); - /** - * 需要在每次更改后手动调用 - */ - void computeNewHash(); + /** + * 需要在每次更改后手动调用 + */ + void computeNewHash(); }; \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp index 7c52d1a..6ad8dc0 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp @@ -294,6 +294,13 @@ QJsonObject FolderLayerWrapper::toJson() const return json; } +void FolderLayerWrapper::replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) +{ + for (auto& child : children) + if (child != nullptr) + child->replaceMaterial(origin, target); +} + QJsonObject LeafLayerWrapper::toJson() const { QJsonObject json = LayerWrapper::toJson(); @@ -303,6 +310,11 @@ QJsonObject LeafLayerWrapper::toJson() const return json; } +void LeafLayerWrapper::replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) +{ + styles->replaceMaterial(origin, target); +} + int FolderLayerWrapper::getReferencedBy()const { if (this->elementManager != nullptr) diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h index 6d4bfaa..8a964df 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h @@ -71,6 +71,7 @@ class LayerWrapper virtual void del(); virtual void delSelf(); virtual QJsonObject toJson() const; + virtual void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) = 0; ~LayerWrapper() = default; virtual void collectUpReachable(std::set& reachable); virtual void collectDownReachable(std::set& reachable); @@ -105,6 +106,7 @@ class FolderLayerWrapper : public LayerWrapper void delSelf() override; QTreeWidgetItem* getQTreeItem() override; QJsonObject toJson() const override; + void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) override; int getReferencedBy()const; void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(ElementType::TYPE_ALL)) override; void collectDownReachable(std::set& reachable) override; @@ -125,6 +127,7 @@ class LeafLayerWrapper : public LayerWrapper void refresh(LayerWrapper* layer = nullptr) override; LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent); QJsonObject toJson() const override; + void replaceMaterial(const Renderer::Material& origin, const Renderer::Material& target) override; void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(ElementType::TYPE_ALL)) override; void collectDownReachable(std::set& reachable) override; void collectDownUsedElements(std::set& elements, bool deep = true) override; diff --git a/ArchitectureColoredPainting/src/Editor/Properties/MaterialReplaceDialog.cpp b/ArchitectureColoredPainting/src/Editor/Properties/MaterialReplaceDialog.cpp new file mode 100644 index 0000000..8deeace --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/Properties/MaterialReplaceDialog.cpp @@ -0,0 +1,53 @@ +#include "MaterialReplaceDialog.h" +#include +#include + +MaterialReplaceDialog::MaterialReplaceDialog(const EditorWidgetItem* editorWidgetItem, QWidget* parent) + : QDialog(parent), + editorWidgetItem(editorWidgetItem), + originColorPicker(new ColorPicker(ColorHelper::instance().getPrimary1(), this)), + originRoughnessField(new QtMaterialTextField(this)), + originMetallicField(new QtMaterialTextField(this)), + targetColorPicker(new ColorPicker(ColorHelper::instance().getPrimary1(), this)), + targetRoughnessField(new QtMaterialTextField(this)), + targetMetallicField(new QtMaterialTextField(this)) +{ + setWindowTitle(QStringLiteral("材质替换")); + + auto* vbox = new QVBoxLayout(this); + + auto gridLayout = new QGridLayout(this); + + auto* formLeft = new QFormLayout(this); + formLeft->addRow(QStringLiteral("原基础色"), originColorPicker); + formLeft->addRow(QStringLiteral("原粗糙度"), originRoughnessField); + formLeft->addRow(QStringLiteral("原金属度"), originMetallicField); + originRoughnessField->setText(QString::number(0.5)); + originMetallicField->setText(QString::number(0)); + + auto* formRight = new QFormLayout(this); + formRight->addRow(QStringLiteral("目标基础色"), targetColorPicker); + formRight->addRow(QStringLiteral("目标粗糙度"), targetRoughnessField); + formRight->addRow(QStringLiteral("目标金属度"), targetMetallicField); + targetRoughnessField->setText(QString::number(0.5)); + targetMetallicField->setText(QString::number(0)); + + gridLayout->addLayout(formLeft, 0, 0); + gridLayout->addLayout(formRight, 0, 1); + + vbox->addLayout(gridLayout); + auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + vbox->addWidget(buttonBox); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &MaterialReplaceDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &MaterialReplaceDialog::reject); +} + +void MaterialReplaceDialog::accept() +{ + editorWidgetItem->layerManager->getRoot()->replaceMaterial( + Renderer::Material(originColorPicker->getColor(), originMetallicField->text().toFloat(), originRoughnessField->text().toFloat()), + Renderer::Material(targetColorPicker->getColor(), targetMetallicField->text().toFloat(), targetRoughnessField->text().toFloat()) + ); + QDialog::accept(); +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/Properties/MaterialReplaceDialog.h b/ArchitectureColoredPainting/src/Editor/Properties/MaterialReplaceDialog.h new file mode 100644 index 0000000..78a70c9 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/Properties/MaterialReplaceDialog.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "EditorWidgetItem.h" +#include "EditorWidgetComponent/ColorPicker.h" +#include "../Renderer/Painting/BaseStyle.h" + +class MaterialReplaceDialog : public QDialog +{ + Q_OBJECT + +public: + MaterialReplaceDialog(const EditorWidgetItem* editorWidgetItem, QWidget* parent = nullptr); + +private slots: + void accept() override; + +private: + const EditorWidgetItem* editorWidgetItem; + ColorPicker* originColorPicker; QtMaterialTextField* originRoughnessField; QtMaterialTextField* originMetallicField; + ColorPicker* targetColorPicker; QtMaterialTextField* targetRoughnessField; QtMaterialTextField* targetMetallicField; +}; + diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp index 30c076a..f4567de 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.cpp @@ -1,6 +1,7 @@ #include "BaseStyle.h" #include "MaterialStyleFill.h" #include "MaterialStyleStroke.h" +#include using namespace Renderer; using namespace glm; @@ -107,3 +108,8 @@ std::unique_ptr Renderer::MaterialStyle::decoded(const std::vecto return std::make_unique(encoded[0], strokeType, endType, std::move(materialStroke), widthMap); } } + +QDebug operator<<(QDebug d, const Renderer::Material& m) +{ + return d << m.color << " " << m.metallic << " " << m.roughness; +} diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h index 115aefb..9ab27df 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/BaseStyle.h @@ -54,3 +54,5 @@ namespace Renderer std::pair toVec() const; }; } + +QDebug operator<<(QDebug, const Renderer::Material&); \ No newline at end of file