Add cut face customizing.
This commit introduce a new tool, allow user to customize the face template for automatical extruding.master
parent
7491679425
commit
02bb32391e
|
@ -287,6 +287,12 @@ HEADERS += src/posedocument.h
|
||||||
SOURCES += src/combinemode.cpp
|
SOURCES += src/combinemode.cpp
|
||||||
HEADERS += src/combinemode.h
|
HEADERS += src/combinemode.h
|
||||||
|
|
||||||
|
SOURCES += src/cutdocument.cpp
|
||||||
|
HEADERS += src/cutdocument.h
|
||||||
|
|
||||||
|
SOURCES += src/cuttemplate.cpp
|
||||||
|
HEADERS += src/cuttemplate.h
|
||||||
|
|
||||||
SOURCES += src/main.cpp
|
SOURCES += src/main.cpp
|
||||||
|
|
||||||
HEADERS += src/version.h
|
HEADERS += src/version.h
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QApplication>
|
||||||
|
#include "cutdocument.h"
|
||||||
|
|
||||||
|
const float CutDocument::m_nodeRadius = 0.05;
|
||||||
|
const float CutDocument::m_nodeScaleFactor = 0.5;
|
||||||
|
|
||||||
|
bool CutDocument::hasPastableNodesInClipboard() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CutDocument::originSettled() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CutDocument::isNodeEditable(QUuid nodeId) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(nodeId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CutDocument::isEdgeEditable(QUuid edgeId) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(edgeId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::copyNodes(std::set<QUuid> nodeIdSet) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(nodeIdSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::saveHistoryItem()
|
||||||
|
{
|
||||||
|
CutHistoryItem item;
|
||||||
|
toCutTemplate(item.cutTemplate);
|
||||||
|
m_undoItems.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CutDocument::undoable() const
|
||||||
|
{
|
||||||
|
return m_undoItems.size() >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CutDocument::redoable() const
|
||||||
|
{
|
||||||
|
return !m_redoItems.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::undo()
|
||||||
|
{
|
||||||
|
if (!undoable())
|
||||||
|
return;
|
||||||
|
m_redoItems.push_back(m_undoItems.back());
|
||||||
|
m_undoItems.pop_back();
|
||||||
|
const auto &item = m_undoItems.back();
|
||||||
|
fromCutTemplate(item.cutTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::redo()
|
||||||
|
{
|
||||||
|
if (m_redoItems.empty())
|
||||||
|
return;
|
||||||
|
m_undoItems.push_back(m_redoItems.back());
|
||||||
|
const auto &item = m_redoItems.back();
|
||||||
|
fromCutTemplate(item.cutTemplate);
|
||||||
|
m_redoItems.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::paste()
|
||||||
|
{
|
||||||
|
// void
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::reset()
|
||||||
|
{
|
||||||
|
nodeMap.clear();
|
||||||
|
edgeMap.clear();
|
||||||
|
partMap.clear();
|
||||||
|
m_cutNodeIds.clear();
|
||||||
|
emit cleanup();
|
||||||
|
emit cutTemplateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::clearHistories()
|
||||||
|
{
|
||||||
|
m_undoItems.clear();
|
||||||
|
m_redoItems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::toCutTemplate(std::vector<QVector2D> &cutTemplate)
|
||||||
|
{
|
||||||
|
for (const auto &nodeId: m_cutNodeIds) {
|
||||||
|
auto findNode = nodeMap.find(nodeId);
|
||||||
|
if (findNode == nodeMap.end())
|
||||||
|
continue;
|
||||||
|
QVector2D position = nodeToCutPosition({findNode->second.x,
|
||||||
|
findNode->second.y,
|
||||||
|
findNode->second.z
|
||||||
|
});
|
||||||
|
cutTemplate.push_back(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector2D CutDocument::nodeToCutPosition(const QVector3D &nodePosition)
|
||||||
|
{
|
||||||
|
return {(nodePosition.x() * 2 - (float)1) / m_nodeScaleFactor,
|
||||||
|
(nodePosition.y() * 2 - (float)1) / m_nodeScaleFactor};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D CutDocument::cutToNodePosition(const QVector2D &cutPosition)
|
||||||
|
{
|
||||||
|
return {(cutPosition.x() * m_nodeScaleFactor + 1) * (float)0.5,
|
||||||
|
(cutPosition.y() * m_nodeScaleFactor + 1) * (float)0.5,
|
||||||
|
(float)0};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::fromCutTemplate(const std::vector<QVector2D> &cutTemplate)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
|
||||||
|
std::set<QUuid> newAddedNodeIds;
|
||||||
|
std::set<QUuid> newAddedEdgeIds;
|
||||||
|
|
||||||
|
m_partId = QUuid::createUuid();
|
||||||
|
auto &part = partMap[m_partId];
|
||||||
|
part.id = m_partId;
|
||||||
|
|
||||||
|
for (const auto &position: cutTemplate) {
|
||||||
|
SkeletonNode node;
|
||||||
|
node.partId = m_partId;
|
||||||
|
node.id = QUuid::createUuid();
|
||||||
|
node.setRadius(m_nodeRadius);
|
||||||
|
auto nodePosition = cutToNodePosition(position);
|
||||||
|
node.x = nodePosition.x();
|
||||||
|
node.y = nodePosition.y();
|
||||||
|
node.z = nodePosition.z();
|
||||||
|
nodeMap[node.id] = node;
|
||||||
|
newAddedNodeIds.insert(node.id);
|
||||||
|
m_cutNodeIds.push_back(node.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_cutNodeIds.size(); ++i) {
|
||||||
|
size_t j = (i + 1) % m_cutNodeIds.size();
|
||||||
|
const QUuid &firstNodeId = m_cutNodeIds[i];
|
||||||
|
const QUuid &secondNodeId = m_cutNodeIds[j];
|
||||||
|
|
||||||
|
SkeletonEdge edge;
|
||||||
|
edge.partId = m_partId;
|
||||||
|
edge.id = QUuid::createUuid();
|
||||||
|
edge.nodeIds.push_back(firstNodeId);
|
||||||
|
edge.nodeIds.push_back(secondNodeId);
|
||||||
|
edgeMap[edge.id] = edge;
|
||||||
|
newAddedEdgeIds.insert(edge.id);
|
||||||
|
nodeMap[firstNodeId].edgeIds.push_back(edge.id);
|
||||||
|
nodeMap[secondNodeId].edgeIds.push_back(edge.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &nodeIt: newAddedNodeIds) {
|
||||||
|
emit nodeAdded(nodeIt);
|
||||||
|
}
|
||||||
|
for (const auto &edgeIt: newAddedEdgeIds) {
|
||||||
|
emit edgeAdded(edgeIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit cutTemplateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.x += x;
|
||||||
|
it->second.y += y;
|
||||||
|
it->second.z += z;
|
||||||
|
emit nodeOriginChanged(it->first);
|
||||||
|
emit cutTemplateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.x = x;
|
||||||
|
it->second.y = y;
|
||||||
|
it->second.z = z;
|
||||||
|
auto part = partMap.find(it->second.partId);
|
||||||
|
if (part != partMap.end())
|
||||||
|
part->second.dirty = true;
|
||||||
|
emit nodeOriginChanged(nodeId);
|
||||||
|
emit cutTemplateChanged();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#ifndef DUST3D_CUT_DOCUMENT_H
|
||||||
|
#define DUST3D_CUT_DOCUMENT_H
|
||||||
|
#include <deque>
|
||||||
|
#include <QVector2D>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
|
||||||
|
struct CutHistoryItem
|
||||||
|
{
|
||||||
|
std::vector<QVector2D> cutTemplate;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CutDocument : public SkeletonDocument
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void cleanup();
|
||||||
|
void nodeAdded(QUuid nodeId);
|
||||||
|
void edgeAdded(QUuid edgeId);
|
||||||
|
void nodeOriginChanged(QUuid nodeId);
|
||||||
|
void cutTemplateChanged();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool undoable() const override;
|
||||||
|
bool redoable() const override;
|
||||||
|
bool hasPastableNodesInClipboard() const override;
|
||||||
|
bool originSettled() const override;
|
||||||
|
bool isNodeEditable(QUuid nodeId) const override;
|
||||||
|
bool isEdgeEditable(QUuid edgeId) const override;
|
||||||
|
void copyNodes(std::set<QUuid> nodeIdSet) const override;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void toCutTemplate(std::vector<QVector2D> &cutTemplate);
|
||||||
|
void fromCutTemplate(const std::vector<QVector2D> &cutTemplate);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void saveHistoryItem();
|
||||||
|
void clearHistories();
|
||||||
|
void undo() override;
|
||||||
|
void redo() override;
|
||||||
|
void paste() override;
|
||||||
|
|
||||||
|
void moveNodeBy(QUuid nodeId, float x, float y, float z);
|
||||||
|
void setNodeOrigin(QUuid nodeId, float x, float y, float z);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const float m_nodeRadius;
|
||||||
|
static const float m_nodeScaleFactor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<CutHistoryItem> m_undoItems;
|
||||||
|
std::deque<CutHistoryItem> m_redoItems;
|
||||||
|
std::vector<QUuid> m_cutNodeIds;
|
||||||
|
QUuid m_partId;
|
||||||
|
|
||||||
|
QVector2D nodeToCutPosition(const QVector3D &nodePosition);
|
||||||
|
QVector3D cutToNodePosition(const QVector2D &cutPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include <QStringList>
|
||||||
|
#include "cuttemplate.h"
|
||||||
|
|
||||||
|
IMPL_CutTemplateToDispName
|
||||||
|
TMPL_CutTemplateToPoints
|
||||||
|
|
||||||
|
static const auto g_defaultCutTemplate = CutTemplateToPoints(CutTemplate::Quad);
|
||||||
|
|
||||||
|
bool cutTemplatePointsCompare(const std::vector<QVector2D> &first, const std::vector<QVector2D> &second)
|
||||||
|
{
|
||||||
|
if (first.size() != second.size())
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < first.size(); ++i) {
|
||||||
|
if (!qFuzzyCompare(first[i], second[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString cutTemplatePointsToString(const std::vector<QVector2D> &points)
|
||||||
|
{
|
||||||
|
QStringList items;
|
||||||
|
for (const auto &point: points) {
|
||||||
|
items.append(QString::number(point.x()));
|
||||||
|
items.append(QString::number(point.y()));
|
||||||
|
}
|
||||||
|
return items.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QVector2D> cutTemplatePointsFromString(const QString &pointsString)
|
||||||
|
{
|
||||||
|
std::vector<float> numbers;
|
||||||
|
for (const auto &item: pointsString.split(",")) {
|
||||||
|
numbers.push_back(item.toFloat());
|
||||||
|
}
|
||||||
|
if (0 != numbers.size() % 2)
|
||||||
|
return g_defaultCutTemplate;
|
||||||
|
size_t pointsNum = numbers.size() / 2;
|
||||||
|
if (pointsNum < 3)
|
||||||
|
return g_defaultCutTemplate;
|
||||||
|
size_t numberIndex = 0;
|
||||||
|
std::vector<QVector2D> points;
|
||||||
|
for (size_t i = 0; i < pointsNum; ++i) {
|
||||||
|
const auto &x = numbers[numberIndex++];
|
||||||
|
const auto &y = numbers[numberIndex++];
|
||||||
|
points.push_back({x, y});
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalizeCutTemplatePoints(std::vector<QVector2D> *points)
|
||||||
|
{
|
||||||
|
QVector2D center;
|
||||||
|
if (nullptr == points || points->empty())
|
||||||
|
return;
|
||||||
|
float xLow = std::numeric_limits<float>::max();
|
||||||
|
float xHigh = std::numeric_limits<float>::lowest();
|
||||||
|
float yLow = std::numeric_limits<float>::max();
|
||||||
|
float yHigh = std::numeric_limits<float>::lowest();
|
||||||
|
for (const auto &position: *points) {
|
||||||
|
if (position.x() < xLow)
|
||||||
|
xLow = position.x();
|
||||||
|
else if (position.x() > xHigh)
|
||||||
|
xHigh = position.x();
|
||||||
|
if (position.y() < yLow)
|
||||||
|
yLow = position.y();
|
||||||
|
else if (position.y() > yHigh)
|
||||||
|
yHigh = position.y();
|
||||||
|
}
|
||||||
|
float xMiddle = (xHigh + xLow) * 0.5;
|
||||||
|
float yMiddle = (yHigh + yLow) * 0.5;
|
||||||
|
float xSize = xHigh - xLow;
|
||||||
|
float ySize = yHigh - yLow;
|
||||||
|
float longSize = ySize;
|
||||||
|
if (xSize > longSize)
|
||||||
|
longSize = xSize;
|
||||||
|
if (qFuzzyIsNull(longSize))
|
||||||
|
longSize = 0.000001;
|
||||||
|
for (auto &position: *points) {
|
||||||
|
position.setX((position.x() - xMiddle) * 2 / longSize);
|
||||||
|
position.setY((position.y() - yMiddle) * 2 / longSize);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef CUT_TEMPLATE_H
|
||||||
|
#define CUT_TEMPLATE_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector2D>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
enum class CutTemplate
|
||||||
|
{
|
||||||
|
Quad = 0,
|
||||||
|
//Octagon,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
QString CutTemplateToDispName(CutTemplate cutTemplate);
|
||||||
|
#define IMPL_CutTemplateToDispName \
|
||||||
|
QString CutTemplateToDispName(CutTemplate cutTemplate) \
|
||||||
|
{ \
|
||||||
|
switch (cutTemplate) { \
|
||||||
|
case CutTemplate::Quad: \
|
||||||
|
return QObject::tr("Quad"); \
|
||||||
|
default: \
|
||||||
|
return ""; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
std::vector<QVector2D> CutTemplateToPoints(CutTemplate cutTemplate);
|
||||||
|
#define TMPL_CutTemplateToPoints \
|
||||||
|
std::vector<QVector2D> CutTemplateToPoints(CutTemplate cutTemplate) \
|
||||||
|
{ \
|
||||||
|
switch (cutTemplate) { \
|
||||||
|
case CutTemplate::Quad: \
|
||||||
|
return { \
|
||||||
|
{-1.0, -1.0}, \
|
||||||
|
{ 1.0, -1.0}, \
|
||||||
|
{ 1.0, 1.0}, \
|
||||||
|
{-1.0, 1.0}, \
|
||||||
|
}; \
|
||||||
|
default: \
|
||||||
|
return { \
|
||||||
|
{-1.0, -1.0}, \
|
||||||
|
{ 1.0, -1.0}, \
|
||||||
|
{ 1.0, 1.0}, \
|
||||||
|
{-1.0, 1.0}, \
|
||||||
|
}; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cutTemplatePointsCompare(const std::vector<QVector2D> &first, const std::vector<QVector2D> &second);
|
||||||
|
QString cutTemplatePointsToString(const std::vector<QVector2D> &points);
|
||||||
|
std::vector<QVector2D> cutTemplatePointsFromString(const QString &pointsString);
|
||||||
|
void normalizeCutTemplatePoints(std::vector<QVector2D> *points);
|
||||||
|
|
||||||
|
#endif
|
|
@ -861,6 +861,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
|
||||||
part["rounded"] = partIt.second.rounded ? "true" : "false";
|
part["rounded"] = partIt.second.rounded ? "true" : "false";
|
||||||
if (partIt.second.cutRotationAdjusted())
|
if (partIt.second.cutRotationAdjusted())
|
||||||
part["cutRotation"] = QString::number(partIt.second.cutRotation);
|
part["cutRotation"] = QString::number(partIt.second.cutRotation);
|
||||||
|
if (partIt.second.cutTemplateAdjusted())
|
||||||
|
part["cutTemplate"] = cutTemplatePointsToString(partIt.second.cutTemplate);
|
||||||
part["dirty"] = partIt.second.dirty ? "true" : "false";
|
part["dirty"] = partIt.second.dirty ? "true" : "false";
|
||||||
if (partIt.second.hasColor)
|
if (partIt.second.hasColor)
|
||||||
part["color"] = partIt.second.color.name();
|
part["color"] = partIt.second.color.name();
|
||||||
|
@ -1118,6 +1120,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
||||||
const auto &cutRotationIt = partKv.second.find("cutRotation");
|
const auto &cutRotationIt = partKv.second.find("cutRotation");
|
||||||
if (cutRotationIt != partKv.second.end())
|
if (cutRotationIt != partKv.second.end())
|
||||||
part.setCutRotation(cutRotationIt->second.toFloat());
|
part.setCutRotation(cutRotationIt->second.toFloat());
|
||||||
|
const auto &cutTemplateIt = partKv.second.find("cutTemplate");
|
||||||
|
if (cutTemplateIt != partKv.second.end())
|
||||||
|
part.setCutTemplate(cutTemplatePointsFromString(cutTemplateIt->second));
|
||||||
if (isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "inverse")))
|
if (isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "inverse")))
|
||||||
inversePartIds.insert(part.id);
|
inversePartIds.insert(part.id);
|
||||||
const auto &colorIt = partKv.second.find("color");
|
const auto &colorIt = partKv.second.find("color");
|
||||||
|
@ -2200,12 +2205,29 @@ void Document::setPartCutRotation(QUuid partId, float cutRotation)
|
||||||
qDebug() << "Part not found:" << partId;
|
qDebug() << "Part not found:" << partId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (qFuzzyCompare(cutRotation, part->second.cutRotation))
|
||||||
|
return;
|
||||||
part->second.setCutRotation(cutRotation);
|
part->second.setCutRotation(cutRotation);
|
||||||
part->second.dirty = true;
|
part->second.dirty = true;
|
||||||
emit partCutRotationChanged(partId);
|
emit partCutRotationChanged(partId);
|
||||||
emit skeletonChanged();
|
emit skeletonChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::setPartCutTemplate(QUuid partId, std::vector<QVector2D> cutTemplate)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cutTemplatePointsCompare(cutTemplate, part->second.cutTemplate))
|
||||||
|
return;
|
||||||
|
part->second.setCutTemplate(cutTemplate);
|
||||||
|
part->second.dirty = true;
|
||||||
|
emit partCutTemplateChanged(partId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void Document::setPartColorState(QUuid partId, bool hasColor, QColor color)
|
void Document::setPartColorState(QUuid partId, bool hasColor, QColor color)
|
||||||
{
|
{
|
||||||
auto part = partMap.find(partId);
|
auto part = partMap.find(partId);
|
||||||
|
|
|
@ -401,6 +401,7 @@ signals:
|
||||||
void partRoundStateChanged(QUuid partId);
|
void partRoundStateChanged(QUuid partId);
|
||||||
void partColorStateChanged(QUuid partId);
|
void partColorStateChanged(QUuid partId);
|
||||||
void partCutRotationChanged(QUuid partId);
|
void partCutRotationChanged(QUuid partId);
|
||||||
|
void partCutTemplateChanged(QUuid partId);
|
||||||
void partMaterialIdChanged(QUuid partId);
|
void partMaterialIdChanged(QUuid partId);
|
||||||
void componentCombineModeChanged(QUuid componentId);
|
void componentCombineModeChanged(QUuid componentId);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
@ -554,6 +555,7 @@ public slots:
|
||||||
void setPartRoundState(QUuid partId, bool rounded);
|
void setPartRoundState(QUuid partId, bool rounded);
|
||||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||||
void setPartCutRotation(QUuid partId, float cutRotation);
|
void setPartCutRotation(QUuid partId, float cutRotation);
|
||||||
|
void setPartCutTemplate(QUuid partId, std::vector<QVector2D> cutTemplate);
|
||||||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||||
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
||||||
void moveComponentUp(QUuid componentId);
|
void moveComponentUp(QUuid componentId);
|
||||||
|
|
|
@ -835,8 +835,9 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(m_document, &Document::partDeformThicknessChanged, partTreeWidget, &PartTreeWidget::partDeformChanged);
|
connect(m_document, &Document::partDeformThicknessChanged, partTreeWidget, &PartTreeWidget::partDeformChanged);
|
||||||
connect(m_document, &Document::partDeformWidthChanged, partTreeWidget, &PartTreeWidget::partDeformChanged);
|
connect(m_document, &Document::partDeformWidthChanged, partTreeWidget, &PartTreeWidget::partDeformChanged);
|
||||||
connect(m_document, &Document::partRoundStateChanged, partTreeWidget, &PartTreeWidget::partRoundStateChanged);
|
connect(m_document, &Document::partRoundStateChanged, partTreeWidget, &PartTreeWidget::partRoundStateChanged);
|
||||||
connect(m_document, &Document::partCutRotationChanged, partTreeWidget, &PartTreeWidget::partWrapStateChanged);
|
|
||||||
connect(m_document, &Document::partColorStateChanged, partTreeWidget, &PartTreeWidget::partColorStateChanged);
|
connect(m_document, &Document::partColorStateChanged, partTreeWidget, &PartTreeWidget::partColorStateChanged);
|
||||||
|
connect(m_document, &Document::partCutRotationChanged, partTreeWidget, &PartTreeWidget::partCutRotationChanged);
|
||||||
|
connect(m_document, &Document::partCutTemplateChanged, partTreeWidget, &PartTreeWidget::partCutTemplateChanged);
|
||||||
connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged);
|
connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged);
|
||||||
connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved);
|
connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved);
|
||||||
connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
|
connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
|
||||||
|
|
|
@ -10,13 +10,7 @@
|
||||||
#include "meshgenerator.h"
|
#include "meshgenerator.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "trianglesourcenoderesolve.h"
|
#include "trianglesourcenoderesolve.h"
|
||||||
|
#include "cuttemplate.h"
|
||||||
const std::vector<QVector2D> g_defaultCutTemplate = {
|
|
||||||
{-1.0, -1.0},
|
|
||||||
{ 1.0, -1.0},
|
|
||||||
{ 1.0, 1.0},
|
|
||||||
{-1.0, 1.0},
|
|
||||||
};
|
|
||||||
|
|
||||||
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
||||||
m_snapshot(snapshot)
|
m_snapshot(snapshot)
|
||||||
|
@ -155,7 +149,8 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
||||||
float deformWidth = 1.0;
|
float deformWidth = 1.0;
|
||||||
float cutRotation = 0.0;
|
float cutRotation = 0.0;
|
||||||
|
|
||||||
std::vector<QVector2D> cutTemplate = g_defaultCutTemplate;
|
std::vector<QVector2D> cutTemplate = cutTemplatePointsFromString(valueOfKeyInMapOrEmpty(part, "cutTemplate"));
|
||||||
|
normalizeCutTemplatePoints(&cutTemplate);
|
||||||
QString cutRotationString = valueOfKeyInMapOrEmpty(part, "cutRotation");
|
QString cutRotationString = valueOfKeyInMapOrEmpty(part, "cutRotation");
|
||||||
if (!cutRotationString.isEmpty()) {
|
if (!cutRotationString.isEmpty()) {
|
||||||
cutRotation = cutRotationString.toFloat();
|
cutRotation = cutRotationString.toFloat();
|
||||||
|
|
|
@ -49,7 +49,7 @@ PartTreeWidget::PartTreeWidget(const Document *document, QWidget *parent) :
|
||||||
|
|
||||||
setFont(m_normalFont);
|
setFont(m_normalFont);
|
||||||
|
|
||||||
QRadialGradient gradient(QPointF(0.2, 0.3), 0.3);
|
QRadialGradient gradient(QPointF(0.115, 0.3), 0.3);
|
||||||
QColor fillColor = QColor(0xfb, 0xf9, 0x87);
|
QColor fillColor = QColor(0xfb, 0xf9, 0x87);
|
||||||
fillColor.setAlphaF(0.85);
|
fillColor.setAlphaF(0.85);
|
||||||
gradient.setCoordinateMode(QGradient::StretchToDeviceMode);
|
gradient.setCoordinateMode(QGradient::StretchToDeviceMode);
|
||||||
|
@ -910,7 +910,18 @@ void PartTreeWidget::partRoundStateChanged(QUuid partId)
|
||||||
widget->updateRoundButton();
|
widget->updateRoundButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartTreeWidget::partWrapStateChanged(QUuid partId)
|
void PartTreeWidget::partColorStateChanged(QUuid partId)
|
||||||
|
{
|
||||||
|
auto item = m_partItemMap.find(partId);
|
||||||
|
if (item == m_partItemMap.end()) {
|
||||||
|
qDebug() << "Part item not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
|
||||||
|
widget->updateColorButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PartTreeWidget::partCutRotationChanged(QUuid partId)
|
||||||
{
|
{
|
||||||
auto item = m_partItemMap.find(partId);
|
auto item = m_partItemMap.find(partId);
|
||||||
if (item == m_partItemMap.end()) {
|
if (item == m_partItemMap.end()) {
|
||||||
|
@ -921,7 +932,7 @@ void PartTreeWidget::partWrapStateChanged(QUuid partId)
|
||||||
widget->updateCutRotationButton();
|
widget->updateCutRotationButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartTreeWidget::partColorStateChanged(QUuid partId)
|
void PartTreeWidget::partCutTemplateChanged(QUuid partId)
|
||||||
{
|
{
|
||||||
auto item = m_partItemMap.find(partId);
|
auto item = m_partItemMap.find(partId);
|
||||||
if (item == m_partItemMap.end()) {
|
if (item == m_partItemMap.end()) {
|
||||||
|
@ -929,7 +940,7 @@ void PartTreeWidget::partColorStateChanged(QUuid partId)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
|
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
|
||||||
widget->updateColorButton();
|
widget->updateCutTemplateButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartTreeWidget::partMaterialIdChanged(QUuid partId)
|
void PartTreeWidget::partMaterialIdChanged(QUuid partId)
|
||||||
|
|
|
@ -59,8 +59,9 @@ public slots:
|
||||||
void partXmirrorStateChanged(QUuid partId);
|
void partXmirrorStateChanged(QUuid partId);
|
||||||
void partDeformChanged(QUuid partId);
|
void partDeformChanged(QUuid partId);
|
||||||
void partRoundStateChanged(QUuid partId);
|
void partRoundStateChanged(QUuid partId);
|
||||||
void partWrapStateChanged(QUuid partId);
|
|
||||||
void partColorStateChanged(QUuid partId);
|
void partColorStateChanged(QUuid partId);
|
||||||
|
void partCutRotationChanged(QUuid partId);
|
||||||
|
void partCutTemplateChanged(QUuid partId);
|
||||||
void partMaterialIdChanged(QUuid partId);
|
void partMaterialIdChanged(QUuid partId);
|
||||||
void partChecked(QUuid partId);
|
void partChecked(QUuid partId);
|
||||||
void partUnchecked(QUuid partId);
|
void partUnchecked(QUuid partId);
|
||||||
|
|
|
@ -6,11 +6,17 @@
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
#include <QSizePolicy>
|
#include <QSizePolicy>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QSizePolicy>
|
||||||
#include "partwidget.h"
|
#include "partwidget.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "floatnumberwidget.h"
|
#include "floatnumberwidget.h"
|
||||||
#include "materiallistwidget.h"
|
#include "materiallistwidget.h"
|
||||||
#include "infolabel.h"
|
#include "infolabel.h"
|
||||||
|
#include "cuttemplate.h"
|
||||||
|
#include "cutdocument.h"
|
||||||
|
#include "skeletongraphicswidget.h"
|
||||||
|
#include "shortcuts.h"
|
||||||
|
#include "graphicscontainerwidget.h"
|
||||||
|
|
||||||
PartWidget::PartWidget(const Document *document, QUuid partId) :
|
PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
m_document(document),
|
m_document(document),
|
||||||
|
@ -31,7 +37,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
initButton(m_lockButton);
|
initButton(m_lockButton);
|
||||||
|
|
||||||
m_subdivButton = new QPushButton();
|
m_subdivButton = new QPushButton();
|
||||||
m_subdivButton->setToolTip(tr("Square/Round"));
|
m_subdivButton->setToolTip(tr("Subdivide"));
|
||||||
m_subdivButton->setSizePolicy(retainSizePolicy);
|
m_subdivButton->setSizePolicy(retainSizePolicy);
|
||||||
initButton(m_subdivButton);
|
initButton(m_subdivButton);
|
||||||
|
|
||||||
|
@ -60,10 +66,15 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
m_colorButton->setSizePolicy(retainSizePolicy);
|
m_colorButton->setSizePolicy(retainSizePolicy);
|
||||||
initButton(m_colorButton);
|
initButton(m_colorButton);
|
||||||
|
|
||||||
m_cutButton = new QPushButton;
|
m_cutRotationButton = new QPushButton;
|
||||||
m_cutButton->setToolTip(tr("Rotation"));
|
m_cutRotationButton->setToolTip(tr("Cut rotation"));
|
||||||
m_cutButton->setSizePolicy(retainSizePolicy);
|
m_cutRotationButton->setSizePolicy(retainSizePolicy);
|
||||||
initButton(m_cutButton);
|
initButton(m_cutRotationButton);
|
||||||
|
|
||||||
|
m_cutTemplateButton = new QPushButton;
|
||||||
|
m_cutTemplateButton->setToolTip(tr("Cut template"));
|
||||||
|
m_cutTemplateButton->setSizePolicy(retainSizePolicy);
|
||||||
|
initButton(m_cutTemplateButton);
|
||||||
|
|
||||||
m_previewWidget = new ModelWidget;
|
m_previewWidget = new ModelWidget;
|
||||||
m_previewWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
m_previewWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
@ -88,6 +99,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
toolsLayout->setContentsMargins(0, 0, 5, 0);
|
toolsLayout->setContentsMargins(0, 0, 5, 0);
|
||||||
int row = 0;
|
int row = 0;
|
||||||
int col = 0;
|
int col = 0;
|
||||||
|
toolsLayout->addWidget(m_visibleButton, row, col++, Qt::AlignBottom);
|
||||||
toolsLayout->addWidget(m_lockButton, row, col++, Qt::AlignBottom);
|
toolsLayout->addWidget(m_lockButton, row, col++, Qt::AlignBottom);
|
||||||
toolsLayout->addWidget(m_disableButton, row, col++, Qt::AlignBottom);
|
toolsLayout->addWidget(m_disableButton, row, col++, Qt::AlignBottom);
|
||||||
toolsLayout->addWidget(m_xMirrorButton, row, col++, Qt::AlignBottom);
|
toolsLayout->addWidget(m_xMirrorButton, row, col++, Qt::AlignBottom);
|
||||||
|
@ -95,16 +107,17 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
row++;
|
row++;
|
||||||
col = 0;
|
col = 0;
|
||||||
toolsLayout->addWidget(m_subdivButton, row, col++, Qt::AlignTop);
|
toolsLayout->addWidget(m_subdivButton, row, col++, Qt::AlignTop);
|
||||||
toolsLayout->addWidget(m_cutButton, row, col++, Qt::AlignTop);
|
|
||||||
toolsLayout->addWidget(m_roundButton, row, col++, Qt::AlignTop);
|
toolsLayout->addWidget(m_roundButton, row, col++, Qt::AlignTop);
|
||||||
|
toolsLayout->addWidget(m_cutTemplateButton, row, col++, Qt::AlignTop);
|
||||||
|
toolsLayout->addWidget(m_cutRotationButton, row, col++, Qt::AlignTop);
|
||||||
toolsLayout->addWidget(m_deformButton, row, col++, Qt::AlignTop);
|
toolsLayout->addWidget(m_deformButton, row, col++, Qt::AlignTop);
|
||||||
|
|
||||||
m_visibleButton->setContentsMargins(0, 0, 0, 0);
|
//m_visibleButton->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
QHBoxLayout *previewAndToolsLayout = new QHBoxLayout;
|
QHBoxLayout *previewAndToolsLayout = new QHBoxLayout;
|
||||||
previewAndToolsLayout->setSpacing(0);
|
previewAndToolsLayout->setSpacing(0);
|
||||||
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
|
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
previewAndToolsLayout->addWidget(m_visibleButton);
|
//previewAndToolsLayout->addWidget(m_visibleButton);
|
||||||
previewAndToolsLayout->addWidget(m_previewWidget);
|
previewAndToolsLayout->addWidget(m_previewWidget);
|
||||||
previewAndToolsLayout->addLayout(toolsLayout);
|
previewAndToolsLayout->addLayout(toolsLayout);
|
||||||
previewAndToolsLayout->setStretch(0, 0);
|
previewAndToolsLayout->setStretch(0, 0);
|
||||||
|
@ -144,6 +157,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
connect(this, &PartWidget::setPartDeformWidth, m_document, &Document::setPartDeformWidth);
|
connect(this, &PartWidget::setPartDeformWidth, m_document, &Document::setPartDeformWidth);
|
||||||
connect(this, &PartWidget::setPartRoundState, m_document, &Document::setPartRoundState);
|
connect(this, &PartWidget::setPartRoundState, m_document, &Document::setPartRoundState);
|
||||||
connect(this, &PartWidget::setPartCutRotation, m_document, &Document::setPartCutRotation);
|
connect(this, &PartWidget::setPartCutRotation, m_document, &Document::setPartCutRotation);
|
||||||
|
connect(this, &PartWidget::setPartCutTemplate, m_document, &Document::setPartCutTemplate);
|
||||||
connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState);
|
connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState);
|
||||||
connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId);
|
connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId);
|
||||||
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
|
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
|
||||||
|
@ -230,7 +244,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
showColorSettingPopup(mapFromGlobal(QCursor::pos()));
|
showColorSettingPopup(mapFromGlobal(QCursor::pos()));
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_cutButton, &QPushButton::clicked, [=]() {
|
connect(m_cutRotationButton, &QPushButton::clicked, [=]() {
|
||||||
const SkeletonPart *part = m_document->findPart(m_partId);
|
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||||
if (!part) {
|
if (!part) {
|
||||||
qDebug() << "Part not found:" << m_partId;
|
qDebug() << "Part not found:" << m_partId;
|
||||||
|
@ -239,6 +253,15 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
showCutRotationSettingPopup(mapFromGlobal(QCursor::pos()));
|
showCutRotationSettingPopup(mapFromGlobal(QCursor::pos()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_cutTemplateButton, &QPushButton::clicked, [=]() {
|
||||||
|
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||||
|
if (!part) {
|
||||||
|
qDebug() << "Part not found:" << m_partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showCutTemplateSettingPopup(mapFromGlobal(QCursor::pos()));
|
||||||
|
});
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
setFixedSize(preferredSize());
|
setFixedSize(preferredSize());
|
||||||
|
|
||||||
|
@ -266,6 +289,7 @@ void PartWidget::updateAllButtons()
|
||||||
updateRoundButton();
|
updateRoundButton();
|
||||||
updateColorButton();
|
updateColorButton();
|
||||||
updateCutRotationButton();
|
updateCutRotationButton();
|
||||||
|
updateCutTemplateButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartWidget::updateCheckedState(bool checked)
|
void PartWidget::updateCheckedState(bool checked)
|
||||||
|
@ -418,6 +442,92 @@ void PartWidget::showCutRotationSettingPopup(const QPoint &pos)
|
||||||
popupMenu.exec(mapToGlobal(pos));
|
popupMenu.exec(mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PartWidget::showCutTemplateSettingPopup(const QPoint &pos)
|
||||||
|
{
|
||||||
|
QMenu popupMenu;
|
||||||
|
|
||||||
|
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||||
|
if (!part) {
|
||||||
|
qDebug() << "Find part failed:" << m_partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CutDocument cutDocument;
|
||||||
|
SkeletonGraphicsWidget *graphicsWidget = new SkeletonGraphicsWidget(&cutDocument);
|
||||||
|
graphicsWidget->setNodePositionModifyOnly(true);
|
||||||
|
graphicsWidget->setMainProfileOnly(true);
|
||||||
|
|
||||||
|
GraphicsContainerWidget *containerWidget = new GraphicsContainerWidget;
|
||||||
|
containerWidget->setGraphicsWidget(graphicsWidget);
|
||||||
|
QGridLayout *containerLayout = new QGridLayout;
|
||||||
|
containerLayout->setSpacing(0);
|
||||||
|
containerLayout->setContentsMargins(1, 0, 0, 0);
|
||||||
|
containerLayout->addWidget(graphicsWidget);
|
||||||
|
containerWidget->setLayout(containerLayout);
|
||||||
|
containerWidget->setFixedSize(160, 100);
|
||||||
|
|
||||||
|
connect(containerWidget, &GraphicsContainerWidget::containerSizeChanged,
|
||||||
|
graphicsWidget, &SkeletonGraphicsWidget::canvasResized);
|
||||||
|
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, &cutDocument, &CutDocument::moveNodeBy);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeOrigin, &cutDocument, &CutDocument::setNodeOrigin);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, &cutDocument, &CutDocument::saveHistoryItem);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, &cutDocument, &CutDocument::undo);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, &cutDocument, &CutDocument::redo);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::paste, &cutDocument, &CutDocument::paste);
|
||||||
|
|
||||||
|
connect(&cutDocument, &CutDocument::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent);
|
||||||
|
|
||||||
|
connect(&cutDocument, &CutDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
|
||||||
|
connect(&cutDocument, &CutDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded);
|
||||||
|
connect(&cutDocument, &CutDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
|
||||||
|
|
||||||
|
cutDocument.fromCutTemplate(part->cutTemplate);
|
||||||
|
|
||||||
|
connect(&cutDocument, &CutDocument::cutTemplateChanged, this, [&]() {
|
||||||
|
std::vector<QVector2D> cutTemplate;
|
||||||
|
cutDocument.toCutTemplate(cutTemplate);
|
||||||
|
emit setPartCutTemplate(m_partId, cutTemplate);
|
||||||
|
});
|
||||||
|
|
||||||
|
QWidget *popup = new QWidget;
|
||||||
|
|
||||||
|
initShortCuts(popup, graphicsWidget);
|
||||||
|
|
||||||
|
std::vector<QPushButton *> presetButtons;
|
||||||
|
for (size_t i = 0; i < (size_t)CutTemplate::Count; ++i) {
|
||||||
|
CutTemplate cutTemplate = (CutTemplate)i;
|
||||||
|
QPushButton *button = new QPushButton(CutTemplateToDispName(cutTemplate));
|
||||||
|
connect(button, &QPushButton::clicked, [&]() {
|
||||||
|
auto points = CutTemplateToPoints(cutTemplate);
|
||||||
|
cutDocument.fromCutTemplate(points);
|
||||||
|
emit setPartCutTemplate(m_partId, points);
|
||||||
|
emit groupOperationAdded();
|
||||||
|
});
|
||||||
|
Theme::initToolButton(button);
|
||||||
|
presetButtons.push_back(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
|
QHBoxLayout *presetButtonsLayout = new QHBoxLayout;
|
||||||
|
for (const auto &it: presetButtons)
|
||||||
|
presetButtonsLayout->addWidget(it);
|
||||||
|
presetButtonsLayout->addStretch();
|
||||||
|
layout->addLayout(presetButtonsLayout);
|
||||||
|
layout->addWidget(containerWidget);
|
||||||
|
|
||||||
|
popup->setLayout(layout);
|
||||||
|
|
||||||
|
popup->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
QWidgetAction action(this);
|
||||||
|
action.setDefaultWidget(popup);
|
||||||
|
|
||||||
|
popupMenu.addAction(&action);
|
||||||
|
|
||||||
|
popupMenu.exec(mapToGlobal(pos));
|
||||||
|
}
|
||||||
|
|
||||||
void PartWidget::showDeformSettingPopup(const QPoint &pos)
|
void PartWidget::showDeformSettingPopup(const QPoint &pos)
|
||||||
{
|
{
|
||||||
QMenu popupMenu;
|
QMenu popupMenu;
|
||||||
|
@ -620,9 +730,22 @@ void PartWidget::updateCutRotationButton()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (part->cutRotationAdjusted())
|
if (part->cutRotationAdjusted())
|
||||||
updateButton(m_cutButton, QChar(fa::spinner), true);
|
updateButton(m_cutRotationButton, QChar(fa::spinner), true);
|
||||||
else
|
else
|
||||||
updateButton(m_cutButton, QChar(fa::spinner), false);
|
updateButton(m_cutRotationButton, QChar(fa::spinner), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PartWidget::updateCutTemplateButton()
|
||||||
|
{
|
||||||
|
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||||
|
if (!part) {
|
||||||
|
qDebug() << "Part not found:" << m_partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (part->cutTemplateAdjusted())
|
||||||
|
updateButton(m_cutTemplateButton, QChar(fa::objectungroup), true);
|
||||||
|
else
|
||||||
|
updateButton(m_cutTemplateButton, QChar(fa::objectungroup), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartWidget::reload()
|
void PartWidget::reload()
|
||||||
|
|
|
@ -21,6 +21,7 @@ signals:
|
||||||
void setPartRoundState(QUuid partId, bool rounded);
|
void setPartRoundState(QUuid partId, bool rounded);
|
||||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||||
void setPartCutRotation(QUuid partId, float cutRotation);
|
void setPartCutRotation(QUuid partId, float cutRotation);
|
||||||
|
void setPartCutTemplate(QUuid partId, std::vector<QVector2D> cutTemplate);
|
||||||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||||
void movePartUp(QUuid partId);
|
void movePartUp(QUuid partId);
|
||||||
void movePartDown(QUuid partId);
|
void movePartDown(QUuid partId);
|
||||||
|
@ -44,6 +45,7 @@ public:
|
||||||
void updateRoundButton();
|
void updateRoundButton();
|
||||||
void updateColorButton();
|
void updateColorButton();
|
||||||
void updateCutRotationButton();
|
void updateCutRotationButton();
|
||||||
|
void updateCutTemplateButton();
|
||||||
void updateCheckedState(bool checked);
|
void updateCheckedState(bool checked);
|
||||||
void updateUnnormalState(bool unnormal);
|
void updateUnnormalState(bool unnormal);
|
||||||
static QSize preferredSize();
|
static QSize preferredSize();
|
||||||
|
@ -53,6 +55,7 @@ protected:
|
||||||
public slots:
|
public slots:
|
||||||
void showDeformSettingPopup(const QPoint &pos);
|
void showDeformSettingPopup(const QPoint &pos);
|
||||||
void showCutRotationSettingPopup(const QPoint &pos);
|
void showCutRotationSettingPopup(const QPoint &pos);
|
||||||
|
void showCutTemplateSettingPopup(const QPoint &pos);
|
||||||
void showColorSettingPopup(const QPoint &pos);
|
void showColorSettingPopup(const QPoint &pos);
|
||||||
private: // need initialize
|
private: // need initialize
|
||||||
const Document *m_document;
|
const Document *m_document;
|
||||||
|
@ -69,7 +72,8 @@ private:
|
||||||
QPushButton *m_deformButton;
|
QPushButton *m_deformButton;
|
||||||
QPushButton *m_roundButton;
|
QPushButton *m_roundButton;
|
||||||
QPushButton *m_colorButton;
|
QPushButton *m_colorButton;
|
||||||
QPushButton *m_cutButton;
|
QPushButton *m_cutRotationButton;
|
||||||
|
QPushButton *m_cutTemplateButton;
|
||||||
QWidget *m_backgroundWidget;
|
QWidget *m_backgroundWidget;
|
||||||
private:
|
private:
|
||||||
void initToolButton(QPushButton *button);
|
void initToolButton(QPushButton *button);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "bonemark.h"
|
#include "bonemark.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "meshloader.h"
|
#include "meshloader.h"
|
||||||
|
#include "cuttemplate.h"
|
||||||
|
|
||||||
class SkeletonNode
|
class SkeletonNode
|
||||||
{
|
{
|
||||||
|
@ -84,6 +85,8 @@ public:
|
||||||
std::vector<QUuid> nodeIds;
|
std::vector<QUuid> nodeIds;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
float cutRotation;
|
float cutRotation;
|
||||||
|
std::vector<QVector2D> cutTemplate;
|
||||||
|
bool cutTemplateChanged;
|
||||||
QUuid materialId;
|
QUuid materialId;
|
||||||
SkeletonPart(const QUuid &withId=QUuid()) :
|
SkeletonPart(const QUuid &withId=QUuid()) :
|
||||||
visible(true),
|
visible(true),
|
||||||
|
@ -98,7 +101,9 @@ public:
|
||||||
color(Theme::white),
|
color(Theme::white),
|
||||||
hasColor(false),
|
hasColor(false),
|
||||||
dirty(true),
|
dirty(true),
|
||||||
cutRotation(0.0)
|
cutRotation(0.0),
|
||||||
|
cutTemplate(CutTemplateToPoints(CutTemplate::Quad)),
|
||||||
|
cutTemplateChanged(false)
|
||||||
{
|
{
|
||||||
id = withId.isNull() ? QUuid::createUuid() : withId;
|
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||||
}
|
}
|
||||||
|
@ -126,6 +131,11 @@ public:
|
||||||
toRotation = 1;
|
toRotation = 1;
|
||||||
cutRotation = toRotation;
|
cutRotation = toRotation;
|
||||||
}
|
}
|
||||||
|
void setCutTemplate(std::vector<QVector2D> points)
|
||||||
|
{
|
||||||
|
cutTemplate = points;
|
||||||
|
cutTemplateChanged = true;
|
||||||
|
}
|
||||||
bool deformThicknessAdjusted() const
|
bool deformThicknessAdjusted() const
|
||||||
{
|
{
|
||||||
return fabs(deformThickness - 1.0) >= 0.01;
|
return fabs(deformThickness - 1.0) >= 0.01;
|
||||||
|
@ -142,6 +152,10 @@ public:
|
||||||
{
|
{
|
||||||
return fabs(cutRotation - 0.0) >= 0.01;
|
return fabs(cutRotation - 0.0) >= 0.01;
|
||||||
}
|
}
|
||||||
|
bool cutTemplateAdjusted() const
|
||||||
|
{
|
||||||
|
return cutTemplateChanged;
|
||||||
|
}
|
||||||
bool materialAdjusted() const
|
bool materialAdjusted() const
|
||||||
{
|
{
|
||||||
return !materialId.isNull();
|
return !materialId.isNull();
|
||||||
|
@ -164,6 +178,8 @@ public:
|
||||||
color = other.color;
|
color = other.color;
|
||||||
hasColor = other.hasColor;
|
hasColor = other.hasColor;
|
||||||
cutRotation = other.cutRotation;
|
cutRotation = other.cutRotation;
|
||||||
|
cutTemplate = other.cutTemplate;
|
||||||
|
cutTemplateChanged = other.cutTemplateChanged;
|
||||||
componentId = other.componentId;
|
componentId = other.componentId;
|
||||||
dirty = other.dirty;
|
dirty = other.dirty;
|
||||||
materialId = other.materialId;
|
materialId = other.materialId;
|
||||||
|
|
|
@ -47,6 +47,7 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
|
||||||
m_inTempDragMode(false),
|
m_inTempDragMode(false),
|
||||||
m_modeBeforeEnterTempDragMode(SkeletonDocumentEditMode::Select),
|
m_modeBeforeEnterTempDragMode(SkeletonDocumentEditMode::Select),
|
||||||
m_nodePositionModifyOnly(false),
|
m_nodePositionModifyOnly(false),
|
||||||
|
m_mainProfileOnly(false),
|
||||||
m_turnaroundOpacity(0.25)
|
m_turnaroundOpacity(0.25)
|
||||||
{
|
{
|
||||||
setRenderHint(QPainter::Antialiasing, false);
|
setRenderHint(QPainter::Antialiasing, false);
|
||||||
|
@ -171,7 +172,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction copyAction(tr("Copy"), this);
|
QAction copyAction(tr("Copy"), this);
|
||||||
if (hasNodeSelection()) {
|
if (!m_mainProfileOnly && hasNodeSelection()) {
|
||||||
connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
|
connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
|
||||||
contextMenu.addAction(©Action);
|
contextMenu.addAction(©Action);
|
||||||
}
|
}
|
||||||
|
@ -213,7 +214,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction switchChainSideAction(tr("Switch Chain Side"), this);
|
QAction switchChainSideAction(tr("Switch Chain Side"), this);
|
||||||
if (m_nodePositionModifyOnly && hasNodeSelection()) {
|
if (m_nodePositionModifyOnly && !m_mainProfileOnly && hasNodeSelection()) {
|
||||||
connect(&switchChainSideAction, &QAction::triggered, this, &SkeletonGraphicsWidget::switchSelectedChainSide);
|
connect(&switchChainSideAction, &QAction::triggered, this, &SkeletonGraphicsWidget::switchSelectedChainSide);
|
||||||
contextMenu.addAction(&switchChainSideAction);
|
contextMenu.addAction(&switchChainSideAction);
|
||||||
}
|
}
|
||||||
|
@ -1747,6 +1748,8 @@ void SkeletonGraphicsWidget::nodeAdded(QUuid nodeId)
|
||||||
sideProfileItem->setMarkColor(markColor);
|
sideProfileItem->setMarkColor(markColor);
|
||||||
mainProfileItem->setId(nodeId);
|
mainProfileItem->setId(nodeId);
|
||||||
sideProfileItem->setId(nodeId);
|
sideProfileItem->setId(nodeId);
|
||||||
|
if (m_mainProfileOnly)
|
||||||
|
sideProfileItem->hide();
|
||||||
scene()->addItem(mainProfileItem);
|
scene()->addItem(mainProfileItem);
|
||||||
scene()->addItem(sideProfileItem);
|
scene()->addItem(sideProfileItem);
|
||||||
nodeItemMap[nodeId] = std::make_pair(mainProfileItem, sideProfileItem);
|
nodeItemMap[nodeId] = std::make_pair(mainProfileItem, sideProfileItem);
|
||||||
|
@ -1796,6 +1799,8 @@ void SkeletonGraphicsWidget::edgeAdded(QUuid edgeId)
|
||||||
sideProfileEdgeItem->setId(edgeId);
|
sideProfileEdgeItem->setId(edgeId);
|
||||||
mainProfileEdgeItem->setEndpoints(fromIt->second.first, toIt->second.first);
|
mainProfileEdgeItem->setEndpoints(fromIt->second.first, toIt->second.first);
|
||||||
sideProfileEdgeItem->setEndpoints(fromIt->second.second, toIt->second.second);
|
sideProfileEdgeItem->setEndpoints(fromIt->second.second, toIt->second.second);
|
||||||
|
if (m_mainProfileOnly)
|
||||||
|
sideProfileEdgeItem->hide();
|
||||||
scene()->addItem(mainProfileEdgeItem);
|
scene()->addItem(mainProfileEdgeItem);
|
||||||
scene()->addItem(sideProfileEdgeItem);
|
scene()->addItem(sideProfileEdgeItem);
|
||||||
edgeItemMap[edgeId] = std::make_pair(mainProfileEdgeItem, sideProfileEdgeItem);
|
edgeItemMap[edgeId] = std::make_pair(mainProfileEdgeItem, sideProfileEdgeItem);
|
||||||
|
@ -2554,3 +2559,10 @@ void SkeletonGraphicsWidget::setNodePositionModifyOnly(bool nodePositionModifyOn
|
||||||
{
|
{
|
||||||
m_nodePositionModifyOnly = nodePositionModifyOnly;
|
m_nodePositionModifyOnly = nodePositionModifyOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::setMainProfileOnly(bool mainProfileOnly)
|
||||||
|
{
|
||||||
|
m_mainProfileOnly = mainProfileOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -411,6 +411,7 @@ public:
|
||||||
bool hasTwoDisconnectedNodesSelection();
|
bool hasTwoDisconnectedNodesSelection();
|
||||||
void setModelWidget(ModelWidget *modelWidget);
|
void setModelWidget(ModelWidget *modelWidget);
|
||||||
void setNodePositionModifyOnly(bool nodePositionModifyOnly);
|
void setNodePositionModifyOnly(bool nodePositionModifyOnly);
|
||||||
|
void setMainProfileOnly(bool mainProfileOnly);
|
||||||
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
||||||
protected:
|
protected:
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
@ -564,6 +565,7 @@ private: //need initalize
|
||||||
bool m_inTempDragMode;
|
bool m_inTempDragMode;
|
||||||
SkeletonDocumentEditMode m_modeBeforeEnterTempDragMode;
|
SkeletonDocumentEditMode m_modeBeforeEnterTempDragMode;
|
||||||
bool m_nodePositionModifyOnly;
|
bool m_nodePositionModifyOnly;
|
||||||
|
bool m_mainProfileOnly;
|
||||||
float m_turnaroundOpacity;
|
float m_turnaroundOpacity;
|
||||||
private:
|
private:
|
||||||
QVector3D m_ikMoveTarget;
|
QVector3D m_ikMoveTarget;
|
||||||
|
|
|
@ -164,6 +164,17 @@ void Theme::initAwesomeToolButton(QPushButton *button)
|
||||||
Theme::initAwesomeToolButtonWithoutFont(button);
|
Theme::initAwesomeToolButtonWithoutFont(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Theme::initToolButton(QPushButton *button)
|
||||||
|
{
|
||||||
|
QFont font = button->font();
|
||||||
|
font.setWeight(QFont::Light);
|
||||||
|
font.setBold(false);
|
||||||
|
button->setFont(font);
|
||||||
|
button->setFixedHeight(Theme::toolIconSize * 0.75);
|
||||||
|
button->setStyleSheet("QPushButton {color: #f7d9c8}");
|
||||||
|
button->setFocusPolicy(Qt::NoFocus);
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *Theme::createHorizontalLineWidget()
|
QWidget *Theme::createHorizontalLineWidget()
|
||||||
{
|
{
|
||||||
QWidget *hrLightWidget = new QWidget;
|
QWidget *hrLightWidget = new QWidget;
|
||||||
|
|
|
@ -51,6 +51,7 @@ public:
|
||||||
static void initAwesomeToolButton(QPushButton *button);
|
static void initAwesomeToolButton(QPushButton *button);
|
||||||
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
|
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
|
||||||
static void initAwsomeBaseSizes();
|
static void initAwsomeBaseSizes();
|
||||||
|
static void initToolButton(QPushButton *button);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -574,11 +574,18 @@ void Builder::makeCut(const QVector3D &position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
baseNormal = orientedBaseNormal.normalized();
|
baseNormal = orientedBaseNormal.normalized();
|
||||||
QVector3D u = QVector3D::crossProduct(cutNormal, orientedBaseNormal).normalized();
|
auto finalCutTemplate = cutTemplate;
|
||||||
QVector3D v = QVector3D::crossProduct(u, cutNormal).normalized();
|
auto finalCutNormal = cutNormal;
|
||||||
|
if (QVector3D::dotProduct(cutNormal, traverseDirection) <= 0) {
|
||||||
|
baseNormal = -baseNormal;
|
||||||
|
finalCutNormal = -finalCutNormal;
|
||||||
|
std::reverse(finalCutTemplate.begin(), finalCutTemplate.end());
|
||||||
|
}
|
||||||
|
QVector3D u = QVector3D::crossProduct(finalCutNormal, baseNormal).normalized();
|
||||||
|
QVector3D v = QVector3D::crossProduct(u, finalCutNormal).normalized();
|
||||||
auto uFactor = u * radius;
|
auto uFactor = u * radius;
|
||||||
auto vFactor = v * radius;
|
auto vFactor = v * radius;
|
||||||
for (const auto &t: cutTemplate) {
|
for (const auto &t: finalCutTemplate) {
|
||||||
resultCut.push_back(uFactor * t.x() + vFactor * t.y());
|
resultCut.push_back(uFactor * t.x() + vFactor * t.y());
|
||||||
}
|
}
|
||||||
if (!qFuzzyIsNull(m_cutRotation)) {
|
if (!qFuzzyIsNull(m_cutRotation)) {
|
||||||
|
|
Loading…
Reference in New Issue