Add mesh smooth slider
This feature could be useful for smooth one component and soften the seam of combination, This commit also include a crash fix about incorrect use of CGAL.master
parent
87b0f70460
commit
3c2d4f7480
|
@ -6,15 +6,14 @@ FloatNumberWidget::FloatNumberWidget(QWidget *parent) :
|
||||||
{
|
{
|
||||||
m_slider = new QSlider(Qt::Horizontal, this);
|
m_slider = new QSlider(Qt::Horizontal, this);
|
||||||
m_slider->setRange(0, 100);
|
m_slider->setRange(0, 100);
|
||||||
|
m_slider->setFixedWidth(120);
|
||||||
|
|
||||||
m_label = new QLabel(this);
|
m_label = new QLabel(this);
|
||||||
m_label->setAlignment(Qt::AlignCenter);
|
m_label->setAlignment(Qt::AlignLeft);
|
||||||
m_label->setNum(0);
|
|
||||||
m_label->setFixedWidth(30);
|
|
||||||
|
|
||||||
connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) {
|
connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) {
|
||||||
float fvalue = value / 100.0;
|
float fvalue = value / 100.0;
|
||||||
m_label->setText(QString().sprintf("%.2f", fvalue));
|
updateValueLabel(fvalue);
|
||||||
emit valueChanged(fvalue);
|
emit valueChanged(fvalue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,6 +23,21 @@ FloatNumberWidget::FloatNumberWidget(QWidget *parent) :
|
||||||
popupLayout->addWidget(m_label);
|
popupLayout->addWidget(m_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FloatNumberWidget::updateValueLabel(float value)
|
||||||
|
{
|
||||||
|
QString valueString = QString().sprintf("%.2f", value);
|
||||||
|
if (m_itemName.isEmpty())
|
||||||
|
m_label->setText(valueString);
|
||||||
|
else
|
||||||
|
m_label->setText(m_itemName + ": " + valueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatNumberWidget::setItemName(const QString &name)
|
||||||
|
{
|
||||||
|
m_itemName = name;
|
||||||
|
updateValueLabel(value());
|
||||||
|
}
|
||||||
|
|
||||||
void FloatNumberWidget::setRange(float min, float max)
|
void FloatNumberWidget::setRange(float min, float max)
|
||||||
{
|
{
|
||||||
m_slider->setRange(min * 100, max * 100);
|
m_slider->setRange(min * 100, max * 100);
|
||||||
|
|
|
@ -12,6 +12,7 @@ public:
|
||||||
explicit FloatNumberWidget(QWidget *parent = nullptr);
|
explicit FloatNumberWidget(QWidget *parent = nullptr);
|
||||||
void setRange(float min, float max);
|
void setRange(float min, float max);
|
||||||
float value() const;
|
float value() const;
|
||||||
|
void setItemName(const QString &name);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void increaseValue();
|
void increaseValue();
|
||||||
|
@ -21,9 +22,13 @@ public slots:
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(float value);
|
void valueChanged(float value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateValueLabel(float value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *m_label = nullptr;
|
QLabel *m_label = nullptr;
|
||||||
QSlider *m_slider = nullptr;
|
QSlider *m_slider = nullptr;
|
||||||
|
QString m_itemName;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,7 +28,7 @@ int main(int argc, char ** argv)
|
||||||
darkPalette.setColor(QPalette::ButtonText, Theme::white);
|
darkPalette.setColor(QPalette::ButtonText, Theme::white);
|
||||||
darkPalette.setColor(QPalette::BrightText, Theme::red);
|
darkPalette.setColor(QPalette::BrightText, Theme::red);
|
||||||
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||||
darkPalette.setColor(QPalette::Highlight, Theme::white);
|
darkPalette.setColor(QPalette::Highlight, Theme::red);
|
||||||
darkPalette.setColor(QPalette::HighlightedText, Theme::black);
|
darkPalette.setColor(QPalette::HighlightedText, Theme::black);
|
||||||
qApp->setPalette(darkPalette);
|
qApp->setPalette(darkPalette);
|
||||||
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }");
|
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }");
|
||||||
|
|
|
@ -215,12 +215,12 @@ void *MeshGenerator::combinePartMesh(QString partId)
|
||||||
if (MeshGenerator::m_enableDebug)
|
if (MeshGenerator::m_enableDebug)
|
||||||
meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1);
|
meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1);
|
||||||
|
|
||||||
QString mirroredPartId;
|
//QString mirroredPartId;
|
||||||
QUuid mirroredPartIdNotAsString;
|
//QUuid mirroredPartIdNotAsString;
|
||||||
if (xMirrored) {
|
//if (xMirrored) {
|
||||||
mirroredPartIdNotAsString = QUuid().createUuid();
|
// mirroredPartIdNotAsString = QUuid().createUuid();
|
||||||
mirroredPartId = mirroredPartIdNotAsString.toString();
|
// mirroredPartId = mirroredPartIdNotAsString.toString();
|
||||||
}
|
//}
|
||||||
|
|
||||||
std::map<QString, int> nodeToBmeshIdMap;
|
std::map<QString, int> nodeToBmeshIdMap;
|
||||||
std::map<int, QUuid> bmeshToNodeIdMap;
|
std::map<int, QUuid> bmeshToNodeIdMap;
|
||||||
|
@ -252,12 +252,11 @@ void *MeshGenerator::combinePartMesh(QString partId)
|
||||||
bmeshNode.nodeId = QUuid(nodeId);
|
bmeshNode.nodeId = QUuid(nodeId);
|
||||||
bmeshNode.color = partColor;
|
bmeshNode.color = partColor;
|
||||||
cacheBmeshNodes.push_back(bmeshNode);
|
cacheBmeshNodes.push_back(bmeshNode);
|
||||||
|
//if (xMirrored) {
|
||||||
if (xMirrored) {
|
// bmeshNode.partId = mirroredPartId;
|
||||||
bmeshNode.partId = mirroredPartId;
|
// bmeshNode.origin.setX(-x);
|
||||||
bmeshNode.origin.setX(-x);
|
// cacheBmeshNodes.push_back(bmeshNode);
|
||||||
cacheBmeshNodes.push_back(bmeshNode);
|
//}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &edgeId: m_partEdgeIds[partId]) {
|
for (const auto &edgeId: m_partEdgeIds[partId]) {
|
||||||
|
@ -300,7 +299,7 @@ void *MeshGenerator::combinePartMesh(QString partId)
|
||||||
if (nullptr != resultMesh) {
|
if (nullptr != resultMesh) {
|
||||||
if (xMirrored) {
|
if (xMirrored) {
|
||||||
int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0);
|
int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0);
|
||||||
loadVertexSources(m_meshliteContext, xMirroredMeshId, mirroredPartIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
|
loadVertexSources(m_meshliteContext, xMirroredMeshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
|
||||||
void *mirroredMesh = nullptr;
|
void *mirroredMesh = nullptr;
|
||||||
if (wrapped)
|
if (wrapped)
|
||||||
mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId);
|
mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId);
|
||||||
|
@ -411,41 +410,122 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
bool smoothSeam = false;
|
||||||
if ("partId" == linkDataType) {
|
float smoothSeamFactor = 0.0;
|
||||||
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
QString smoothSeamString = valueOfKeyInMapOrEmpty(*component, "smoothSeam");
|
||||||
return combinePartMesh(partId);
|
if (!smoothSeamString.isEmpty()) {
|
||||||
|
smoothSeam = true;
|
||||||
|
smoothSeamFactor = smoothSeamString.toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool smoothAll = false;
|
||||||
|
float smoothAllFactor = 0.0;
|
||||||
|
QString smoothAllString = valueOfKeyInMapOrEmpty(*component, "smoothAll");
|
||||||
|
if (!smoothAllString.isEmpty()) {
|
||||||
|
smoothAll = true;
|
||||||
|
smoothAllFactor = smoothAllString.toFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *resultMesh = nullptr;
|
void *resultMesh = nullptr;
|
||||||
|
|
||||||
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
PositionMap<bool> positionsBeforeCombination;
|
||||||
if (childId.isEmpty())
|
auto &verticesSources = m_cacheContext->componentVerticesSources[componentId];
|
||||||
continue;
|
verticesSources.map().clear();
|
||||||
bool childInverse = false;
|
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
||||||
void *childCombinedMesh = combineComponentMesh(childId, &childInverse);
|
if ("partId" == linkDataType) {
|
||||||
if (nullptr == childCombinedMesh)
|
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
||||||
continue;
|
resultMesh = combinePartMesh(partId);
|
||||||
if (nullptr == resultMesh) {
|
for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) {
|
||||||
if (childInverse) {
|
verticesSources.addPosition(bmeshVertex.position.x(), bmeshVertex.position.y(), bmeshVertex.position.z(),
|
||||||
deleteCombinableMesh(childCombinedMesh);
|
bmeshVertex);
|
||||||
} else {
|
}
|
||||||
resultMesh = childCombinedMesh;
|
} else {
|
||||||
|
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
||||||
|
if (childId.isEmpty())
|
||||||
|
continue;
|
||||||
|
bool childInverse = false;
|
||||||
|
void *childCombinedMesh = combineComponentMesh(childId, &childInverse);
|
||||||
|
if (smoothSeam) {
|
||||||
|
for (const auto &positionIt: m_cacheContext->componentPositions[childId]) {
|
||||||
|
positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
for (const auto &verticesSourceIt: m_cacheContext->componentVerticesSources[childId].map()) {
|
||||||
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
verticesSources.map()[verticesSourceIt.first] = verticesSourceIt.second;
|
||||||
deleteCombinableMesh(childCombinedMesh);
|
}
|
||||||
if (nullptr != newResultMesh) {
|
if (nullptr == childCombinedMesh)
|
||||||
deleteCombinableMesh(resultMesh);
|
continue;
|
||||||
resultMesh = newResultMesh;
|
if (nullptr == resultMesh) {
|
||||||
|
if (childInverse) {
|
||||||
|
deleteCombinableMesh(childCombinedMesh);
|
||||||
|
} else {
|
||||||
|
resultMesh = childCombinedMesh;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
||||||
|
deleteCombinableMesh(childCombinedMesh);
|
||||||
|
if (nullptr != newResultMesh) {
|
||||||
|
deleteCombinableMesh(resultMesh);
|
||||||
|
resultMesh = newResultMesh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!componentIdNotAsString.isNull()) {
|
if (nullptr != resultMesh) {
|
||||||
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
|
if (smoothSeam || smoothAll) {
|
||||||
|
int meshIdForSmooth = convertFromCombinableMesh(m_meshliteContext, resultMesh);
|
||||||
|
std::vector<QVector3D> positionsBeforeSmooth;
|
||||||
|
loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsBeforeSmooth);
|
||||||
|
|
||||||
|
if (!positionsBeforeSmooth.empty()) {
|
||||||
|
|
||||||
|
if (smoothSeam) {
|
||||||
|
int *seamVerticesIndicies = new int[positionsBeforeSmooth.size()];
|
||||||
|
int seamVerticesNum = 0;
|
||||||
|
for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) {
|
||||||
|
const auto &oldPosition = positionsBeforeSmooth[vertexIndex];
|
||||||
|
if (!positionsBeforeCombination.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z())) {
|
||||||
|
seamVerticesIndicies[seamVerticesNum++] = vertexIndex + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (seamVerticesNum > 0) {
|
||||||
|
qDebug() << "smoothSeamFactor:" << smoothSeamFactor << "seamVerticesIndicies.size():" << seamVerticesNum;
|
||||||
|
meshlite_smooth_vertices(m_meshliteContext, meshIdForSmooth, smoothSeamFactor, seamVerticesIndicies, seamVerticesNum);
|
||||||
|
}
|
||||||
|
delete[] seamVerticesIndicies;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smoothAll) {
|
||||||
|
meshlite_smooth(m_meshliteContext, meshIdForSmooth, smoothAllFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QVector3D> positionsAfterSmooth;
|
||||||
|
loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsAfterSmooth);
|
||||||
|
assert(positionsBeforeSmooth.size() == positionsAfterSmooth.size());
|
||||||
|
|
||||||
|
for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) {
|
||||||
|
const auto &oldPosition = positionsBeforeSmooth[vertexIndex];
|
||||||
|
const auto &smoothedPosition = positionsAfterSmooth[vertexIndex];
|
||||||
|
BmeshVertex source;
|
||||||
|
if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) {
|
||||||
|
verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z());
|
||||||
|
source.position = smoothedPosition;
|
||||||
|
verticesSources.addPosition(smoothedPosition.x(), smoothedPosition.y(), smoothedPosition.z(), source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCombinableMesh(resultMesh);
|
||||||
|
resultMesh = convertToCombinableMesh(m_meshliteContext, meshIdForSmooth);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
|
||||||
|
auto &cachedComponentPositions = m_cacheContext->componentPositions[componentId];
|
||||||
|
cachedComponentPositions.clear();
|
||||||
|
loadCombinableMeshVerticesPositions(resultMesh, cachedComponentPositions);
|
||||||
|
|
||||||
return resultMesh;
|
return resultMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,11 +568,27 @@ void MeshGenerator::process()
|
||||||
}
|
}
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
|
for (auto it = m_cacheContext->componentPositions.begin(); it != m_cacheContext->componentPositions.end(); ) {
|
||||||
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||||
|
it = m_cacheContext->componentPositions.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
for (auto it = m_cacheContext->componentVerticesSources.begin(); it != m_cacheContext->componentVerticesSources.end(); ) {
|
||||||
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||||
|
it = m_cacheContext->componentVerticesSources.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collectParts();
|
collectParts();
|
||||||
checkDirtyFlags();
|
checkDirtyFlags();
|
||||||
|
|
||||||
|
m_dirtyComponentIds.insert(QUuid().toString());
|
||||||
|
|
||||||
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
|
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
|
||||||
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
|
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
|
||||||
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
|
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
|
||||||
|
@ -506,9 +602,8 @@ void MeshGenerator::process()
|
||||||
deleteCombinableMesh(combinedMesh);
|
deleteCombinableMesh(combinedMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &bmeshVertices: m_cacheContext->partBmeshVertices) {
|
for (const auto &verticesSourcesIt: m_cacheContext->componentVerticesSources[QUuid().toString()].map()) {
|
||||||
m_meshResultContext->bmeshVertices.insert(m_meshResultContext->bmeshVertices.end(),
|
m_meshResultContext->bmeshVertices.push_back(verticesSourcesIt.second);
|
||||||
bmeshVertices.second.begin(), bmeshVertices.second.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) {
|
for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "meshloader.h"
|
#include "meshloader.h"
|
||||||
#include "modelofflinerender.h"
|
#include "modelofflinerender.h"
|
||||||
#include "meshresultcontext.h"
|
#include "meshresultcontext.h"
|
||||||
|
#include "positionmap.h"
|
||||||
|
|
||||||
class GeneratedCacheContext
|
class GeneratedCacheContext
|
||||||
{
|
{
|
||||||
|
@ -19,6 +20,8 @@ public:
|
||||||
std::map<QString, std::vector<BmeshVertex>> partBmeshVertices;
|
std::map<QString, std::vector<BmeshVertex>> partBmeshVertices;
|
||||||
std::map<QString, std::vector<BmeshNode>> partBmeshNodes;
|
std::map<QString, std::vector<BmeshNode>> partBmeshNodes;
|
||||||
std::map<QString, void *> componentCombinableMeshs;
|
std::map<QString, void *> componentCombinableMeshs;
|
||||||
|
std::map<QString, std::vector<QVector3D>> componentPositions;
|
||||||
|
std::map<QString, PositionMap<BmeshVertex>> componentVerticesSources;
|
||||||
void updateComponentCombinableMesh(QString componentId, void *mesh);
|
void updateComponentCombinableMesh(QString componentId, void *mesh);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +32,6 @@ public:
|
||||||
MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread);
|
MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread);
|
||||||
~MeshGenerator();
|
~MeshGenerator();
|
||||||
void setSharedContextWidget(QOpenGLWidget *widget);
|
void setSharedContextWidget(QOpenGLWidget *widget);
|
||||||
//void addPreviewRequirement();
|
|
||||||
void addPartPreviewRequirement(const QString &partId);
|
void addPartPreviewRequirement(const QString &partId);
|
||||||
void setGeneratedCacheContext(GeneratedCacheContext *cacheContext);
|
void setGeneratedCacheContext(GeneratedCacheContext *cacheContext);
|
||||||
MeshLoader *takeResultMesh();
|
MeshLoader *takeResultMesh();
|
||||||
|
@ -59,6 +61,7 @@ private:
|
||||||
std::map<QString, std::set<QString>> m_partEdgeIds;
|
std::map<QString, std::set<QString>> m_partEdgeIds;
|
||||||
std::set<QString> m_dirtyComponentIds;
|
std::set<QString> m_dirtyComponentIds;
|
||||||
std::set<QString> m_dirtyPartIds;
|
std::set<QString> m_dirtyPartIds;
|
||||||
|
PositionMap<std::pair<QString, int>> m_bmeshPartVerticesIndiciesMap;
|
||||||
private:
|
private:
|
||||||
static bool m_enableDebug;
|
static bool m_enableDebug;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -31,16 +31,13 @@ typedef CGAL::Surface_mesh<ExactKernel::Point_3> ExactMesh;
|
||||||
typedef CGAL::Surface_mesh<SimpleKernel::Point_3> SimpleMesh;
|
typedef CGAL::Surface_mesh<SimpleKernel::Point_3> SimpleMesh;
|
||||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||||
|
|
||||||
template <class Kernel>
|
void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QVector3D> &positions)
|
||||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
|
|
||||||
{
|
{
|
||||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>;
|
int vertexCount = meshlite_get_vertex_count(meshliteContext, meshId);
|
||||||
int vertexCount = meshlite_get_vertex_count(meshlite, meshId);
|
|
||||||
float *vertexPositions = new float[vertexCount * 3];
|
float *vertexPositions = new float[vertexCount * 3];
|
||||||
int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3);
|
int vertexArrayLen = meshlite_get_vertex_position_array(meshliteContext, meshId, vertexPositions, vertexCount * 3);
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
assert(vertexArrayLen == vertexCount * 3);
|
assert(vertexArrayLen == vertexCount * 3);
|
||||||
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> vertices;
|
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
float x = vertexPositions[offset + 0];
|
float x = vertexPositions[offset + 0];
|
||||||
float y = vertexPositions[offset + 1];
|
float y = vertexPositions[offset + 1];
|
||||||
|
@ -51,7 +48,34 @@ typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(
|
||||||
y = 0;
|
y = 0;
|
||||||
if (std::isnan(z) || std::isinf(z))
|
if (std::isnan(z) || std::isinf(z))
|
||||||
z = 0;
|
z = 0;
|
||||||
vertices.push_back(mesh->add_vertex(typename Kernel::Point_3(x, y, z)));
|
positions.push_back(QVector3D(x, y, z));
|
||||||
|
offset += 3;
|
||||||
|
}
|
||||||
|
delete[] vertexPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Kernel>
|
||||||
|
typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
|
||||||
|
{
|
||||||
|
typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>;
|
||||||
|
int vertexCount = meshlite_get_vertex_count(meshlite, meshId);
|
||||||
|
float *vertexPositions = new float[vertexCount * 3];
|
||||||
|
int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3);
|
||||||
|
int offset = 0;
|
||||||
|
assert(vertexArrayLen == vertexCount * 3);
|
||||||
|
std::vector<QVector3D> oldPositions;
|
||||||
|
std::map<int, typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> oldToNewMap;
|
||||||
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
|
float x = vertexPositions[offset + 0];
|
||||||
|
float y = vertexPositions[offset + 1];
|
||||||
|
float z = vertexPositions[offset + 2];
|
||||||
|
if (std::isnan(x) || std::isinf(x))
|
||||||
|
x = 0;
|
||||||
|
if (std::isnan(y) || std::isinf(y))
|
||||||
|
y = 0;
|
||||||
|
if (std::isnan(z) || std::isinf(z))
|
||||||
|
z = 0;
|
||||||
|
oldPositions.push_back(QVector3D(x, y, z));
|
||||||
offset += 3;
|
offset += 3;
|
||||||
}
|
}
|
||||||
int faceCount = meshlite_get_face_count(meshlite, meshId);
|
int faceCount = meshlite_get_face_count(meshlite, meshId);
|
||||||
|
@ -62,26 +86,51 @@ typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(
|
||||||
while (i < filledLength) {
|
while (i < filledLength) {
|
||||||
int num = faceVertexNumAndIndices[i++];
|
int num = faceVertexNumAndIndices[i++];
|
||||||
assert(num > 0 && num <= MAX_VERTICES_PER_FACE);
|
assert(num > 0 && num <= MAX_VERTICES_PER_FACE);
|
||||||
|
if (num < 3)
|
||||||
|
continue;
|
||||||
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> faceVertexIndices;
|
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> faceVertexIndices;
|
||||||
for (int j = 0; j < num; j++) {
|
for (int j = 0; j < num; j++) {
|
||||||
int index = faceVertexNumAndIndices[i++];
|
int index = faceVertexNumAndIndices[i++];
|
||||||
assert(index >= 0 && index < vertexCount);
|
assert(index >= 0 && index < vertexCount);
|
||||||
faceVertexIndices.push_back(vertices[index]);
|
if (oldToNewMap.find(index) == oldToNewMap.end()) {
|
||||||
}
|
const auto &pos = oldPositions[index];
|
||||||
if (faceVertexIndices.size() >= 3) {
|
oldToNewMap[index] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z()));
|
||||||
mesh->add_face(faceVertexIndices);
|
}
|
||||||
addedFaceCount++;
|
faceVertexIndices.push_back(oldToNewMap[index]);
|
||||||
}
|
}
|
||||||
|
mesh->add_face(faceVertexIndices);
|
||||||
|
addedFaceCount++;
|
||||||
}
|
}
|
||||||
delete[] faceVertexNumAndIndices;
|
delete[] faceVertexNumAndIndices;
|
||||||
delete[] vertexPositions;
|
delete[] vertexPositions;
|
||||||
if (0 == addedFaceCount) {
|
if (addedFaceCount < 3) {
|
||||||
delete mesh;
|
delete mesh;
|
||||||
mesh = nullptr;
|
mesh = nullptr;
|
||||||
}
|
}
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadCombinableMeshVerticesPositions(void *mesh, std::vector<QVector3D> &positions)
|
||||||
|
{
|
||||||
|
ExactMesh *exactMesh = (ExactMesh *)mesh;
|
||||||
|
if (nullptr == exactMesh)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto vertexIt = exactMesh->vertices_begin(); vertexIt != exactMesh->vertices_end(); vertexIt++) {
|
||||||
|
auto point = exactMesh->point(*vertexIt);
|
||||||
|
float x = (float)CGAL::to_double(point.x());
|
||||||
|
float y = (float)CGAL::to_double(point.y());
|
||||||
|
float z = (float)CGAL::to_double(point.z());
|
||||||
|
if (std::isnan(x) || std::isinf(x))
|
||||||
|
x = 0;
|
||||||
|
if (std::isnan(y) || std::isinf(y))
|
||||||
|
y = 0;
|
||||||
|
if (std::isnan(z) || std::isinf(z))
|
||||||
|
z = 0;
|
||||||
|
positions.push_back(QVector3D(x, y, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://doc.cgal.org/latest/Surface_mesh/index.html#circulators_example
|
// https://doc.cgal.org/latest/Surface_mesh/index.html#circulators_example
|
||||||
// https://www.cgal.org/FAQ.html
|
// https://www.cgal.org/FAQ.html
|
||||||
template <class Kernel>
|
template <class Kernel>
|
||||||
|
@ -256,7 +305,6 @@ ExactMesh *diffCgalMeshs(ExactMesh *first, ExactMesh *second)
|
||||||
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, 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::CONTINUE);
|
|
||||||
std::vector<ExactMesh *> externalMeshs;
|
std::vector<ExactMesh *> externalMeshs;
|
||||||
for (size_t i = 0; i < meshIds.size(); i++) {
|
for (size_t i = 0; i < meshIds.size(); i++) {
|
||||||
int triangledMeshId = meshlite_triangulate(meshliteContext, meshIds[i]);
|
int triangledMeshId = meshlite_triangulate(meshliteContext, meshIds[i]);
|
||||||
|
@ -331,7 +379,6 @@ int subdivMesh(void *meshliteContext, int meshId, int *errorCount)
|
||||||
int triangulatedMeshId = meshlite_triangulate(meshliteContext, meshId);
|
int triangulatedMeshId = meshlite_triangulate(meshliteContext, meshId);
|
||||||
if (0 == meshlite_is_triangulated_manifold(meshliteContext, triangulatedMeshId)) {
|
if (0 == meshlite_is_triangulated_manifold(meshliteContext, triangulatedMeshId)) {
|
||||||
#if USE_CGAL == 1
|
#if USE_CGAL == 1
|
||||||
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
|
|
||||||
int subdiviedMeshId = 0;
|
int subdiviedMeshId = 0;
|
||||||
SimpleMesh *simpleMesh = nullptr;
|
SimpleMesh *simpleMesh = nullptr;
|
||||||
try {
|
try {
|
||||||
|
@ -359,7 +406,7 @@ int fixMeshHoles(void *meshliteContext, int meshId)
|
||||||
|
|
||||||
void initMeshUtils()
|
void initMeshUtils()
|
||||||
{
|
{
|
||||||
CGAL::set_error_behaviour(CGAL::CONTINUE);
|
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *convertToCombinableMesh(void *meshliteContext, int meshId)
|
void *convertToCombinableMesh(void *meshliteContext, int meshId)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define MESH_UTIL_H
|
#define MESH_UTIL_H
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
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, const std::set<int> &inverseIds, int *errorCount=0);
|
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount=0);
|
||||||
|
@ -16,5 +17,7 @@ int convertFromCombinableMesh(void *meshliteContext, void *mesh);
|
||||||
void deleteCombinableMesh(void *mesh);
|
void deleteCombinableMesh(void *mesh);
|
||||||
void *cloneCombinableMesh(void *mesh);
|
void *cloneCombinableMesh(void *mesh);
|
||||||
void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId);
|
void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId);
|
||||||
|
void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QVector3D> &positions);
|
||||||
|
void loadCombinableMeshVerticesPositions(void *mesh, std::vector<QVector3D> &positions);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -50,7 +50,7 @@ public:
|
||||||
m_map[makeKey(x, y, z)] = data;
|
m_map[makeKey(x, y, z)] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool findPosition(float x, float y, float z, T *data)
|
bool findPosition(float x, float y, float z, T *data = nullptr)
|
||||||
{
|
{
|
||||||
const auto &result = m_map.find(makeKey(x, y, z));
|
const auto &result = m_map.find(makeKey(x, y, z));
|
||||||
if (result == m_map.end())
|
if (result == m_map.end())
|
||||||
|
@ -60,6 +60,16 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void removePosition(float x, float y, float z)
|
||||||
|
{
|
||||||
|
m_map.erase(makeKey(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<PositionMapKey, T> &map()
|
||||||
|
{
|
||||||
|
return m_map;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<PositionMapKey, T> m_map;
|
std::map<PositionMapKey, T> m_map;
|
||||||
int m_intGridSize;
|
int m_intGridSize;
|
||||||
|
|
|
@ -725,6 +725,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
|
||||||
component["expanded"] = componentIt.second.expanded ? "true" : "false";
|
component["expanded"] = componentIt.second.expanded ? "true" : "false";
|
||||||
component["inverse"] = componentIt.second.inverse ? "true" : "false";
|
component["inverse"] = componentIt.second.inverse ? "true" : "false";
|
||||||
component["dirty"] = componentIt.second.dirty ? "true" : "false";
|
component["dirty"] = componentIt.second.dirty ? "true" : "false";
|
||||||
|
if (componentIt.second.smoothAllAdjusted())
|
||||||
|
component["smoothAll"] = QString::number(componentIt.second.smoothAll);
|
||||||
|
if (componentIt.second.smoothSeamAdjusted())
|
||||||
|
component["smoothSeam"] = QString::number(componentIt.second.smoothSeam);
|
||||||
QStringList childIdList;
|
QStringList childIdList;
|
||||||
for (const auto &childId: componentIt.second.childrenIds) {
|
for (const auto &childId: componentIt.second.childrenIds) {
|
||||||
childIdList.append(childId.toString());
|
childIdList.append(childId.toString());
|
||||||
|
@ -862,6 +866,12 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
|
||||||
component.name = valueOfKeyInMapOrEmpty(componentKv.second, "name");
|
component.name = valueOfKeyInMapOrEmpty(componentKv.second, "name");
|
||||||
component.expanded = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "expanded"));
|
component.expanded = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "expanded"));
|
||||||
component.inverse = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "inverse"));
|
component.inverse = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "inverse"));
|
||||||
|
const auto &smoothAllIt = componentKv.second.find("smoothAll");
|
||||||
|
if (smoothAllIt != componentKv.second.end())
|
||||||
|
component.setSmoothAll(smoothAllIt->second.toFloat());
|
||||||
|
const auto &smoothSeamIt = componentKv.second.find("smoothSeam");
|
||||||
|
if (smoothSeamIt != componentKv.second.end())
|
||||||
|
component.setSmoothSeam(smoothSeamIt->second.toFloat());
|
||||||
qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
|
qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
|
||||||
if ("partId" == linkDataType) {
|
if ("partId" == linkDataType) {
|
||||||
QUuid partId = oldNewIdMap[QUuid(linkData)];
|
QUuid partId = oldNewIdMap[QUuid(linkData)];
|
||||||
|
@ -1284,6 +1294,32 @@ void SkeletonDocument::setComponentInverseState(QUuid componentId, bool inverse)
|
||||||
emit skeletonChanged();
|
emit skeletonChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setComponentSmoothAll(QUuid componentId, float toSmoothAll)
|
||||||
|
{
|
||||||
|
auto component = componentMap.find(componentId);
|
||||||
|
if (component == componentMap.end()) {
|
||||||
|
qDebug() << "Component not found:" << componentId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
component->second.setSmoothAll(toSmoothAll);
|
||||||
|
component->second.dirty = true;
|
||||||
|
emit componentSmoothAllChanged(componentId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setComponentSmoothSeam(QUuid componentId, float toSmoothSeam)
|
||||||
|
{
|
||||||
|
auto component = componentMap.find(componentId);
|
||||||
|
if (component == componentMap.end()) {
|
||||||
|
qDebug() << "Component not found:" << componentId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
component->second.setSmoothSeam(toSmoothSeam);
|
||||||
|
component->second.dirty = true;
|
||||||
|
emit componentSmoothSeamChanged(componentId);
|
||||||
|
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);
|
||||||
|
|
|
@ -199,6 +199,8 @@ public:
|
||||||
bool expanded = true;
|
bool expanded = true;
|
||||||
bool inverse = false;
|
bool inverse = false;
|
||||||
bool dirty = true;
|
bool dirty = true;
|
||||||
|
float smoothAll = 0.0;
|
||||||
|
float smoothSeam = 0.0;
|
||||||
std::vector<QUuid> childrenIds;
|
std::vector<QUuid> childrenIds;
|
||||||
QString linkData() const
|
QString linkData() const
|
||||||
{
|
{
|
||||||
|
@ -290,6 +292,34 @@ public:
|
||||||
for (int i = index; i <= (int)childrenIds.size() - 2; i++)
|
for (int i = index; i <= (int)childrenIds.size() - 2; i++)
|
||||||
std::swap(childrenIds[i], childrenIds[i + 1]);
|
std::swap(childrenIds[i], childrenIds[i + 1]);
|
||||||
}
|
}
|
||||||
|
void setSmoothAll(float toSmoothAll)
|
||||||
|
{
|
||||||
|
if (toSmoothAll < 0)
|
||||||
|
toSmoothAll = 0;
|
||||||
|
else if (toSmoothAll > 1)
|
||||||
|
toSmoothAll = 1;
|
||||||
|
smoothAll = toSmoothAll;
|
||||||
|
}
|
||||||
|
void setSmoothSeam(float toSmoothSeam)
|
||||||
|
{
|
||||||
|
if (toSmoothSeam < 0)
|
||||||
|
toSmoothSeam = 0;
|
||||||
|
else if (toSmoothSeam > 1)
|
||||||
|
toSmoothSeam = 1;
|
||||||
|
smoothSeam = toSmoothSeam;
|
||||||
|
}
|
||||||
|
bool smoothAllAdjusted() const
|
||||||
|
{
|
||||||
|
return fabs(smoothAll - 0.0) >= 0.01;
|
||||||
|
}
|
||||||
|
bool smoothSeamAdjusted() const
|
||||||
|
{
|
||||||
|
return fabs(smoothSeam - 0.0) >= 0.01;
|
||||||
|
}
|
||||||
|
bool smoothAdjusted() const
|
||||||
|
{
|
||||||
|
return smoothAllAdjusted() || smoothSeamAdjusted();
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::set<QUuid> m_childrenIdSet;
|
std::set<QUuid> m_childrenIdSet;
|
||||||
};
|
};
|
||||||
|
@ -307,6 +337,8 @@ signals:
|
||||||
void componentRemoved(QUuid componentId);
|
void componentRemoved(QUuid componentId);
|
||||||
void componentAdded(QUuid componentId);
|
void componentAdded(QUuid componentId);
|
||||||
void componentExpandStateChanged(QUuid componentId);
|
void componentExpandStateChanged(QUuid componentId);
|
||||||
|
void componentSmoothAllChanged(QUuid componentId);
|
||||||
|
void componentSmoothSeamChanged(QUuid componentId);
|
||||||
void nodeRemoved(QUuid nodeId);
|
void nodeRemoved(QUuid nodeId);
|
||||||
void edgeRemoved(QUuid edgeId);
|
void edgeRemoved(QUuid edgeId);
|
||||||
void nodeRadiusChanged(QUuid nodeId);
|
void nodeRadiusChanged(QUuid nodeId);
|
||||||
|
@ -442,6 +474,8 @@ public slots:
|
||||||
void setCurrentCanvasComponentId(QUuid componentId);
|
void setCurrentCanvasComponentId(QUuid componentId);
|
||||||
void createNewComponentAndMoveThisIn(QUuid componentId);
|
void createNewComponentAndMoveThisIn(QUuid componentId);
|
||||||
void setComponentExpandState(QUuid componentId, bool expanded);
|
void setComponentExpandState(QUuid componentId, bool expanded);
|
||||||
|
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
||||||
|
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
||||||
void hideOtherComponents(QUuid componentId);
|
void hideOtherComponents(QUuid componentId);
|
||||||
void lockOtherComponents(QUuid componentId);
|
void lockOtherComponents(QUuid componentId);
|
||||||
void hideAllComponents();
|
void hideAllComponents();
|
||||||
|
|
|
@ -596,6 +596,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
||||||
connect(partTreeWidget, &SkeletonPartTreeWidget::createNewComponentAndMoveThisIn, m_document, &SkeletonDocument::createNewComponentAndMoveThisIn);
|
connect(partTreeWidget, &SkeletonPartTreeWidget::createNewComponentAndMoveThisIn, m_document, &SkeletonDocument::createNewComponentAndMoveThisIn);
|
||||||
connect(partTreeWidget, &SkeletonPartTreeWidget::renameComponent, m_document, &SkeletonDocument::renameComponent);
|
connect(partTreeWidget, &SkeletonPartTreeWidget::renameComponent, m_document, &SkeletonDocument::renameComponent);
|
||||||
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentExpandState, m_document, &SkeletonDocument::setComponentExpandState);
|
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentExpandState, m_document, &SkeletonDocument::setComponentExpandState);
|
||||||
|
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentSmoothAll, m_document, &SkeletonDocument::setComponentSmoothAll);
|
||||||
|
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentSmoothSeam, m_document, &SkeletonDocument::setComponentSmoothSeam);
|
||||||
connect(partTreeWidget, &SkeletonPartTreeWidget::moveComponent, m_document, &SkeletonDocument::moveComponent);
|
connect(partTreeWidget, &SkeletonPartTreeWidget::moveComponent, m_document, &SkeletonDocument::moveComponent);
|
||||||
connect(partTreeWidget, &SkeletonPartTreeWidget::removeComponent, m_document, &SkeletonDocument::removeComponent);
|
connect(partTreeWidget, &SkeletonPartTreeWidget::removeComponent, m_document, &SkeletonDocument::removeComponent);
|
||||||
connect(partTreeWidget, &SkeletonPartTreeWidget::hideOtherComponents, m_document, &SkeletonDocument::hideOtherComponents);
|
connect(partTreeWidget, &SkeletonPartTreeWidget::hideOtherComponents, m_document, &SkeletonDocument::hideOtherComponents);
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QWidgetAction>
|
||||||
#include "skeletonparttreewidget.h"
|
#include "skeletonparttreewidget.h"
|
||||||
#include "skeletonpartwidget.h"
|
#include "skeletonpartwidget.h"
|
||||||
#include "skeletongraphicswidget.h"
|
#include "skeletongraphicswidget.h"
|
||||||
|
#include "floatnumberwidget.h"
|
||||||
|
|
||||||
SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent) :
|
SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent) :
|
||||||
QTreeWidget(parent),
|
QTreeWidget(parent),
|
||||||
|
@ -22,6 +26,7 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
|
||||||
m_componentItemMap[QUuid()] = invisibleRootItem();
|
m_componentItemMap[QUuid()] = invisibleRootItem();
|
||||||
|
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
//setIconSize(QSize(Theme::miniIconFontSize, Theme::miniIconFontSize));
|
||||||
|
|
||||||
setStyleSheet("QTreeView {qproperty-indentation: 10; margin-left: 5px; margin-top: 5px;}"
|
setStyleSheet("QTreeView {qproperty-indentation: 10; margin-left: 5px; margin-top: 5px;}"
|
||||||
"QTreeView::branch:has-siblings:!adjoins-item {border-image: url(:/resources/tree-vline.png);}"
|
"QTreeView::branch:has-siblings:!adjoins-item {border-image: url(:/resources/tree-vline.png);}"
|
||||||
|
@ -30,42 +35,63 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
|
||||||
"QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-closed.png);}"
|
"QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-closed.png);}"
|
||||||
"QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-open.png);}");
|
"QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-open.png);}");
|
||||||
|
|
||||||
QFont font;
|
m_normalFont.setWeight(QFont::Light);
|
||||||
font.setWeight(QFont::Light);
|
m_normalFont.setPixelSize(9);
|
||||||
font.setPixelSize(9);
|
m_normalFont.setBold(false);
|
||||||
font.setBold(false);
|
|
||||||
setFont(font);
|
m_selectedFont.setWeight(QFont::Light);
|
||||||
|
m_selectedFont.setPixelSize(9);
|
||||||
|
m_selectedFont.setBold(true);
|
||||||
|
|
||||||
|
setFont(m_normalFont);
|
||||||
|
|
||||||
connect(this, &QTreeWidget::customContextMenuRequested, this, &SkeletonPartTreeWidget::showContextMenu);
|
connect(this, &QTreeWidget::customContextMenuRequested, this, &SkeletonPartTreeWidget::showContextMenu);
|
||||||
connect(this, &QTreeWidget::itemChanged, this, &SkeletonPartTreeWidget::groupChanged);
|
connect(this, &QTreeWidget::itemChanged, this, &SkeletonPartTreeWidget::groupChanged);
|
||||||
connect(this, &QTreeWidget::itemExpanded, this, &SkeletonPartTreeWidget::groupExpanded);
|
connect(this, &QTreeWidget::itemExpanded, this, &SkeletonPartTreeWidget::groupExpanded);
|
||||||
connect(this, &QTreeWidget::itemCollapsed, this, &SkeletonPartTreeWidget::groupCollapsed);
|
connect(this, &QTreeWidget::itemCollapsed, this, &SkeletonPartTreeWidget::groupCollapsed);
|
||||||
connect(this, &QTreeWidget::currentItemChanged, this, &SkeletonPartTreeWidget::currentGroupChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartTreeWidget::currentGroupChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
|
void SkeletonPartTreeWidget::selectComponent(QUuid componentId)
|
||||||
{
|
{
|
||||||
if (nullptr != current) {
|
if (m_currentSelectedComponent != componentId) {
|
||||||
auto componentId = QUuid(current->data(0, Qt::UserRole).toString());
|
if (!m_currentSelectedComponent.isNull()) {
|
||||||
const SkeletonComponent *component = m_document->findComponent(componentId);
|
auto item = m_componentItemMap.find(m_currentSelectedComponent);
|
||||||
if (nullptr != component) {
|
if (item != m_componentItemMap.end()) {
|
||||||
emit currentComponentChanged(componentId);
|
item->second->setFont(0, m_normalFont);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
m_currentSelectedComponent = componentId;
|
||||||
|
if (!m_currentSelectedComponent.isNull()) {
|
||||||
|
auto item = m_componentItemMap.find(m_currentSelectedComponent);
|
||||||
|
if (item != m_componentItemMap.end()) {
|
||||||
|
item->second->setFont(0, m_selectedFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit currentComponentChanged(m_currentSelectedComponent);
|
||||||
}
|
}
|
||||||
emit currentComponentChanged(QUuid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartTreeWidget::mousePressEvent(QMouseEvent *event)
|
void SkeletonPartTreeWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
QModelIndex item = indexAt(event->pos());
|
QModelIndex itemIndex = indexAt(event->pos());
|
||||||
if (item.isValid()) {
|
QTreeView::mousePressEvent(event);
|
||||||
QTreeView::mousePressEvent(event);
|
if (itemIndex.isValid()) {
|
||||||
} else {
|
QTreeWidgetItem *item = itemFromIndex(itemIndex);
|
||||||
clearSelection();
|
auto componentId = QUuid(item->data(0, Qt::UserRole).toString());
|
||||||
QTreeView::mousePressEvent(event);
|
if (m_componentItemMap.find(componentId) != m_componentItemMap.end()) {
|
||||||
emit currentComponentChanged(QUuid());
|
selectComponent(componentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item = item->parent();
|
||||||
|
if (nullptr != item) {
|
||||||
|
componentId = QUuid(item->data(0, Qt::UserRole).toString());
|
||||||
|
if (m_componentItemMap.find(componentId) != m_componentItemMap.end()) {
|
||||||
|
selectComponent(componentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
selectComponent(QUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
|
@ -92,9 +118,13 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
contextMenu.addSection(component->name);
|
contextMenu.addSection(component->name);
|
||||||
|
|
||||||
QAction showAction(tr("Show"), this);
|
QAction showAction(tr("Show"), this);
|
||||||
|
showAction.setIcon(Theme::awesome()->icon(fa::eye));
|
||||||
QAction hideAction(tr("Hide"), this);
|
QAction hideAction(tr("Hide"), this);
|
||||||
|
hideAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
|
||||||
QAction lockAction(tr("Lock"), this);
|
QAction lockAction(tr("Lock"), this);
|
||||||
|
lockAction.setIcon(Theme::awesome()->icon(fa::lock));
|
||||||
QAction unlockAction(tr("Unlock"), this);
|
QAction unlockAction(tr("Unlock"), this);
|
||||||
|
unlockAction.setIcon(Theme::awesome()->icon(fa::unlock));
|
||||||
QAction invertAction(tr("Invert"), this);
|
QAction invertAction(tr("Invert"), this);
|
||||||
QAction cancelInverseAction(tr("Cancel Inverse"), this);
|
QAction cancelInverseAction(tr("Cancel Inverse"), this);
|
||||||
QAction selectAction(tr("Select"), this);
|
QAction selectAction(tr("Select"), this);
|
||||||
|
@ -176,13 +206,22 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
|
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
|
QMenu *smoothMenu = contextMenu.addMenu(tr("Smooth"));
|
||||||
|
QWidgetAction smoothAction(this);
|
||||||
|
smoothAction.setDefaultWidget(createSmoothMenuWidget(componentId));
|
||||||
|
smoothMenu->addAction(&smoothAction);
|
||||||
|
|
||||||
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction hideOthersAction(tr("Hide Others"), this);
|
QAction hideOthersAction(tr("Hide Others"), this);
|
||||||
|
hideOthersAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
|
||||||
connect(&hideOthersAction, &QAction::triggered, [=]() {
|
connect(&hideOthersAction, &QAction::triggered, [=]() {
|
||||||
emit hideOtherComponents(componentId);
|
emit hideOtherComponents(componentId);
|
||||||
});
|
});
|
||||||
contextMenu.addAction(&hideOthersAction);
|
contextMenu.addAction(&hideOthersAction);
|
||||||
|
|
||||||
QAction lockOthersAction(tr("Lock Others"), this);
|
QAction lockOthersAction(tr("Lock Others"), this);
|
||||||
|
lockOthersAction.setIcon(Theme::awesome()->icon(fa::lock));
|
||||||
connect(&lockOthersAction, &QAction::triggered, [=]() {
|
connect(&lockOthersAction, &QAction::triggered, [=]() {
|
||||||
emit lockOtherComponents(componentId);
|
emit lockOtherComponents(componentId);
|
||||||
});
|
});
|
||||||
|
@ -205,12 +244,14 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction hideAllAction(tr("Hide All"), this);
|
QAction hideAllAction(tr("Hide All"), this);
|
||||||
|
hideAllAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
|
||||||
connect(&hideAllAction, &QAction::triggered, [=]() {
|
connect(&hideAllAction, &QAction::triggered, [=]() {
|
||||||
emit hideAllComponents();
|
emit hideAllComponents();
|
||||||
});
|
});
|
||||||
contextMenu.addAction(&hideAllAction);
|
contextMenu.addAction(&hideAllAction);
|
||||||
|
|
||||||
QAction showAllAction(tr("Show All"), this);
|
QAction showAllAction(tr("Show All"), this);
|
||||||
|
showAllAction.setIcon(Theme::awesome()->icon(fa::eye));
|
||||||
connect(&showAllAction, &QAction::triggered, [=]() {
|
connect(&showAllAction, &QAction::triggered, [=]() {
|
||||||
emit showAllComponents();
|
emit showAllComponents();
|
||||||
});
|
});
|
||||||
|
@ -219,12 +260,14 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction lockAllAction(tr("Lock All"), this);
|
QAction lockAllAction(tr("Lock All"), this);
|
||||||
|
lockAllAction.setIcon(Theme::awesome()->icon(fa::lock));
|
||||||
connect(&lockAllAction, &QAction::triggered, [=]() {
|
connect(&lockAllAction, &QAction::triggered, [=]() {
|
||||||
emit lockAllComponents();
|
emit lockAllComponents();
|
||||||
});
|
});
|
||||||
contextMenu.addAction(&lockAllAction);
|
contextMenu.addAction(&lockAllAction);
|
||||||
|
|
||||||
QAction unlockAllAction(tr("Unlock All"), this);
|
QAction unlockAllAction(tr("Unlock All"), this);
|
||||||
|
unlockAllAction.setIcon(Theme::awesome()->icon(fa::unlock));
|
||||||
connect(&unlockAllAction, &QAction::triggered, [=]() {
|
connect(&unlockAllAction, &QAction::triggered, [=]() {
|
||||||
emit unlockAllComponents();
|
emit unlockAllComponents();
|
||||||
});
|
});
|
||||||
|
@ -234,25 +277,29 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
|
|
||||||
QMenu *moveToMenu = contextMenu.addMenu(tr("Move To"));
|
QMenu *moveToMenu = contextMenu.addMenu(tr("Move To"));
|
||||||
|
|
||||||
|
QAction moveToTopAction(tr("Top"), this);
|
||||||
|
moveToTopAction.setIcon(Theme::awesome()->icon(fa::angledoubleup));
|
||||||
|
connect(&moveToTopAction, &QAction::triggered, [=]() {
|
||||||
|
emit moveComponentToTop(componentId);
|
||||||
|
});
|
||||||
|
moveToMenu->addAction(&moveToTopAction);
|
||||||
|
|
||||||
QAction moveUpAction(tr("Up"), this);
|
QAction moveUpAction(tr("Up"), this);
|
||||||
|
moveUpAction.setIcon(Theme::awesome()->icon(fa::angleup));
|
||||||
connect(&moveUpAction, &QAction::triggered, [=]() {
|
connect(&moveUpAction, &QAction::triggered, [=]() {
|
||||||
emit moveComponentUp(componentId);
|
emit moveComponentUp(componentId);
|
||||||
});
|
});
|
||||||
moveToMenu->addAction(&moveUpAction);
|
moveToMenu->addAction(&moveUpAction);
|
||||||
|
|
||||||
QAction moveDownAction(tr("Down"), this);
|
QAction moveDownAction(tr("Down"), this);
|
||||||
|
moveDownAction.setIcon(Theme::awesome()->icon(fa::angledown));
|
||||||
connect(&moveDownAction, &QAction::triggered, [=]() {
|
connect(&moveDownAction, &QAction::triggered, [=]() {
|
||||||
emit moveComponentDown(componentId);
|
emit moveComponentDown(componentId);
|
||||||
});
|
});
|
||||||
moveToMenu->addAction(&moveDownAction);
|
moveToMenu->addAction(&moveDownAction);
|
||||||
|
|
||||||
QAction moveToTopAction(tr("Top"), this);
|
|
||||||
connect(&moveToTopAction, &QAction::triggered, [=]() {
|
|
||||||
emit moveComponentToTop(componentId);
|
|
||||||
});
|
|
||||||
moveToMenu->addAction(&moveToTopAction);
|
|
||||||
|
|
||||||
QAction moveToBottomAction(tr("Bottom"), this);
|
QAction moveToBottomAction(tr("Bottom"), this);
|
||||||
|
moveToBottomAction.setIcon(Theme::awesome()->icon(fa::angledoubledown));
|
||||||
connect(&moveToBottomAction, &QAction::triggered, [=]() {
|
connect(&moveToBottomAction, &QAction::triggered, [=]() {
|
||||||
emit moveComponentToBottom(componentId);
|
emit moveComponentToBottom(componentId);
|
||||||
});
|
});
|
||||||
|
@ -261,6 +308,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
moveToMenu->addSeparator();
|
moveToMenu->addSeparator();
|
||||||
|
|
||||||
QAction moveToNewGroupAction(tr("New Group"), this);
|
QAction moveToNewGroupAction(tr("New Group"), this);
|
||||||
|
moveToNewGroupAction.setIcon(Theme::awesome()->icon(fa::file));
|
||||||
moveToMenu->addAction(&moveToNewGroupAction);
|
moveToMenu->addAction(&moveToNewGroupAction);
|
||||||
connect(&moveToNewGroupAction, &QAction::triggered, [=]() {
|
connect(&moveToNewGroupAction, &QAction::triggered, [=]() {
|
||||||
emit createNewComponentAndMoveThisIn(componentId);
|
emit createNewComponentAndMoveThisIn(componentId);
|
||||||
|
@ -297,6 +345,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction deleteAction(tr("Delete"), this);
|
QAction deleteAction(tr("Delete"), this);
|
||||||
|
deleteAction.setIcon(Theme::awesome()->icon(fa::trash));
|
||||||
connect(&deleteAction, &QAction::triggered, [=]() {
|
connect(&deleteAction, &QAction::triggered, [=]() {
|
||||||
emit removeComponent(componentId);
|
emit removeComponent(componentId);
|
||||||
});
|
});
|
||||||
|
@ -309,6 +358,78 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget *SkeletonPartTreeWidget::createSmoothMenuWidget(QUuid componentId)
|
||||||
|
{
|
||||||
|
QWidget *popup = new QWidget;
|
||||||
|
|
||||||
|
const SkeletonComponent *component = m_document->findComponent(componentId);
|
||||||
|
if (!component) {
|
||||||
|
qDebug() << "Find component failed:" << componentId;
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool showSeamControl = component->linkToPartId.isNull();
|
||||||
|
|
||||||
|
FloatNumberWidget *smoothAllWidget = new FloatNumberWidget;
|
||||||
|
smoothAllWidget->setItemName(tr("All"));
|
||||||
|
smoothAllWidget->setRange(0, 1);
|
||||||
|
smoothAllWidget->setValue(component->smoothAll);
|
||||||
|
|
||||||
|
connect(smoothAllWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
||||||
|
emit setComponentSmoothAll(componentId, value);
|
||||||
|
emit groupOperationAdded();
|
||||||
|
});
|
||||||
|
|
||||||
|
QPushButton *smoothAllEraser = new QPushButton(QChar(fa::eraser));
|
||||||
|
Theme::initAwesomeToolButton(smoothAllEraser);
|
||||||
|
|
||||||
|
connect(smoothAllEraser, &QPushButton::clicked, [=]() {
|
||||||
|
smoothAllWidget->setValue(0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
FloatNumberWidget *smoothSeamWidget = nullptr;
|
||||||
|
QPushButton *smoothSeamEraser = nullptr;
|
||||||
|
|
||||||
|
if (showSeamControl) {
|
||||||
|
smoothSeamWidget = new FloatNumberWidget;
|
||||||
|
smoothSeamWidget->setItemName(tr("Seam"));
|
||||||
|
smoothSeamWidget->setRange(0, 1);
|
||||||
|
smoothSeamWidget->setValue(component->smoothSeam);
|
||||||
|
|
||||||
|
connect(smoothSeamWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
||||||
|
emit setComponentSmoothSeam(componentId, value);
|
||||||
|
emit groupOperationAdded();
|
||||||
|
});
|
||||||
|
|
||||||
|
smoothSeamEraser = new QPushButton(QChar(fa::eraser));
|
||||||
|
Theme::initAwesomeToolButton(smoothSeamEraser);
|
||||||
|
|
||||||
|
connect(smoothSeamEraser, &QPushButton::clicked, [=]() {
|
||||||
|
smoothSeamWidget->setValue(0.0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *smoothSeamLayout = nullptr;
|
||||||
|
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
|
QHBoxLayout *smoothAllLayout = new QHBoxLayout;
|
||||||
|
if (showSeamControl)
|
||||||
|
smoothSeamLayout = new QHBoxLayout;
|
||||||
|
smoothAllLayout->addWidget(smoothAllEraser);
|
||||||
|
smoothAllLayout->addWidget(smoothAllWidget);
|
||||||
|
if (showSeamControl) {
|
||||||
|
smoothSeamLayout->addWidget(smoothSeamEraser);
|
||||||
|
smoothSeamLayout->addWidget(smoothSeamWidget);
|
||||||
|
}
|
||||||
|
layout->addLayout(smoothAllLayout);
|
||||||
|
if (showSeamControl)
|
||||||
|
layout->addLayout(smoothSeamLayout);
|
||||||
|
|
||||||
|
popup->setLayout(layout);
|
||||||
|
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
QTreeWidgetItem *SkeletonPartTreeWidget::findComponentItem(QUuid componentId)
|
QTreeWidgetItem *SkeletonPartTreeWidget::findComponentItem(QUuid componentId)
|
||||||
{
|
{
|
||||||
auto findResult = m_componentItemMap.find(componentId);
|
auto findResult = m_componentItemMap.find(componentId);
|
||||||
|
@ -379,7 +500,7 @@ void SkeletonPartTreeWidget::addComponentChildrenToItem(QUuid componentId, QTree
|
||||||
item->setData(0, Qt::UserRole, QVariant(component->id.toString()));
|
item->setData(0, Qt::UserRole, QVariant(component->id.toString()));
|
||||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||||
item->setExpanded(component->expanded);
|
item->setExpanded(component->expanded);
|
||||||
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
item->setFlags((item->flags() | Qt::ItemIsEditable | Qt::ItemIsEnabled) & ~(Qt::ItemIsSelectable));
|
||||||
m_componentItemMap[childId] = item;
|
m_componentItemMap[childId] = item;
|
||||||
addComponentChildrenToItem(childId, item);
|
addComponentChildrenToItem(childId, item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ signals:
|
||||||
void createNewComponentAndMoveThisIn(QUuid componentId);
|
void createNewComponentAndMoveThisIn(QUuid componentId);
|
||||||
void renameComponent(QUuid componentId, QString name);
|
void renameComponent(QUuid componentId, QString name);
|
||||||
void setComponentExpandState(QUuid componentId, bool expanded);
|
void setComponentExpandState(QUuid componentId, bool expanded);
|
||||||
|
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
||||||
|
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
||||||
void moveComponent(QUuid componentId, QUuid toParentId);
|
void moveComponent(QUuid componentId, QUuid toParentId);
|
||||||
void removeComponent(QUuid componentId);
|
void removeComponent(QUuid componentId);
|
||||||
void hideOtherComponents(QUuid componentId);
|
void hideOtherComponents(QUuid componentId);
|
||||||
|
@ -38,6 +40,7 @@ signals:
|
||||||
void lockDescendantComponents(QUuid componentId);
|
void lockDescendantComponents(QUuid componentId);
|
||||||
void unlockDescendantComponents(QUuid componentId);
|
void unlockDescendantComponents(QUuid componentId);
|
||||||
void addPartToSelection(QUuid partId);
|
void addPartToSelection(QUuid partId);
|
||||||
|
void groupOperationAdded();
|
||||||
public:
|
public:
|
||||||
SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent);
|
SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent);
|
||||||
QTreeWidgetItem *findComponentItem(QUuid componentId);
|
QTreeWidgetItem *findComponentItem(QUuid componentId);
|
||||||
|
@ -64,7 +67,6 @@ public slots:
|
||||||
void groupChanged(QTreeWidgetItem *item, int column);
|
void groupChanged(QTreeWidgetItem *item, int column);
|
||||||
void groupExpanded(QTreeWidgetItem *item);
|
void groupExpanded(QTreeWidgetItem *item);
|
||||||
void groupCollapsed(QTreeWidgetItem *item);
|
void groupCollapsed(QTreeWidgetItem *item);
|
||||||
void currentGroupChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
|
|
||||||
void removeAllContent();
|
void removeAllContent();
|
||||||
void showContextMenu(const QPoint &pos);
|
void showContextMenu(const QPoint &pos);
|
||||||
protected:
|
protected:
|
||||||
|
@ -74,11 +76,16 @@ protected:
|
||||||
private:
|
private:
|
||||||
void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem);
|
void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem);
|
||||||
void deleteItemChildren(QTreeWidgetItem *item);
|
void deleteItemChildren(QTreeWidgetItem *item);
|
||||||
|
void selectComponent(QUuid componentId);
|
||||||
|
QWidget *createSmoothMenuWidget(QUuid componentId);
|
||||||
private:
|
private:
|
||||||
const SkeletonDocument *m_document = nullptr;
|
const SkeletonDocument *m_document = nullptr;
|
||||||
std::map<QUuid, QTreeWidgetItem *> m_partItemMap;
|
std::map<QUuid, QTreeWidgetItem *> m_partItemMap;
|
||||||
std::map<QUuid, QTreeWidgetItem *> m_componentItemMap;
|
std::map<QUuid, QTreeWidgetItem *> m_componentItemMap;
|
||||||
SkeletonGraphicsFunctions *m_graphicsFunctions = nullptr;
|
SkeletonGraphicsFunctions *m_graphicsFunctions = nullptr;
|
||||||
|
QFont m_normalFont;
|
||||||
|
QFont m_selectedFont;
|
||||||
|
QUuid m_currentSelectedComponent;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -263,15 +263,12 @@ void SkeletonPartWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
|
||||||
void SkeletonPartWidget::initToolButtonWithoutFont(QPushButton *button)
|
void SkeletonPartWidget::initToolButtonWithoutFont(QPushButton *button)
|
||||||
{
|
{
|
||||||
button->setFixedSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2);
|
Theme::initAwesomeToolButtonWithoutFont(button);
|
||||||
button->setStyleSheet("QPushButton {color: #f7d9c8}");
|
|
||||||
button->setFocusPolicy(Qt::NoFocus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartWidget::initToolButton(QPushButton *button)
|
void SkeletonPartWidget::initToolButton(QPushButton *button)
|
||||||
{
|
{
|
||||||
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize / 2));
|
Theme::initAwesomeToolButton(button);
|
||||||
initToolButtonWithoutFont(button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
|
void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
|
||||||
|
@ -289,10 +286,6 @@ void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
|
||||||
QPushButton *colorEraser = new QPushButton(QChar(fa::eraser));
|
QPushButton *colorEraser = new QPushButton(QChar(fa::eraser));
|
||||||
initToolButton(colorEraser);
|
initToolButton(colorEraser);
|
||||||
|
|
||||||
//QLabel *colorPreviewLabel = new QLabel;
|
|
||||||
//colorPreviewLabel->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
|
|
||||||
//colorPreviewLabel->setAutoFillBackground(true);
|
|
||||||
|
|
||||||
QPushButton *pickButton = new QPushButton();
|
QPushButton *pickButton = new QPushButton();
|
||||||
initToolButtonWithoutFont(pickButton);
|
initToolButtonWithoutFont(pickButton);
|
||||||
QPalette palette = pickButton->palette();
|
QPalette palette = pickButton->palette();
|
||||||
|
@ -343,14 +336,17 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
||||||
QWidget *popup = new QWidget;
|
QWidget *popup = new QWidget;
|
||||||
|
|
||||||
FloatNumberWidget *thicknessWidget = new FloatNumberWidget;
|
FloatNumberWidget *thicknessWidget = new FloatNumberWidget;
|
||||||
|
thicknessWidget->setItemName(tr("Thickness"));
|
||||||
thicknessWidget->setRange(0, 2);
|
thicknessWidget->setRange(0, 2);
|
||||||
thicknessWidget->setValue(part->deformThickness);
|
thicknessWidget->setValue(part->deformThickness);
|
||||||
|
|
||||||
connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
||||||
emit setPartDeformThickness(m_partId, value);
|
emit setPartDeformThickness(m_partId, value);
|
||||||
|
emit groupOperationAdded();
|
||||||
});
|
});
|
||||||
|
|
||||||
FloatNumberWidget *widthWidget = new FloatNumberWidget;
|
FloatNumberWidget *widthWidget = new FloatNumberWidget;
|
||||||
|
widthWidget->setItemName(tr("Width"));
|
||||||
widthWidget->setRange(0, 2);
|
widthWidget->setRange(0, 2);
|
||||||
widthWidget->setValue(part->deformWidth);
|
widthWidget->setValue(part->deformWidth);
|
||||||
|
|
||||||
|
@ -364,6 +360,7 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
||||||
|
|
||||||
connect(thicknessEraser, &QPushButton::clicked, [=]() {
|
connect(thicknessEraser, &QPushButton::clicked, [=]() {
|
||||||
thicknessWidget->setValue(1.0);
|
thicknessWidget->setValue(1.0);
|
||||||
|
emit groupOperationAdded();
|
||||||
});
|
});
|
||||||
|
|
||||||
QPushButton *widthEraser = new QPushButton(QChar(fa::eraser));
|
QPushButton *widthEraser = new QPushButton(QChar(fa::eraser));
|
||||||
|
@ -371,6 +368,7 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
||||||
|
|
||||||
connect(widthEraser, &QPushButton::clicked, [=]() {
|
connect(widthEraser, &QPushButton::clicked, [=]() {
|
||||||
widthWidget->setValue(1.0);
|
widthWidget->setValue(1.0);
|
||||||
|
emit groupOperationAdded();
|
||||||
});
|
});
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout;
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
|
@ -385,35 +383,22 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
||||||
|
|
||||||
popup->setLayout(layout);
|
popup->setLayout(layout);
|
||||||
|
|
||||||
QWidgetAction *action = new QWidgetAction(this);
|
QWidgetAction action(this);
|
||||||
action->setDefaultWidget(popup);
|
action.setDefaultWidget(popup);
|
||||||
|
|
||||||
popupMenu.addAction(action);
|
popupMenu.addAction(&action);
|
||||||
|
|
||||||
popupMenu.exec(mapToGlobal(pos));
|
popupMenu.exec(mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartWidget::initButton(QPushButton *button)
|
void SkeletonPartWidget::initButton(QPushButton *button)
|
||||||
{
|
{
|
||||||
button->setFont(Theme::awesome()->font(Theme::miniIconFontSize));
|
Theme::initAwesomeMiniButton(button);
|
||||||
button->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
|
|
||||||
button->setFocusPolicy(Qt::NoFocus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartWidget::updateButton(QPushButton *button, QChar icon, bool highlighted)
|
void SkeletonPartWidget::updateButton(QPushButton *button, QChar icon, bool highlighted)
|
||||||
{
|
{
|
||||||
button->setText(icon);
|
Theme::updateAwesomeMiniButton(button, icon, highlighted);
|
||||||
QColor color;
|
|
||||||
if (highlighted)
|
|
||||||
color = QColor("#fc6621");
|
|
||||||
else
|
|
||||||
color = QColor("#525252");
|
|
||||||
|
|
||||||
color = color.toHsv();
|
|
||||||
color.setHsv(color.hue(), color.saturation() / 5, color.value() * 2 / 3);
|
|
||||||
color = color.toRgb();
|
|
||||||
|
|
||||||
button->setStyleSheet("QPushButton {border: none; background: none; color: " + color.name() + ";}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonPartWidget::updatePreview()
|
void SkeletonPartWidget::updatePreview()
|
||||||
|
|
|
@ -92,4 +92,39 @@ void Theme::initAwesomeLabel(QLabel *label)
|
||||||
label->setStyleSheet("QLabel {color: #f7d9c8}");
|
label->setStyleSheet("QLabel {color: #f7d9c8}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Theme::initAwesomeMiniButton(QPushButton *button)
|
||||||
|
{
|
||||||
|
button->setFont(Theme::awesome()->font(Theme::miniIconFontSize));
|
||||||
|
button->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
|
||||||
|
button->setFocusPolicy(Qt::NoFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Theme::updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted)
|
||||||
|
{
|
||||||
|
button->setText(icon);
|
||||||
|
QColor color;
|
||||||
|
if (highlighted)
|
||||||
|
color = QColor("#fc6621");
|
||||||
|
else
|
||||||
|
color = QColor("#525252");
|
||||||
|
|
||||||
|
color = color.toHsv();
|
||||||
|
color.setHsv(color.hue(), color.saturation() / 5, color.value() * 2 / 3);
|
||||||
|
color = color.toRgb();
|
||||||
|
|
||||||
|
button->setStyleSheet("QPushButton {border: none; background: none; color: " + color.name() + ";}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Theme::initAwesomeToolButtonWithoutFont(QPushButton *button)
|
||||||
|
{
|
||||||
|
button->setFixedSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2);
|
||||||
|
button->setStyleSheet("QPushButton {color: #f7d9c8}");
|
||||||
|
button->setFocusPolicy(Qt::NoFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Theme::initAwesomeToolButton(QPushButton *button)
|
||||||
|
{
|
||||||
|
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize / 2));
|
||||||
|
Theme::initAwesomeToolButtonWithoutFont(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,10 @@ public:
|
||||||
static void initAwesomeButton(QPushButton *button);
|
static void initAwesomeButton(QPushButton *button);
|
||||||
static void initAwesomeLabel(QLabel *label);
|
static void initAwesomeLabel(QLabel *label);
|
||||||
static void initAwesomeSmallButton(QPushButton *button);
|
static void initAwesomeSmallButton(QPushButton *button);
|
||||||
|
static void initAwesomeMiniButton(QPushButton *button);
|
||||||
|
static void updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted);
|
||||||
|
static void initAwesomeToolButton(QPushButton *button);
|
||||||
|
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -59,6 +59,8 @@ int meshlite_skeletonmesh_create(void *context);
|
||||||
int meshlite_skeletonmesh_set_end_radius(void *context, float radius);
|
int meshlite_skeletonmesh_set_end_radius(void *context, float radius);
|
||||||
int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z);
|
int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z);
|
||||||
int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id);
|
int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id);
|
||||||
|
int meshlite_smooth_vertices(void *context, int mesh_id, float factor, int *buffer, int max_buffer_len);
|
||||||
|
int meshlite_smooth(void *context, int mesh_id, float factor);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -59,6 +59,8 @@ int meshlite_skeletonmesh_create(void *context);
|
||||||
int meshlite_skeletonmesh_set_end_radius(void *context, float radius);
|
int meshlite_skeletonmesh_set_end_radius(void *context, float radius);
|
||||||
int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z);
|
int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z);
|
||||||
int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id);
|
int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id);
|
||||||
|
int meshlite_smooth_vertices(void *context, int mesh_id, float factor, int *buffer, int max_buffer_len);
|
||||||
|
int meshlite_smooth(void *context, int mesh_id, float factor);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue