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->setRange(0, 100);
|
||||
m_slider->setFixedWidth(120);
|
||||
|
||||
m_label = new QLabel(this);
|
||||
m_label->setAlignment(Qt::AlignCenter);
|
||||
m_label->setNum(0);
|
||||
m_label->setFixedWidth(30);
|
||||
m_label->setAlignment(Qt::AlignLeft);
|
||||
|
||||
connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) {
|
||||
float fvalue = value / 100.0;
|
||||
m_label->setText(QString().sprintf("%.2f", fvalue));
|
||||
updateValueLabel(fvalue);
|
||||
emit valueChanged(fvalue);
|
||||
});
|
||||
|
||||
|
@ -24,6 +23,21 @@ FloatNumberWidget::FloatNumberWidget(QWidget *parent) :
|
|||
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)
|
||||
{
|
||||
m_slider->setRange(min * 100, max * 100);
|
||||
|
|
|
@ -12,6 +12,7 @@ public:
|
|||
explicit FloatNumberWidget(QWidget *parent = nullptr);
|
||||
void setRange(float min, float max);
|
||||
float value() const;
|
||||
void setItemName(const QString &name);
|
||||
|
||||
public slots:
|
||||
void increaseValue();
|
||||
|
@ -20,10 +21,14 @@ public slots:
|
|||
|
||||
signals:
|
||||
void valueChanged(float value);
|
||||
|
||||
private:
|
||||
void updateValueLabel(float value);
|
||||
|
||||
private:
|
||||
QLabel *m_label = nullptr;
|
||||
QSlider *m_slider = nullptr;
|
||||
QString m_itemName;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,7 +28,7 @@ int main(int argc, char ** argv)
|
|||
darkPalette.setColor(QPalette::ButtonText, Theme::white);
|
||||
darkPalette.setColor(QPalette::BrightText, Theme::red);
|
||||
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);
|
||||
qApp->setPalette(darkPalette);
|
||||
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)
|
||||
meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1);
|
||||
|
||||
QString mirroredPartId;
|
||||
QUuid mirroredPartIdNotAsString;
|
||||
if (xMirrored) {
|
||||
mirroredPartIdNotAsString = QUuid().createUuid();
|
||||
mirroredPartId = mirroredPartIdNotAsString.toString();
|
||||
}
|
||||
//QString mirroredPartId;
|
||||
//QUuid mirroredPartIdNotAsString;
|
||||
//if (xMirrored) {
|
||||
// mirroredPartIdNotAsString = QUuid().createUuid();
|
||||
// mirroredPartId = mirroredPartIdNotAsString.toString();
|
||||
//}
|
||||
|
||||
std::map<QString, int> nodeToBmeshIdMap;
|
||||
std::map<int, QUuid> bmeshToNodeIdMap;
|
||||
|
@ -252,12 +252,11 @@ void *MeshGenerator::combinePartMesh(QString partId)
|
|||
bmeshNode.nodeId = QUuid(nodeId);
|
||||
bmeshNode.color = partColor;
|
||||
cacheBmeshNodes.push_back(bmeshNode);
|
||||
|
||||
if (xMirrored) {
|
||||
bmeshNode.partId = mirroredPartId;
|
||||
bmeshNode.origin.setX(-x);
|
||||
cacheBmeshNodes.push_back(bmeshNode);
|
||||
}
|
||||
//if (xMirrored) {
|
||||
// bmeshNode.partId = mirroredPartId;
|
||||
// bmeshNode.origin.setX(-x);
|
||||
// cacheBmeshNodes.push_back(bmeshNode);
|
||||
//}
|
||||
}
|
||||
|
||||
for (const auto &edgeId: m_partEdgeIds[partId]) {
|
||||
|
@ -300,7 +299,7 @@ void *MeshGenerator::combinePartMesh(QString partId)
|
|||
if (nullptr != resultMesh) {
|
||||
if (xMirrored) {
|
||||
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;
|
||||
if (wrapped)
|
||||
mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId);
|
||||
|
@ -411,41 +410,122 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|||
}
|
||||
}
|
||||
|
||||
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
||||
if ("partId" == linkDataType) {
|
||||
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
||||
return combinePartMesh(partId);
|
||||
bool smoothSeam = false;
|
||||
float smoothSeamFactor = 0.0;
|
||||
QString smoothSeamString = valueOfKeyInMapOrEmpty(*component, "smoothSeam");
|
||||
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;
|
||||
|
||||
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
||||
if (childId.isEmpty())
|
||||
continue;
|
||||
bool childInverse = false;
|
||||
void *childCombinedMesh = combineComponentMesh(childId, &childInverse);
|
||||
if (nullptr == childCombinedMesh)
|
||||
continue;
|
||||
if (nullptr == resultMesh) {
|
||||
if (childInverse) {
|
||||
deleteCombinableMesh(childCombinedMesh);
|
||||
} else {
|
||||
resultMesh = childCombinedMesh;
|
||||
PositionMap<bool> positionsBeforeCombination;
|
||||
auto &verticesSources = m_cacheContext->componentVerticesSources[componentId];
|
||||
verticesSources.map().clear();
|
||||
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
||||
if ("partId" == linkDataType) {
|
||||
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
||||
resultMesh = combinePartMesh(partId);
|
||||
for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) {
|
||||
verticesSources.addPosition(bmeshVertex.position.x(), bmeshVertex.position.y(), bmeshVertex.position.z(),
|
||||
bmeshVertex);
|
||||
}
|
||||
} 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 {
|
||||
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
||||
deleteCombinableMesh(childCombinedMesh);
|
||||
if (nullptr != newResultMesh) {
|
||||
deleteCombinableMesh(resultMesh);
|
||||
resultMesh = newResultMesh;
|
||||
for (const auto &verticesSourceIt: m_cacheContext->componentVerticesSources[childId].map()) {
|
||||
verticesSources.map()[verticesSourceIt.first] = verticesSourceIt.second;
|
||||
}
|
||||
if (nullptr == childCombinedMesh)
|
||||
continue;
|
||||
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()) {
|
||||
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
|
||||
if (nullptr != 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;
|
||||
}
|
||||
|
||||
|
@ -488,11 +568,27 @@ void MeshGenerator::process()
|
|||
}
|
||||
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();
|
||||
checkDirtyFlags();
|
||||
|
||||
m_dirtyComponentIds.insert(QUuid().toString());
|
||||
|
||||
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
|
||||
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
|
||||
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
|
||||
|
@ -506,9 +602,8 @@ void MeshGenerator::process()
|
|||
deleteCombinableMesh(combinedMesh);
|
||||
}
|
||||
|
||||
for (const auto &bmeshVertices: m_cacheContext->partBmeshVertices) {
|
||||
m_meshResultContext->bmeshVertices.insert(m_meshResultContext->bmeshVertices.end(),
|
||||
bmeshVertices.second.begin(), bmeshVertices.second.end());
|
||||
for (const auto &verticesSourcesIt: m_cacheContext->componentVerticesSources[QUuid().toString()].map()) {
|
||||
m_meshResultContext->bmeshVertices.push_back(verticesSourcesIt.second);
|
||||
}
|
||||
|
||||
for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "meshloader.h"
|
||||
#include "modelofflinerender.h"
|
||||
#include "meshresultcontext.h"
|
||||
#include "positionmap.h"
|
||||
|
||||
class GeneratedCacheContext
|
||||
{
|
||||
|
@ -19,6 +20,8 @@ public:
|
|||
std::map<QString, std::vector<BmeshVertex>> partBmeshVertices;
|
||||
std::map<QString, std::vector<BmeshNode>> partBmeshNodes;
|
||||
std::map<QString, void *> componentCombinableMeshs;
|
||||
std::map<QString, std::vector<QVector3D>> componentPositions;
|
||||
std::map<QString, PositionMap<BmeshVertex>> componentVerticesSources;
|
||||
void updateComponentCombinableMesh(QString componentId, void *mesh);
|
||||
};
|
||||
|
||||
|
@ -29,7 +32,6 @@ public:
|
|||
MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread);
|
||||
~MeshGenerator();
|
||||
void setSharedContextWidget(QOpenGLWidget *widget);
|
||||
//void addPreviewRequirement();
|
||||
void addPartPreviewRequirement(const QString &partId);
|
||||
void setGeneratedCacheContext(GeneratedCacheContext *cacheContext);
|
||||
MeshLoader *takeResultMesh();
|
||||
|
@ -59,6 +61,7 @@ private:
|
|||
std::map<QString, std::set<QString>> m_partEdgeIds;
|
||||
std::set<QString> m_dirtyComponentIds;
|
||||
std::set<QString> m_dirtyPartIds;
|
||||
PositionMap<std::pair<QString, int>> m_bmeshPartVerticesIndiciesMap;
|
||||
private:
|
||||
static bool m_enableDebug;
|
||||
private:
|
||||
|
|
|
@ -31,16 +31,13 @@ typedef CGAL::Surface_mesh<ExactKernel::Point_3> ExactMesh;
|
|||
typedef CGAL::Surface_mesh<SimpleKernel::Point_3> SimpleMesh;
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
template <class Kernel>
|
||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
|
||||
void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QVector3D> &positions)
|
||||
{
|
||||
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);
|
||||
int vertexCount = meshlite_get_vertex_count(meshliteContext, meshId);
|
||||
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;
|
||||
assert(vertexArrayLen == vertexCount * 3);
|
||||
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> vertices;
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
float x = vertexPositions[offset + 0];
|
||||
float y = vertexPositions[offset + 1];
|
||||
|
@ -51,7 +48,34 @@ typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(
|
|||
y = 0;
|
||||
if (std::isnan(z) || std::isinf(z))
|
||||
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;
|
||||
}
|
||||
int faceCount = meshlite_get_face_count(meshlite, meshId);
|
||||
|
@ -62,26 +86,51 @@ typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(
|
|||
while (i < filledLength) {
|
||||
int num = faceVertexNumAndIndices[i++];
|
||||
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;
|
||||
for (int j = 0; j < num; j++) {
|
||||
int index = faceVertexNumAndIndices[i++];
|
||||
assert(index >= 0 && index < vertexCount);
|
||||
faceVertexIndices.push_back(vertices[index]);
|
||||
}
|
||||
if (faceVertexIndices.size() >= 3) {
|
||||
mesh->add_face(faceVertexIndices);
|
||||
addedFaceCount++;
|
||||
if (oldToNewMap.find(index) == oldToNewMap.end()) {
|
||||
const auto &pos = oldPositions[index];
|
||||
oldToNewMap[index] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z()));
|
||||
}
|
||||
faceVertexIndices.push_back(oldToNewMap[index]);
|
||||
}
|
||||
mesh->add_face(faceVertexIndices);
|
||||
addedFaceCount++;
|
||||
}
|
||||
delete[] faceVertexNumAndIndices;
|
||||
delete[] vertexPositions;
|
||||
if (0 == addedFaceCount) {
|
||||
if (addedFaceCount < 3) {
|
||||
delete mesh;
|
||||
mesh = nullptr;
|
||||
}
|
||||
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://www.cgal.org/FAQ.html
|
||||
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)
|
||||
{
|
||||
#if USE_CGAL == 1
|
||||
CGAL::set_error_behaviour(CGAL::CONTINUE);
|
||||
std::vector<ExactMesh *> externalMeshs;
|
||||
for (size_t i = 0; i < meshIds.size(); 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);
|
||||
if (0 == meshlite_is_triangulated_manifold(meshliteContext, triangulatedMeshId)) {
|
||||
#if USE_CGAL == 1
|
||||
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
|
||||
int subdiviedMeshId = 0;
|
||||
SimpleMesh *simpleMesh = nullptr;
|
||||
try {
|
||||
|
@ -359,7 +406,7 @@ int fixMeshHoles(void *meshliteContext, int meshId)
|
|||
|
||||
void initMeshUtils()
|
||||
{
|
||||
CGAL::set_error_behaviour(CGAL::CONTINUE);
|
||||
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
|
||||
}
|
||||
|
||||
void *convertToCombinableMesh(void *meshliteContext, int meshId)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define MESH_UTIL_H
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <QVector3D>
|
||||
|
||||
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);
|
||||
|
@ -16,5 +17,7 @@ int convertFromCombinableMesh(void *meshliteContext, void *mesh);
|
|||
void deleteCombinableMesh(void *mesh);
|
||||
void *cloneCombinableMesh(void *mesh);
|
||||
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
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
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));
|
||||
if (result == m_map.end())
|
||||
|
@ -59,6 +59,16 @@ public:
|
|||
*data = result->second;
|
||||
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:
|
||||
std::map<PositionMapKey, T> m_map;
|
||||
|
|
|
@ -725,6 +725,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
|
|||
component["expanded"] = componentIt.second.expanded ? "true" : "false";
|
||||
component["inverse"] = componentIt.second.inverse ? "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;
|
||||
for (const auto &childId: componentIt.second.childrenIds) {
|
||||
childIdList.append(childId.toString());
|
||||
|
@ -862,6 +866,12 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
|
|||
component.name = valueOfKeyInMapOrEmpty(componentKv.second, "name");
|
||||
component.expanded = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "expanded"));
|
||||
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;
|
||||
if ("partId" == linkDataType) {
|
||||
QUuid partId = oldNewIdMap[QUuid(linkData)];
|
||||
|
@ -1284,6 +1294,32 @@ void SkeletonDocument::setComponentInverseState(QUuid componentId, bool inverse)
|
|||
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)
|
||||
{
|
||||
auto part = partMap.find(partId);
|
||||
|
|
|
@ -199,6 +199,8 @@ public:
|
|||
bool expanded = true;
|
||||
bool inverse = false;
|
||||
bool dirty = true;
|
||||
float smoothAll = 0.0;
|
||||
float smoothSeam = 0.0;
|
||||
std::vector<QUuid> childrenIds;
|
||||
QString linkData() const
|
||||
{
|
||||
|
@ -290,6 +292,34 @@ public:
|
|||
for (int i = index; i <= (int)childrenIds.size() - 2; i++)
|
||||
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:
|
||||
std::set<QUuid> m_childrenIdSet;
|
||||
};
|
||||
|
@ -307,6 +337,8 @@ signals:
|
|||
void componentRemoved(QUuid componentId);
|
||||
void componentAdded(QUuid componentId);
|
||||
void componentExpandStateChanged(QUuid componentId);
|
||||
void componentSmoothAllChanged(QUuid componentId);
|
||||
void componentSmoothSeamChanged(QUuid componentId);
|
||||
void nodeRemoved(QUuid nodeId);
|
||||
void edgeRemoved(QUuid edgeId);
|
||||
void nodeRadiusChanged(QUuid nodeId);
|
||||
|
@ -442,6 +474,8 @@ public slots:
|
|||
void setCurrentCanvasComponentId(QUuid componentId);
|
||||
void createNewComponentAndMoveThisIn(QUuid componentId);
|
||||
void setComponentExpandState(QUuid componentId, bool expanded);
|
||||
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
|
||||
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
||||
void hideOtherComponents(QUuid componentId);
|
||||
void lockOtherComponents(QUuid componentId);
|
||||
void hideAllComponents();
|
||||
|
|
|
@ -596,6 +596,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
|||
connect(partTreeWidget, &SkeletonPartTreeWidget::createNewComponentAndMoveThisIn, m_document, &SkeletonDocument::createNewComponentAndMoveThisIn);
|
||||
connect(partTreeWidget, &SkeletonPartTreeWidget::renameComponent, m_document, &SkeletonDocument::renameComponent);
|
||||
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::removeComponent, m_document, &SkeletonDocument::removeComponent);
|
||||
connect(partTreeWidget, &SkeletonPartTreeWidget::hideOtherComponents, m_document, &SkeletonDocument::hideOtherComponents);
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#include <QMenu>
|
||||
#include <vector>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QWidgetAction>
|
||||
#include "skeletonparttreewidget.h"
|
||||
#include "skeletonpartwidget.h"
|
||||
#include "skeletongraphicswidget.h"
|
||||
#include "floatnumberwidget.h"
|
||||
|
||||
SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent) :
|
||||
QTreeWidget(parent),
|
||||
|
@ -22,6 +26,7 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
|
|||
m_componentItemMap[QUuid()] = invisibleRootItem();
|
||||
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
//setIconSize(QSize(Theme::miniIconFontSize, Theme::miniIconFontSize));
|
||||
|
||||
setStyleSheet("QTreeView {qproperty-indentation: 10; margin-left: 5px; margin-top: 5px;}"
|
||||
"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:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-open.png);}");
|
||||
|
||||
QFont font;
|
||||
font.setWeight(QFont::Light);
|
||||
font.setPixelSize(9);
|
||||
font.setBold(false);
|
||||
setFont(font);
|
||||
m_normalFont.setWeight(QFont::Light);
|
||||
m_normalFont.setPixelSize(9);
|
||||
m_normalFont.setBold(false);
|
||||
|
||||
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::itemChanged, this, &SkeletonPartTreeWidget::groupChanged);
|
||||
connect(this, &QTreeWidget::itemExpanded, this, &SkeletonPartTreeWidget::groupExpanded);
|
||||
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) {
|
||||
auto componentId = QUuid(current->data(0, Qt::UserRole).toString());
|
||||
const SkeletonComponent *component = m_document->findComponent(componentId);
|
||||
if (nullptr != component) {
|
||||
emit currentComponentChanged(componentId);
|
||||
return;
|
||||
if (m_currentSelectedComponent != componentId) {
|
||||
if (!m_currentSelectedComponent.isNull()) {
|
||||
auto item = m_componentItemMap.find(m_currentSelectedComponent);
|
||||
if (item != m_componentItemMap.end()) {
|
||||
item->second->setFont(0, m_normalFont);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
QModelIndex item = indexAt(event->pos());
|
||||
if (item.isValid()) {
|
||||
QTreeView::mousePressEvent(event);
|
||||
} else {
|
||||
clearSelection();
|
||||
QTreeView::mousePressEvent(event);
|
||||
emit currentComponentChanged(QUuid());
|
||||
QModelIndex itemIndex = indexAt(event->pos());
|
||||
QTreeView::mousePressEvent(event);
|
||||
if (itemIndex.isValid()) {
|
||||
QTreeWidgetItem *item = itemFromIndex(itemIndex);
|
||||
auto componentId = QUuid(item->data(0, Qt::UserRole).toString());
|
||||
if (m_componentItemMap.find(componentId) != m_componentItemMap.end()) {
|
||||
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)
|
||||
|
@ -92,9 +118,13 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
contextMenu.addSection(component->name);
|
||||
|
||||
QAction showAction(tr("Show"), this);
|
||||
showAction.setIcon(Theme::awesome()->icon(fa::eye));
|
||||
QAction hideAction(tr("Hide"), this);
|
||||
hideAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
|
||||
QAction lockAction(tr("Lock"), this);
|
||||
lockAction.setIcon(Theme::awesome()->icon(fa::lock));
|
||||
QAction unlockAction(tr("Unlock"), this);
|
||||
unlockAction.setIcon(Theme::awesome()->icon(fa::unlock));
|
||||
QAction invertAction(tr("Invert"), this);
|
||||
QAction cancelInverseAction(tr("Cancel Inverse"), this);
|
||||
QAction selectAction(tr("Select"), this);
|
||||
|
@ -176,13 +206,22 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
|
||||
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);
|
||||
hideOthersAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
|
||||
connect(&hideOthersAction, &QAction::triggered, [=]() {
|
||||
emit hideOtherComponents(componentId);
|
||||
});
|
||||
contextMenu.addAction(&hideOthersAction);
|
||||
|
||||
QAction lockOthersAction(tr("Lock Others"), this);
|
||||
lockOthersAction.setIcon(Theme::awesome()->icon(fa::lock));
|
||||
connect(&lockOthersAction, &QAction::triggered, [=]() {
|
||||
emit lockOtherComponents(componentId);
|
||||
});
|
||||
|
@ -205,12 +244,14 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
contextMenu.addSeparator();
|
||||
|
||||
QAction hideAllAction(tr("Hide All"), this);
|
||||
hideAllAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
|
||||
connect(&hideAllAction, &QAction::triggered, [=]() {
|
||||
emit hideAllComponents();
|
||||
});
|
||||
contextMenu.addAction(&hideAllAction);
|
||||
|
||||
QAction showAllAction(tr("Show All"), this);
|
||||
showAllAction.setIcon(Theme::awesome()->icon(fa::eye));
|
||||
connect(&showAllAction, &QAction::triggered, [=]() {
|
||||
emit showAllComponents();
|
||||
});
|
||||
|
@ -219,12 +260,14 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
contextMenu.addSeparator();
|
||||
|
||||
QAction lockAllAction(tr("Lock All"), this);
|
||||
lockAllAction.setIcon(Theme::awesome()->icon(fa::lock));
|
||||
connect(&lockAllAction, &QAction::triggered, [=]() {
|
||||
emit lockAllComponents();
|
||||
});
|
||||
contextMenu.addAction(&lockAllAction);
|
||||
|
||||
QAction unlockAllAction(tr("Unlock All"), this);
|
||||
unlockAllAction.setIcon(Theme::awesome()->icon(fa::unlock));
|
||||
connect(&unlockAllAction, &QAction::triggered, [=]() {
|
||||
emit unlockAllComponents();
|
||||
});
|
||||
|
@ -234,25 +277,29 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
|
||||
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);
|
||||
moveUpAction.setIcon(Theme::awesome()->icon(fa::angleup));
|
||||
connect(&moveUpAction, &QAction::triggered, [=]() {
|
||||
emit moveComponentUp(componentId);
|
||||
});
|
||||
moveToMenu->addAction(&moveUpAction);
|
||||
|
||||
QAction moveDownAction(tr("Down"), this);
|
||||
moveDownAction.setIcon(Theme::awesome()->icon(fa::angledown));
|
||||
connect(&moveDownAction, &QAction::triggered, [=]() {
|
||||
emit moveComponentDown(componentId);
|
||||
});
|
||||
moveToMenu->addAction(&moveDownAction);
|
||||
|
||||
QAction moveToTopAction(tr("Top"), this);
|
||||
connect(&moveToTopAction, &QAction::triggered, [=]() {
|
||||
emit moveComponentToTop(componentId);
|
||||
});
|
||||
moveToMenu->addAction(&moveToTopAction);
|
||||
|
||||
QAction moveToBottomAction(tr("Bottom"), this);
|
||||
moveToBottomAction.setIcon(Theme::awesome()->icon(fa::angledoubledown));
|
||||
connect(&moveToBottomAction, &QAction::triggered, [=]() {
|
||||
emit moveComponentToBottom(componentId);
|
||||
});
|
||||
|
@ -261,6 +308,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
moveToMenu->addSeparator();
|
||||
|
||||
QAction moveToNewGroupAction(tr("New Group"), this);
|
||||
moveToNewGroupAction.setIcon(Theme::awesome()->icon(fa::file));
|
||||
moveToMenu->addAction(&moveToNewGroupAction);
|
||||
connect(&moveToNewGroupAction, &QAction::triggered, [=]() {
|
||||
emit createNewComponentAndMoveThisIn(componentId);
|
||||
|
@ -297,6 +345,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
contextMenu.addSeparator();
|
||||
|
||||
QAction deleteAction(tr("Delete"), this);
|
||||
deleteAction.setIcon(Theme::awesome()->icon(fa::trash));
|
||||
connect(&deleteAction, &QAction::triggered, [=]() {
|
||||
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)
|
||||
{
|
||||
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->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
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;
|
||||
addComponentChildrenToItem(childId, item);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ signals:
|
|||
void createNewComponentAndMoveThisIn(QUuid componentId);
|
||||
void renameComponent(QUuid componentId, QString name);
|
||||
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 removeComponent(QUuid componentId);
|
||||
void hideOtherComponents(QUuid componentId);
|
||||
|
@ -38,6 +40,7 @@ signals:
|
|||
void lockDescendantComponents(QUuid componentId);
|
||||
void unlockDescendantComponents(QUuid componentId);
|
||||
void addPartToSelection(QUuid partId);
|
||||
void groupOperationAdded();
|
||||
public:
|
||||
SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent);
|
||||
QTreeWidgetItem *findComponentItem(QUuid componentId);
|
||||
|
@ -64,7 +67,6 @@ public slots:
|
|||
void groupChanged(QTreeWidgetItem *item, int column);
|
||||
void groupExpanded(QTreeWidgetItem *item);
|
||||
void groupCollapsed(QTreeWidgetItem *item);
|
||||
void currentGroupChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
|
||||
void removeAllContent();
|
||||
void showContextMenu(const QPoint &pos);
|
||||
protected:
|
||||
|
@ -74,11 +76,16 @@ protected:
|
|||
private:
|
||||
void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem);
|
||||
void deleteItemChildren(QTreeWidgetItem *item);
|
||||
void selectComponent(QUuid componentId);
|
||||
QWidget *createSmoothMenuWidget(QUuid componentId);
|
||||
private:
|
||||
const SkeletonDocument *m_document = nullptr;
|
||||
std::map<QUuid, QTreeWidgetItem *> m_partItemMap;
|
||||
std::map<QUuid, QTreeWidgetItem *> m_componentItemMap;
|
||||
SkeletonGraphicsFunctions *m_graphicsFunctions = nullptr;
|
||||
QFont m_normalFont;
|
||||
QFont m_selectedFont;
|
||||
QUuid m_currentSelectedComponent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -263,15 +263,12 @@ void SkeletonPartWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
|||
|
||||
void SkeletonPartWidget::initToolButtonWithoutFont(QPushButton *button)
|
||||
{
|
||||
button->setFixedSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2);
|
||||
button->setStyleSheet("QPushButton {color: #f7d9c8}");
|
||||
button->setFocusPolicy(Qt::NoFocus);
|
||||
Theme::initAwesomeToolButtonWithoutFont(button);
|
||||
}
|
||||
|
||||
void SkeletonPartWidget::initToolButton(QPushButton *button)
|
||||
{
|
||||
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize / 2));
|
||||
initToolButtonWithoutFont(button);
|
||||
Theme::initAwesomeToolButton(button);
|
||||
}
|
||||
|
||||
void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
|
||||
|
@ -289,10 +286,6 @@ void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
|
|||
QPushButton *colorEraser = new QPushButton(QChar(fa::eraser));
|
||||
initToolButton(colorEraser);
|
||||
|
||||
//QLabel *colorPreviewLabel = new QLabel;
|
||||
//colorPreviewLabel->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
|
||||
//colorPreviewLabel->setAutoFillBackground(true);
|
||||
|
||||
QPushButton *pickButton = new QPushButton();
|
||||
initToolButtonWithoutFont(pickButton);
|
||||
QPalette palette = pickButton->palette();
|
||||
|
@ -343,14 +336,17 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
|||
QWidget *popup = new QWidget;
|
||||
|
||||
FloatNumberWidget *thicknessWidget = new FloatNumberWidget;
|
||||
thicknessWidget->setItemName(tr("Thickness"));
|
||||
thicknessWidget->setRange(0, 2);
|
||||
thicknessWidget->setValue(part->deformThickness);
|
||||
|
||||
connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
||||
emit setPartDeformThickness(m_partId, value);
|
||||
emit groupOperationAdded();
|
||||
});
|
||||
|
||||
FloatNumberWidget *widthWidget = new FloatNumberWidget;
|
||||
widthWidget->setItemName(tr("Width"));
|
||||
widthWidget->setRange(0, 2);
|
||||
widthWidget->setValue(part->deformWidth);
|
||||
|
||||
|
@ -364,6 +360,7 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
|||
|
||||
connect(thicknessEraser, &QPushButton::clicked, [=]() {
|
||||
thicknessWidget->setValue(1.0);
|
||||
emit groupOperationAdded();
|
||||
});
|
||||
|
||||
QPushButton *widthEraser = new QPushButton(QChar(fa::eraser));
|
||||
|
@ -371,6 +368,7 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
|||
|
||||
connect(widthEraser, &QPushButton::clicked, [=]() {
|
||||
widthWidget->setValue(1.0);
|
||||
emit groupOperationAdded();
|
||||
});
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
|
@ -385,35 +383,22 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
|
|||
|
||||
popup->setLayout(layout);
|
||||
|
||||
QWidgetAction *action = new QWidgetAction(this);
|
||||
action->setDefaultWidget(popup);
|
||||
QWidgetAction action(this);
|
||||
action.setDefaultWidget(popup);
|
||||
|
||||
popupMenu.addAction(action);
|
||||
popupMenu.addAction(&action);
|
||||
|
||||
popupMenu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void SkeletonPartWidget::initButton(QPushButton *button)
|
||||
{
|
||||
button->setFont(Theme::awesome()->font(Theme::miniIconFontSize));
|
||||
button->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
|
||||
button->setFocusPolicy(Qt::NoFocus);
|
||||
Theme::initAwesomeMiniButton(button);
|
||||
}
|
||||
|
||||
void SkeletonPartWidget::updateButton(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() + ";}");
|
||||
Theme::updateAwesomeMiniButton(button, icon, highlighted);
|
||||
}
|
||||
|
||||
void SkeletonPartWidget::updatePreview()
|
||||
|
|
|
@ -92,4 +92,39 @@ void Theme::initAwesomeLabel(QLabel *label)
|
|||
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 initAwesomeLabel(QLabel *label);
|
||||
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
|
||||
|
|
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_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_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
|
||||
}
|
||||
|
|
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_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_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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue