diff --git a/application/sources/component_property_widget.cc b/application/sources/component_property_widget.cc index 2395229b..6e80397a 100644 --- a/application/sources/component_property_widget.cc +++ b/application/sources/component_property_widget.cc @@ -4,6 +4,7 @@ #include #include #include "component_property_widget.h" +#include "float_number_widget.h" #include "document.h" #include "theme.h" @@ -22,21 +23,140 @@ ComponentPropertyWidget::ComponentPropertyWidget(Document *document, colorPreviewArea->setFixedSize(Theme::toolIconSize * 1.8, Theme::toolIconSize); QPushButton *colorPickerButton = new QPushButton(QChar(fa::eyedropper)); - colorPickerButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); + Theme::initIconButton(colorPickerButton); connect(colorPickerButton, &QPushButton::clicked, this, &ComponentPropertyWidget::showColorDialog); QHBoxLayout *colorLayout = new QHBoxLayout; colorLayout->addWidget(colorPreviewArea); colorLayout->addWidget(colorPickerButton); + colorLayout->addStretch(); colorLayout->setSizeConstraint(QLayout::SetFixedSize); + QVBoxLayout *deformLayout = nullptr; + if (nullptr != m_part) { + + FloatNumberWidget *thicknessWidget = new FloatNumberWidget; + thicknessWidget->setItemName(tr("Thickness")); + thicknessWidget->setRange(0, 2); + thicknessWidget->setValue(m_part->deformThickness); + + connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartDeformThickness(m_partId, value); + emit groupOperationAdded(); + }); + + FloatNumberWidget *widthWidget = new FloatNumberWidget; + widthWidget->setItemName(tr("Width")); + widthWidget->setRange(0, 2); + widthWidget->setValue(m_part->deformWidth); + + connect(widthWidget, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartDeformWidth(m_partId, value); + emit groupOperationAdded(); + }); + + QPushButton *thicknessEraser = new QPushButton(QChar(fa::eraser)); + Theme::initIconButton(thicknessEraser); + + connect(thicknessEraser, &QPushButton::clicked, [=]() { + thicknessWidget->setValue(1.0); + emit groupOperationAdded(); + }); + + QPushButton *widthEraser = new QPushButton(QChar(fa::eraser)); + Theme::initIconButton(widthEraser); + + connect(widthEraser, &QPushButton::clicked, [=]() { + widthWidget->setValue(1.0); + emit groupOperationAdded(); + }); + + deformLayout = new QVBoxLayout; + + QHBoxLayout *thicknessLayout = new QHBoxLayout; + QHBoxLayout *widthLayout = new QHBoxLayout; + thicknessLayout->addWidget(thicknessEraser); + thicknessLayout->addWidget(thicknessWidget); + widthLayout->addWidget(widthEraser); + widthLayout->addWidget(widthWidget); + + QCheckBox *deformUnifyStateBox = new QCheckBox(); + Theme::initCheckbox(deformUnifyStateBox); + deformUnifyStateBox->setText(tr("Unified")); + deformUnifyStateBox->setChecked(m_part->deformUnified); + + connect(deformUnifyStateBox, &QCheckBox::stateChanged, this, [=]() { + emit setPartDeformUnified(m_partId, deformUnifyStateBox->isChecked()); + emit groupOperationAdded(); + }); + + QHBoxLayout *deformUnifyLayout = new QHBoxLayout; + deformUnifyLayout->addStretch(); + deformUnifyLayout->addWidget(deformUnifyStateBox); + + deformLayout->addLayout(thicknessLayout); + deformLayout->addLayout(widthLayout); + deformLayout->addLayout(deformUnifyLayout); + } + + QHBoxLayout *rotationLayout = nullptr; + if (nullptr != m_part) { + FloatNumberWidget *rotationWidget = new FloatNumberWidget; + rotationWidget->setItemName(tr("Rotation")); + rotationWidget->setRange(-1, 1); + rotationWidget->setValue(m_part->cutRotation); + + connect(rotationWidget, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartCutRotation(m_partId, value); + emit groupOperationAdded(); + }); + + QPushButton *rotationEraser = new QPushButton(QChar(fa::eraser)); + Theme::initIconButton(rotationEraser); + + connect(rotationEraser, &QPushButton::clicked, [=]() { + rotationWidget->setValue(0.0); + emit groupOperationAdded(); + }); + + QPushButton *rotationMinus5Button = new QPushButton(QChar(fa::rotateleft)); + Theme::initIconButton(rotationMinus5Button); + + connect(rotationMinus5Button, &QPushButton::clicked, [=]() { + rotationWidget->setValue(-0.5); + emit groupOperationAdded(); + }); + + QPushButton *rotation5Button = new QPushButton(QChar(fa::rotateright)); + Theme::initIconButton(rotation5Button); + + connect(rotation5Button, &QPushButton::clicked, [=]() { + rotationWidget->setValue(0.5); + emit groupOperationAdded(); + }); + + rotationLayout = new QHBoxLayout; + rotationLayout->addWidget(rotationEraser); + rotationLayout->addWidget(rotationWidget); + rotationLayout->addWidget(rotationMinus5Button); + rotationLayout->addWidget(rotation5Button); + } + QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(colorLayout); + if (nullptr != deformLayout) + mainLayout->addLayout(deformLayout); + if (nullptr != rotationLayout) + mainLayout->addLayout(rotationLayout); mainLayout->setSizeConstraint(QLayout::SetFixedSize); connect(this, &ComponentPropertyWidget::setPartColorState, m_document, &Document::setPartColorState); connect(this, &ComponentPropertyWidget::endColorPicking, m_document, &Document::enableBackgroundBlur); connect(this, &ComponentPropertyWidget::beginColorPicking, m_document, &Document::disableBackgroundBlur); + connect(this, &ComponentPropertyWidget::setPartDeformThickness, m_document, &Document::setPartDeformThickness); + connect(this, &ComponentPropertyWidget::setPartDeformWidth, m_document, &Document::setPartDeformWidth); + connect(this, &ComponentPropertyWidget::setPartDeformUnified, m_document, &Document::setPartDeformUnified); + connect(this, &ComponentPropertyWidget::setPartCutRotation, m_document, &Document::setPartCutRotation); connect(this, &ComponentPropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot); setLayout(mainLayout); @@ -55,6 +175,11 @@ void ComponentPropertyWidget::preparePartIds() m_partIds.emplace_back(it); } } + if (1 == m_partIds.size()) { + m_part = m_document->findPart(m_partIds.front()); + if (nullptr != m_part) + m_partId = m_partIds.front(); + } } QColor ComponentPropertyWidget::lastColor() diff --git a/application/sources/component_property_widget.h b/application/sources/component_property_widget.h index adb9029e..6c46b9d0 100644 --- a/application/sources/component_property_widget.h +++ b/application/sources/component_property_widget.h @@ -5,6 +5,7 @@ #include class Document; +class SkeletonPart; class ComponentPropertyWidget: public QWidget { @@ -13,6 +14,10 @@ signals: void beginColorPicking(); void endColorPicking(); void setPartColorState(const dust3d::Uuid &partId, bool hasColor, const QColor &color); + void setPartDeformThickness(const dust3d::Uuid &partId, float thickness); + void setPartDeformWidth(const dust3d::Uuid &partId, float width); + void setPartDeformUnified(const dust3d::Uuid &partId, bool unified); + void setPartCutRotation(const dust3d::Uuid &partId, float cutRotation); void groupOperationAdded(); public: ComponentPropertyWidget(Document *document, @@ -24,6 +29,8 @@ private: Document *m_document = nullptr; std::vector m_componentIds; std::vector m_partIds; + dust3d::Uuid m_partId; + const SkeletonPart *m_part = nullptr; QColor m_color; QColor lastColor(); void preparePartIds(); diff --git a/application/sources/cut_face_preview.cc b/application/sources/cut_face_preview.cc index 7df79464..190d27ed 100644 --- a/application/sources/cut_face_preview.cc +++ b/application/sources/cut_face_preview.cc @@ -29,7 +29,7 @@ QImage *buildCutFaceTemplatePreviewImage(const std::vector &cut const float scale = 0.7f; QPolygon polygon; - for (int i = 0; i <= cutTemplate.size(); ++i) { + for (size_t i = 0; i <= cutTemplate.size(); ++i) { const auto &it = cutTemplate[i % cutTemplate.size()]; polygon.append(QPoint((it.x() * scale + 1.0) * 0.5 * Theme::partPreviewImageSize, (it.y() * scale + 1.0) * 0.5 * Theme::partPreviewImageSize)); diff --git a/application/sources/part_manage_widget.cc b/application/sources/part_manage_widget.cc index 9837ccf0..0a28c7dd 100644 --- a/application/sources/part_manage_widget.cc +++ b/application/sources/part_manage_widget.cc @@ -22,7 +22,7 @@ PartManageWidget::PartManageWidget(Document *document, QWidget *parent): auto createButton = [](QChar icon, const QString &title) { QPushButton *button = new QPushButton(icon); - button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); + Theme::initIconButton(button); button->setToolTip(title); return button; }; diff --git a/application/sources/theme.cc b/application/sources/theme.cc index faa4a5fa..afb0c677 100644 --- a/application/sources/theme.cc +++ b/application/sources/theme.cc @@ -158,3 +158,8 @@ void Theme::initCheckbox(QCheckBox *checkbox) palette.setColor(QPalette::Background, Theme::white); checkbox->setPalette(palette); } + +void Theme::initIconButton(QPushButton *button) +{ + button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); +} diff --git a/application/sources/theme.h b/application/sources/theme.h index 05e7ee37..2964d15d 100644 --- a/application/sources/theme.h +++ b/application/sources/theme.h @@ -43,6 +43,7 @@ public: static void initAwsome(); static void initToolButton(QPushButton *button); static void initCheckbox(QCheckBox *checkbox); + static void initIconButton(QPushButton *button); }; #endif diff --git a/dust3d/mesh/mesh_generator.cc b/dust3d/mesh/mesh_generator.cc index 768a062b..dfc99de8 100644 --- a/dust3d/mesh/mesh_generator.cc +++ b/dust3d/mesh/mesh_generator.cc @@ -659,6 +659,10 @@ std::unique_ptr MeshGenerator::combinePartMesh(const std::st return nullptr; TubeMeshBuilder::BuildParameters buildParameters; + buildParameters.deformThickness = deformThickness; + buildParameters.deformWidth = deformWidth; + buildParameters.deformUnified = deformUnified; + buildParameters.baseNormalRotation = cutRotation * Math::Pi; buildParameters.cutFace = cutTemplate; auto tubeMeshBuilder = std::make_unique(buildParameters, std::move(meshNodes), isCircle); tubeMeshBuilder->build(); diff --git a/dust3d/mesh/tube_mesh_builder.cc b/dust3d/mesh/tube_mesh_builder.cc index 5e613370..64a21ee6 100644 --- a/dust3d/mesh/tube_mesh_builder.cc +++ b/dust3d/mesh/tube_mesh_builder.cc @@ -94,8 +94,8 @@ std::vector TubeMeshBuilder::buildCutFaceVertices(const Vector3 &origin const Vector3 &forwardDirection) { std::vector cutFaceVertices(m_buildParameters.cutFace.size()); - Vector3 u = m_generatedBaseNormal; - Vector3 v = Vector3::crossProduct(forwardDirection, m_generatedBaseNormal).normalized(); + Vector3 u = m_generatedBaseNormal.rotated(-forwardDirection, m_buildParameters.baseNormalRotation); + Vector3 v = Vector3::crossProduct(forwardDirection, u).normalized(); auto uFactor = u * radius; auto vFactor = v * radius; for (size_t i = 0; i < m_buildParameters.cutFace.size(); ++i) { diff --git a/dust3d/mesh/tube_mesh_builder.h b/dust3d/mesh/tube_mesh_builder.h index 8f5b5107..8dacaf79 100644 --- a/dust3d/mesh/tube_mesh_builder.h +++ b/dust3d/mesh/tube_mesh_builder.h @@ -37,7 +37,10 @@ public: struct BuildParameters { std::vector cutFace; - double baseNormalRotation; + double deformThickness = 1.0; + double deformWidth = 1.0; + bool deformUnified = false; + double baseNormalRotation = 0.0; }; TubeMeshBuilder(const BuildParameters &buildParameters, std::vector &&nodes, bool isCircle);