Add parts subtract operation.
This is useful for making hole surface. For example, without subtraction, it's difficult to make a bucket like mesh, now, you just need to copy and paste the nodes, invert it from the Parts List Panel Context Menu, then down scale and move up a little bit.master
parent
35635534c2
commit
22910028e9
|
@ -3,7 +3,7 @@ Parts List Panel
|
||||||
|
|
||||||
.. image:: https://raw.githubusercontent.com/huxingyi/dust3d/master/docs/interface/parts-list-panel.png
|
.. image:: https://raw.githubusercontent.com/huxingyi/dust3d/master/docs/interface/parts-list-panel.png
|
||||||
|
|
||||||
In Dust3D, model consists of parts, part consists of nodes. User manipulats nodes's position and radius, toggle part's settings, then the mesh autogenerated by Dust3D.
|
In Dust3D, model consists of parts, part consists of nodes. User manipulates nodes's position and radius, toggle part's settings, then the mesh autogenerated by Dust3D.
|
||||||
|
|
||||||
Top Mini Buttons
|
Top Mini Buttons
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -48,3 +48,23 @@ If there are two parts have the same shape but one sit left, the other sit right
|
||||||
* End Ro(U)ndable Modifier
|
* End Ro(U)ndable Modifier
|
||||||
|
|
||||||
Toggle the begin/end roundable (Flat / Rounded). If you want the cut effect, choose not rounded, otherwise, the edge end will be rounded (Automatically added one more small face).
|
Toggle the begin/end roundable (Flat / Rounded). If you want the cut effect, choose not rounded, otherwise, the edge end will be rounded (Automatically added one more small face).
|
||||||
|
|
||||||
|
Context Menu
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Right click on Parts List Panel to trigger Context Menu.
|
||||||
|
|
||||||
|
* Hide Part / Show Part / Hide Other Parts / Show All Parts / Hide All Parts
|
||||||
|
|
||||||
|
Check mini button **Visible/(H)idden**.
|
||||||
|
|
||||||
|
* Lock All Parts / Unlock All Parts
|
||||||
|
|
||||||
|
Check mini button **_(L)ock/Unlock**.
|
||||||
|
|
||||||
|
* Invert Part / Cancel Inverse
|
||||||
|
|
||||||
|
Inverting a part means subtract this part from other parts which sit before this part.
|
||||||
|
|
||||||
|
* Move Up / Move Down / Move To Top / Move To Bottom
|
||||||
|
|
||||||
|
The process of mesh generation is mainly combining all the parts in listed order, so move up and down may affect the generated result. The order is especially important for inverse part been placed, if the inverse part sit in the first place of the list, the inverse operation would never happen, because nothing to invert before it.
|
||||||
|
|
|
@ -348,6 +348,7 @@ void MeshGenerator::process()
|
||||||
|
|
||||||
std::vector<int> meshIds;
|
std::vector<int> meshIds;
|
||||||
std::vector<int> subdivMeshIds;
|
std::vector<int> subdivMeshIds;
|
||||||
|
std::set<int> inverseIds;
|
||||||
for (const auto &partIdIt: m_snapshot->partIdList) {
|
for (const auto &partIdIt: m_snapshot->partIdList) {
|
||||||
const auto &part = m_snapshot->parts.find(partIdIt);
|
const auto &part = m_snapshot->parts.find(partIdIt);
|
||||||
if (part == m_snapshot->parts.end())
|
if (part == m_snapshot->parts.end())
|
||||||
|
@ -378,15 +379,21 @@ void MeshGenerator::process()
|
||||||
m_partPreviewMap[partIdIt] = image;
|
m_partPreviewMap[partIdIt] = image;
|
||||||
}
|
}
|
||||||
meshIds.push_back(meshId);
|
meshIds.push_back(meshId);
|
||||||
if (xMirroredMeshId)
|
bool inverse = isTrueValueString(valueOfKeyInMapOrEmpty(part->second, "inverse"));
|
||||||
|
if (inverse)
|
||||||
|
inverseIds.insert(meshId);
|
||||||
|
if (xMirroredMeshId) {
|
||||||
meshIds.push_back(xMirroredMeshId);
|
meshIds.push_back(xMirroredMeshId);
|
||||||
|
if (inverse)
|
||||||
|
inverseIds.insert(xMirroredMeshId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subdivMeshIds.empty()) {
|
if (!subdivMeshIds.empty()) {
|
||||||
int mergedMeshId = 0;
|
int mergedMeshId = 0;
|
||||||
if (subdivMeshIds.size() > 1) {
|
if (subdivMeshIds.size() > 1) {
|
||||||
int errorCount = 0;
|
int errorCount = 0;
|
||||||
mergedMeshId = unionMeshs(meshliteContext, subdivMeshIds, &errorCount);
|
mergedMeshId = unionMeshs(meshliteContext, subdivMeshIds, inverseIds, &errorCount);
|
||||||
if (errorCount)
|
if (errorCount)
|
||||||
broken = true;
|
broken = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -416,7 +423,7 @@ void MeshGenerator::process()
|
||||||
if (disableUnion)
|
if (disableUnion)
|
||||||
mergedMeshId = mergeMeshs(meshliteContext, meshIds);
|
mergedMeshId = mergeMeshs(meshliteContext, meshIds);
|
||||||
else
|
else
|
||||||
mergedMeshId = unionMeshs(meshliteContext, meshIds, &errorCount);
|
mergedMeshId = unionMeshs(meshliteContext, meshIds, inverseIds, &errorCount);
|
||||||
if (errorCount)
|
if (errorCount)
|
||||||
broken = true;
|
broken = true;
|
||||||
else if (mergedMeshId > 0)
|
else if (mergedMeshId > 0)
|
||||||
|
|
|
@ -140,7 +140,21 @@ ExactMesh *unionCgalMeshs(ExactMesh *first, ExactMesh *second)
|
||||||
delete mesh;
|
delete mesh;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
//CGAL::Polygon_mesh_processing::isotropic_remeshing(mesh->faces(), 0.4, *mesh);
|
} catch (...) {
|
||||||
|
delete mesh;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactMesh *diffCgalMeshs(ExactMesh *first, ExactMesh *second)
|
||||||
|
{
|
||||||
|
ExactMesh *mesh = new ExactMesh;
|
||||||
|
try {
|
||||||
|
if (!PMP::corefine_and_compute_difference(*first, *second, *mesh)) {
|
||||||
|
delete mesh;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
delete mesh;
|
delete mesh;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -150,7 +164,7 @@ ExactMesh *unionCgalMeshs(ExactMesh *first, ExactMesh *second)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, int *errorCount)
|
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount)
|
||||||
{
|
{
|
||||||
#if USE_CGAL == 1
|
#if USE_CGAL == 1
|
||||||
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
|
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
|
||||||
|
@ -185,7 +199,10 @@ int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, int *erro
|
||||||
}
|
}
|
||||||
ExactMesh *unionedExternalMesh = NULL;
|
ExactMesh *unionedExternalMesh = NULL;
|
||||||
try {
|
try {
|
||||||
|
if (inverseIds.find(meshIds[i]) == inverseIds.end())
|
||||||
unionedExternalMesh = unionCgalMeshs(mergedExternalMesh, externalMeshs[i]);
|
unionedExternalMesh = unionCgalMeshs(mergedExternalMesh, externalMeshs[i]);
|
||||||
|
else
|
||||||
|
unionedExternalMesh = diffCgalMeshs(mergedExternalMesh, externalMeshs[i]);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
qDebug() << "unionCgalMeshs throw exception";
|
qDebug() << "unionCgalMeshs throw exception";
|
||||||
if (errorCount)
|
if (errorCount)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#ifndef MESH_UTIL_H
|
#ifndef MESH_UTIL_H
|
||||||
#define MESH_UTIL_H
|
#define MESH_UTIL_H
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
int mergeMeshs(void *meshliteContext, const std::vector<int> &meshIds);
|
int mergeMeshs(void *meshliteContext, const std::vector<int> &meshIds);
|
||||||
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, int *errorCount=0);
|
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount=0);
|
||||||
int subdivMesh(void *meshliteContext, int meshId, int *errorCount=0);
|
int subdivMesh(void *meshliteContext, int meshId, int *errorCount=0);
|
||||||
int fixMeshHoles(void *meshliteContext, int meshId);
|
int fixMeshHoles(void *meshliteContext, int meshId);
|
||||||
|
|
||||||
|
|
|
@ -640,6 +640,7 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
|
||||||
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
|
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
|
||||||
part["zMirrored"] = partIt.second.zMirrored ? "true" : "false";
|
part["zMirrored"] = partIt.second.zMirrored ? "true" : "false";
|
||||||
part["rounded"] = partIt.second.rounded ? "true" : "false";
|
part["rounded"] = partIt.second.rounded ? "true" : "false";
|
||||||
|
part["inverse"] = partIt.second.inverse ? "true" : "false";
|
||||||
if (partIt.second.hasColor)
|
if (partIt.second.hasColor)
|
||||||
part["color"] = partIt.second.color.name();
|
part["color"] = partIt.second.color.name();
|
||||||
if (partIt.second.deformThicknessAdjusted())
|
if (partIt.second.deformThicknessAdjusted())
|
||||||
|
@ -732,6 +733,7 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
|
||||||
part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored"));
|
part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored"));
|
||||||
part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored"));
|
part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored"));
|
||||||
part.rounded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "rounded"));
|
part.rounded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "rounded"));
|
||||||
|
part.inverse = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "inverse"));
|
||||||
const auto &colorIt = partKv.second.find("color");
|
const auto &colorIt = partKv.second.find("color");
|
||||||
if (colorIt != partKv.second.end()) {
|
if (colorIt != partKv.second.end()) {
|
||||||
part.color = QColor(colorIt->second);
|
part.color = QColor(colorIt->second);
|
||||||
|
@ -1210,6 +1212,20 @@ void SkeletonDocument::setPartVisibleState(QUuid partId, bool visible)
|
||||||
emit partVisibleStateChanged(partId);
|
emit partVisibleStateChanged(partId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setPartInverseState(QUuid partId, bool inverse)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (part->second.inverse == inverse)
|
||||||
|
return;
|
||||||
|
part->second.inverse = inverse;
|
||||||
|
emit partInverseStateChanged(partId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived)
|
void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived)
|
||||||
{
|
{
|
||||||
auto part = partMap.find(partId);
|
auto part = partMap.find(partId);
|
||||||
|
@ -1238,6 +1254,96 @@ void SkeletonDocument::setPartDisableState(QUuid partId, bool disabled)
|
||||||
emit skeletonChanged();
|
emit skeletonChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::movePartUp(QUuid partId)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::find(partIds.begin(), partIds.end(), partId);
|
||||||
|
if (it == partIds.end()) {
|
||||||
|
qDebug() << "Part not found in list:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = std::distance(partIds.begin(), it);
|
||||||
|
if (index == 0)
|
||||||
|
return;
|
||||||
|
std::swap(partIds[index - 1], partIds[index]);
|
||||||
|
emit partListChanged();
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::movePartDown(QUuid partId)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::find(partIds.begin(), partIds.end(), partId);
|
||||||
|
if (it == partIds.end()) {
|
||||||
|
qDebug() << "Part not found in list:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = std::distance(partIds.begin(), it);
|
||||||
|
if (index == (int)partIds.size() - 1)
|
||||||
|
return;
|
||||||
|
std::swap(partIds[index], partIds[index + 1]);
|
||||||
|
emit partListChanged();
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::movePartToTop(QUuid partId)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::find(partIds.begin(), partIds.end(), partId);
|
||||||
|
if (it == partIds.end()) {
|
||||||
|
qDebug() << "Part not found in list:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = std::distance(partIds.begin(), it);
|
||||||
|
if (index == 0)
|
||||||
|
return;
|
||||||
|
for (int i = index; i >= 1; i--)
|
||||||
|
std::swap(partIds[i - 1], partIds[i]);
|
||||||
|
emit partListChanged();
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::movePartToBottom(QUuid partId)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::find(partIds.begin(), partIds.end(), partId);
|
||||||
|
if (it == partIds.end()) {
|
||||||
|
qDebug() << "Part not found in list:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = std::distance(partIds.begin(), it);
|
||||||
|
if (index == (int)partIds.size() - 1)
|
||||||
|
return;
|
||||||
|
for (int i = index; i <= (int)partIds.size() - 2; i++)
|
||||||
|
std::swap(partIds[i], partIds[i + 1]);
|
||||||
|
emit partListChanged();
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void SkeletonDocument::settleOrigin()
|
void SkeletonDocument::settleOrigin()
|
||||||
{
|
{
|
||||||
if (originSettled())
|
if (originSettled())
|
||||||
|
|
|
@ -86,6 +86,7 @@ public:
|
||||||
bool rounded;
|
bool rounded;
|
||||||
QColor color;
|
QColor color;
|
||||||
bool hasColor;
|
bool hasColor;
|
||||||
|
bool inverse;
|
||||||
QImage preview;
|
QImage preview;
|
||||||
std::vector<QUuid> nodeIds;
|
std::vector<QUuid> nodeIds;
|
||||||
SkeletonPart(const QUuid &withId=QUuid()) :
|
SkeletonPart(const QUuid &withId=QUuid()) :
|
||||||
|
@ -99,7 +100,8 @@ public:
|
||||||
deformWidth(1.0),
|
deformWidth(1.0),
|
||||||
rounded(false),
|
rounded(false),
|
||||||
color(Theme::white),
|
color(Theme::white),
|
||||||
hasColor(false)
|
hasColor(false),
|
||||||
|
inverse(false)
|
||||||
{
|
{
|
||||||
id = withId.isNull() ? QUuid::createUuid() : withId;
|
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||||
}
|
}
|
||||||
|
@ -148,6 +150,7 @@ public:
|
||||||
rounded = other.rounded;
|
rounded = other.rounded;
|
||||||
color = other.color;
|
color = other.color;
|
||||||
hasColor = other.hasColor;
|
hasColor = other.hasColor;
|
||||||
|
inverse = other.inverse;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -217,6 +220,7 @@ signals:
|
||||||
void partDeformWidthChanged(QUuid partId);
|
void partDeformWidthChanged(QUuid partId);
|
||||||
void partRoundStateChanged(QUuid partId);
|
void partRoundStateChanged(QUuid partId);
|
||||||
void partColorStateChanged(QUuid partId);
|
void partColorStateChanged(QUuid partId);
|
||||||
|
void partInverseStateChanged(QUuid partId);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void originChanged();
|
void originChanged();
|
||||||
void xlockStateChanged();
|
void xlockStateChanged();
|
||||||
|
@ -314,6 +318,11 @@ public slots:
|
||||||
void setPartDeformWidth(QUuid partId, float width);
|
void setPartDeformWidth(QUuid partId, float width);
|
||||||
void setPartRoundState(QUuid partId, bool rounded);
|
void setPartRoundState(QUuid partId, bool rounded);
|
||||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||||
|
void setPartInverseState(QUuid partId, bool inverse);
|
||||||
|
void movePartUp(QUuid partId);
|
||||||
|
void movePartDown(QUuid partId);
|
||||||
|
void movePartToTop(QUuid partId);
|
||||||
|
void movePartToBottom(QUuid partId);
|
||||||
void saveSnapshot();
|
void saveSnapshot();
|
||||||
void undo();
|
void undo();
|
||||||
void redo();
|
void redo();
|
||||||
|
|
|
@ -123,6 +123,11 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
|
||||||
connect(this, &SkeletonPartWidget::setPartDeformWidth, m_document, &SkeletonDocument::setPartDeformWidth);
|
connect(this, &SkeletonPartWidget::setPartDeformWidth, m_document, &SkeletonDocument::setPartDeformWidth);
|
||||||
connect(this, &SkeletonPartWidget::setPartRoundState, m_document, &SkeletonDocument::setPartRoundState);
|
connect(this, &SkeletonPartWidget::setPartRoundState, m_document, &SkeletonDocument::setPartRoundState);
|
||||||
connect(this, &SkeletonPartWidget::setPartColorState, m_document, &SkeletonDocument::setPartColorState);
|
connect(this, &SkeletonPartWidget::setPartColorState, m_document, &SkeletonDocument::setPartColorState);
|
||||||
|
connect(this, &SkeletonPartWidget::setPartInverseState, m_document, &SkeletonDocument::setPartInverseState);
|
||||||
|
connect(this, &SkeletonPartWidget::movePartUp, m_document, &SkeletonDocument::movePartUp);
|
||||||
|
connect(this, &SkeletonPartWidget::movePartDown, m_document, &SkeletonDocument::movePartDown);
|
||||||
|
connect(this, &SkeletonPartWidget::movePartToTop, m_document, &SkeletonDocument::movePartToTop);
|
||||||
|
connect(this, &SkeletonPartWidget::movePartToBottom, m_document, &SkeletonDocument::movePartToBottom);
|
||||||
connect(this, &SkeletonPartWidget::checkPart, m_document, &SkeletonDocument::checkPart);
|
connect(this, &SkeletonPartWidget::checkPart, m_document, &SkeletonDocument::checkPart);
|
||||||
connect(this, &SkeletonPartWidget::enableBackgroundBlur, m_document, &SkeletonDocument::enableBackgroundBlur);
|
connect(this, &SkeletonPartWidget::enableBackgroundBlur, m_document, &SkeletonDocument::enableBackgroundBlur);
|
||||||
connect(this, &SkeletonPartWidget::disableBackgroundBlur, m_document, &SkeletonDocument::disableBackgroundBlur);
|
connect(this, &SkeletonPartWidget::disableBackgroundBlur, m_document, &SkeletonDocument::disableBackgroundBlur);
|
||||||
|
@ -206,6 +211,9 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
|
||||||
});
|
});
|
||||||
|
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(this, &SkeletonPartWidget::customContextMenuRequested, [=] {
|
||||||
|
emit checkPart(m_partId);
|
||||||
|
});
|
||||||
connect(this, &SkeletonPartWidget::customContextMenuRequested, this, &SkeletonPartWidget::showContextMenu);
|
connect(this, &SkeletonPartWidget::customContextMenuRequested, this, &SkeletonPartWidget::showContextMenu);
|
||||||
|
|
||||||
updateAllButtons();
|
updateAllButtons();
|
||||||
|
@ -227,8 +235,6 @@ void SkeletonPartWidget::updateAllButtons()
|
||||||
|
|
||||||
void SkeletonPartWidget::showContextMenu(const QPoint &pos)
|
void SkeletonPartWidget::showContextMenu(const QPoint &pos)
|
||||||
{
|
{
|
||||||
emit checkPart(m_partId);
|
|
||||||
|
|
||||||
QMenu contextMenu(this);
|
QMenu contextMenu(this);
|
||||||
|
|
||||||
const SkeletonPart *part = m_document->findPart(m_partId);
|
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||||
|
@ -274,6 +280,8 @@ void SkeletonPartWidget::showContextMenu(const QPoint &pos)
|
||||||
});
|
});
|
||||||
contextMenu.addAction(&hideAllPartsAction);
|
contextMenu.addAction(&hideAllPartsAction);
|
||||||
|
|
||||||
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction lockAllPartsAction(tr("Lock All Parts"), this);
|
QAction lockAllPartsAction(tr("Lock All Parts"), this);
|
||||||
connect(&lockAllPartsAction, &QAction::triggered, [=]() {
|
connect(&lockAllPartsAction, &QAction::triggered, [=]() {
|
||||||
for (const auto &it: m_document->partIds) {
|
for (const auto &it: m_document->partIds) {
|
||||||
|
@ -290,10 +298,51 @@ void SkeletonPartWidget::showContextMenu(const QPoint &pos)
|
||||||
});
|
});
|
||||||
contextMenu.addAction(&unlockAllPartsAction);
|
contextMenu.addAction(&unlockAllPartsAction);
|
||||||
|
|
||||||
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
|
QAction invertPartAction(tr("Invert Part"), this);
|
||||||
|
if (part && !part->inverse) {
|
||||||
|
connect(&invertPartAction, &QAction::triggered, [=]() {
|
||||||
|
emit setPartInverseState(m_partId, true);
|
||||||
|
});
|
||||||
|
contextMenu.addAction(&invertPartAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction cancelInverseAction(tr("Cancel Inverse"), this);
|
||||||
|
if (part && part->inverse) {
|
||||||
|
connect(&cancelInverseAction, &QAction::triggered, [=]() {
|
||||||
|
emit setPartInverseState(m_partId, false);
|
||||||
|
});
|
||||||
|
contextMenu.addAction(&cancelInverseAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction moveUpAction(tr("Move Up"), this);
|
||||||
|
connect(&moveUpAction, &QAction::triggered, [=]() {
|
||||||
|
emit movePartUp(m_partId);
|
||||||
|
});
|
||||||
|
contextMenu.addAction(&moveUpAction);
|
||||||
|
|
||||||
|
QAction moveDownAction(tr("Move Down"), this);
|
||||||
|
connect(&moveDownAction, &QAction::triggered, [=]() {
|
||||||
|
emit movePartDown(m_partId);
|
||||||
|
});
|
||||||
|
contextMenu.addAction(&moveDownAction);
|
||||||
|
|
||||||
|
QAction moveToTopAction(tr("Move To Top"), this);
|
||||||
|
connect(&moveToTopAction, &QAction::triggered, [=]() {
|
||||||
|
emit movePartToTop(m_partId);
|
||||||
|
});
|
||||||
|
contextMenu.addAction(&moveToTopAction);
|
||||||
|
|
||||||
|
QAction moveToBottomAction(tr("Move To Bottom"), this);
|
||||||
|
connect(&moveToBottomAction, &QAction::triggered, [=]() {
|
||||||
|
emit movePartToBottom(m_partId);
|
||||||
|
});
|
||||||
|
contextMenu.addAction(&moveToBottomAction);
|
||||||
|
|
||||||
contextMenu.exec(mapToGlobal(pos));
|
contextMenu.exec(mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SkeletonPartWidget::updateCheckedState(bool checked)
|
void SkeletonPartWidget::updateCheckedState(bool checked)
|
||||||
{
|
{
|
||||||
if (checked)
|
if (checked)
|
||||||
|
|
|
@ -21,6 +21,11 @@ signals:
|
||||||
void setPartDeformWidth(QUuid partId, float width);
|
void setPartDeformWidth(QUuid partId, float width);
|
||||||
void setPartRoundState(QUuid partId, bool rounded);
|
void setPartRoundState(QUuid partId, bool rounded);
|
||||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||||
|
void setPartInverseState(QUuid partId, bool inverse);
|
||||||
|
void movePartUp(QUuid partId);
|
||||||
|
void movePartDown(QUuid partId);
|
||||||
|
void movePartToTop(QUuid partId);
|
||||||
|
void movePartToBottom(QUuid partId);
|
||||||
void checkPart(QUuid partId);
|
void checkPart(QUuid partId);
|
||||||
void groupOperationAdded();
|
void groupOperationAdded();
|
||||||
void enableBackgroundBlur();
|
void enableBackgroundBlur();
|
||||||
|
|
Loading…
Reference in New Issue