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
|
||||
HEADERS += src/combinemode.h
|
||||
|
||||
SOURCES += src/cutdocument.cpp
|
||||
HEADERS += src/cutdocument.h
|
||||
|
||||
SOURCES += src/cuttemplate.cpp
|
||||
HEADERS += src/cuttemplate.h
|
||||
|
||||
SOURCES += src/main.cpp
|
||||
|
||||
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";
|
||||
if (partIt.second.cutRotationAdjusted())
|
||||
part["cutRotation"] = QString::number(partIt.second.cutRotation);
|
||||
if (partIt.second.cutTemplateAdjusted())
|
||||
part["cutTemplate"] = cutTemplatePointsToString(partIt.second.cutTemplate);
|
||||
part["dirty"] = partIt.second.dirty ? "true" : "false";
|
||||
if (partIt.second.hasColor)
|
||||
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");
|
||||
if (cutRotationIt != partKv.second.end())
|
||||
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")))
|
||||
inversePartIds.insert(part.id);
|
||||
const auto &colorIt = partKv.second.find("color");
|
||||
|
@ -2200,12 +2205,29 @@ void Document::setPartCutRotation(QUuid partId, float cutRotation)
|
|||
qDebug() << "Part not found:" << partId;
|
||||
return;
|
||||
}
|
||||
if (qFuzzyCompare(cutRotation, part->second.cutRotation))
|
||||
return;
|
||||
part->second.setCutRotation(cutRotation);
|
||||
part->second.dirty = true;
|
||||
emit partCutRotationChanged(partId);
|
||||
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)
|
||||
{
|
||||
auto part = partMap.find(partId);
|
||||
|
|
|
@ -401,6 +401,7 @@ signals:
|
|||
void partRoundStateChanged(QUuid partId);
|
||||
void partColorStateChanged(QUuid partId);
|
||||
void partCutRotationChanged(QUuid partId);
|
||||
void partCutTemplateChanged(QUuid partId);
|
||||
void partMaterialIdChanged(QUuid partId);
|
||||
void componentCombineModeChanged(QUuid componentId);
|
||||
void cleanup();
|
||||
|
@ -554,6 +555,7 @@ public slots:
|
|||
void setPartRoundState(QUuid partId, bool rounded);
|
||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||
void setPartCutRotation(QUuid partId, float cutRotation);
|
||||
void setPartCutTemplate(QUuid partId, std::vector<QVector2D> cutTemplate);
|
||||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
||||
void moveComponentUp(QUuid componentId);
|
||||
|
|
|
@ -835,8 +835,9 @@ DocumentWindow::DocumentWindow() :
|
|||
connect(m_document, &Document::partDeformThicknessChanged, partTreeWidget, &PartTreeWidget::partDeformChanged);
|
||||
connect(m_document, &Document::partDeformWidthChanged, partTreeWidget, &PartTreeWidget::partDeformChanged);
|
||||
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::partCutRotationChanged, partTreeWidget, &PartTreeWidget::partCutRotationChanged);
|
||||
connect(m_document, &Document::partCutTemplateChanged, partTreeWidget, &PartTreeWidget::partCutTemplateChanged);
|
||||
connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged);
|
||||
connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved);
|
||||
connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
|
||||
|
|
|
@ -10,13 +10,7 @@
|
|||
#include "meshgenerator.h"
|
||||
#include "util.h"
|
||||
#include "trianglesourcenoderesolve.h"
|
||||
|
||||
const std::vector<QVector2D> g_defaultCutTemplate = {
|
||||
{-1.0, -1.0},
|
||||
{ 1.0, -1.0},
|
||||
{ 1.0, 1.0},
|
||||
{-1.0, 1.0},
|
||||
};
|
||||
#include "cuttemplate.h"
|
||||
|
||||
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
||||
m_snapshot(snapshot)
|
||||
|
@ -155,7 +149,8 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
float deformWidth = 1.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");
|
||||
if (!cutRotationString.isEmpty()) {
|
||||
cutRotation = cutRotationString.toFloat();
|
||||
|
|
|
@ -49,7 +49,7 @@ PartTreeWidget::PartTreeWidget(const Document *document, QWidget *parent) :
|
|||
|
||||
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);
|
||||
fillColor.setAlphaF(0.85);
|
||||
gradient.setCoordinateMode(QGradient::StretchToDeviceMode);
|
||||
|
@ -910,7 +910,18 @@ void PartTreeWidget::partRoundStateChanged(QUuid partId)
|
|||
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);
|
||||
if (item == m_partItemMap.end()) {
|
||||
|
@ -921,7 +932,7 @@ void PartTreeWidget::partWrapStateChanged(QUuid partId)
|
|||
widget->updateCutRotationButton();
|
||||
}
|
||||
|
||||
void PartTreeWidget::partColorStateChanged(QUuid partId)
|
||||
void PartTreeWidget::partCutTemplateChanged(QUuid partId)
|
||||
{
|
||||
auto item = m_partItemMap.find(partId);
|
||||
if (item == m_partItemMap.end()) {
|
||||
|
@ -929,7 +940,7 @@ void PartTreeWidget::partColorStateChanged(QUuid partId)
|
|||
return;
|
||||
}
|
||||
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
|
||||
widget->updateColorButton();
|
||||
widget->updateCutTemplateButton();
|
||||
}
|
||||
|
||||
void PartTreeWidget::partMaterialIdChanged(QUuid partId)
|
||||
|
|
|
@ -59,8 +59,9 @@ public slots:
|
|||
void partXmirrorStateChanged(QUuid partId);
|
||||
void partDeformChanged(QUuid partId);
|
||||
void partRoundStateChanged(QUuid partId);
|
||||
void partWrapStateChanged(QUuid partId);
|
||||
void partColorStateChanged(QUuid partId);
|
||||
void partCutRotationChanged(QUuid partId);
|
||||
void partCutTemplateChanged(QUuid partId);
|
||||
void partMaterialIdChanged(QUuid partId);
|
||||
void partChecked(QUuid partId);
|
||||
void partUnchecked(QUuid partId);
|
||||
|
|
|
@ -6,11 +6,17 @@
|
|||
#include <QColorDialog>
|
||||
#include <QSizePolicy>
|
||||
#include <QFileDialog>
|
||||
#include <QSizePolicy>
|
||||
#include "partwidget.h"
|
||||
#include "theme.h"
|
||||
#include "floatnumberwidget.h"
|
||||
#include "materiallistwidget.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) :
|
||||
m_document(document),
|
||||
|
@ -31,7 +37,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
initButton(m_lockButton);
|
||||
|
||||
m_subdivButton = new QPushButton();
|
||||
m_subdivButton->setToolTip(tr("Square/Round"));
|
||||
m_subdivButton->setToolTip(tr("Subdivide"));
|
||||
m_subdivButton->setSizePolicy(retainSizePolicy);
|
||||
initButton(m_subdivButton);
|
||||
|
||||
|
@ -60,10 +66,15 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
m_colorButton->setSizePolicy(retainSizePolicy);
|
||||
initButton(m_colorButton);
|
||||
|
||||
m_cutButton = new QPushButton;
|
||||
m_cutButton->setToolTip(tr("Rotation"));
|
||||
m_cutButton->setSizePolicy(retainSizePolicy);
|
||||
initButton(m_cutButton);
|
||||
m_cutRotationButton = new QPushButton;
|
||||
m_cutRotationButton->setToolTip(tr("Cut rotation"));
|
||||
m_cutRotationButton->setSizePolicy(retainSizePolicy);
|
||||
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->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
@ -88,6 +99,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
toolsLayout->setContentsMargins(0, 0, 5, 0);
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
toolsLayout->addWidget(m_visibleButton, row, col++, Qt::AlignBottom);
|
||||
toolsLayout->addWidget(m_lockButton, row, col++, Qt::AlignBottom);
|
||||
toolsLayout->addWidget(m_disableButton, row, col++, Qt::AlignBottom);
|
||||
toolsLayout->addWidget(m_xMirrorButton, row, col++, Qt::AlignBottom);
|
||||
|
@ -95,16 +107,17 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
row++;
|
||||
col = 0;
|
||||
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_cutTemplateButton, row, col++, Qt::AlignTop);
|
||||
toolsLayout->addWidget(m_cutRotationButton, 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;
|
||||
previewAndToolsLayout->setSpacing(0);
|
||||
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
previewAndToolsLayout->addWidget(m_visibleButton);
|
||||
//previewAndToolsLayout->addWidget(m_visibleButton);
|
||||
previewAndToolsLayout->addWidget(m_previewWidget);
|
||||
previewAndToolsLayout->addLayout(toolsLayout);
|
||||
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::setPartRoundState, m_document, &Document::setPartRoundState);
|
||||
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::setPartMaterialId, m_document, &Document::setPartMaterialId);
|
||||
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
|
||||
|
@ -230,7 +244,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
showColorSettingPopup(mapFromGlobal(QCursor::pos()));
|
||||
});
|
||||
|
||||
connect(m_cutButton, &QPushButton::clicked, [=]() {
|
||||
connect(m_cutRotationButton, &QPushButton::clicked, [=]() {
|
||||
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||
if (!part) {
|
||||
qDebug() << "Part not found:" << m_partId;
|
||||
|
@ -239,6 +253,15 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
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);
|
||||
setFixedSize(preferredSize());
|
||||
|
||||
|
@ -266,6 +289,7 @@ void PartWidget::updateAllButtons()
|
|||
updateRoundButton();
|
||||
updateColorButton();
|
||||
updateCutRotationButton();
|
||||
updateCutTemplateButton();
|
||||
}
|
||||
|
||||
void PartWidget::updateCheckedState(bool checked)
|
||||
|
@ -418,6 +442,92 @@ void PartWidget::showCutRotationSettingPopup(const QPoint &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)
|
||||
{
|
||||
QMenu popupMenu;
|
||||
|
@ -620,9 +730,22 @@ void PartWidget::updateCutRotationButton()
|
|||
return;
|
||||
}
|
||||
if (part->cutRotationAdjusted())
|
||||
updateButton(m_cutButton, QChar(fa::spinner), true);
|
||||
updateButton(m_cutRotationButton, QChar(fa::spinner), true);
|
||||
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()
|
||||
|
|
|
@ -21,6 +21,7 @@ signals:
|
|||
void setPartRoundState(QUuid partId, bool rounded);
|
||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||
void setPartCutRotation(QUuid partId, float cutRotation);
|
||||
void setPartCutTemplate(QUuid partId, std::vector<QVector2D> cutTemplate);
|
||||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||
void movePartUp(QUuid partId);
|
||||
void movePartDown(QUuid partId);
|
||||
|
@ -44,6 +45,7 @@ public:
|
|||
void updateRoundButton();
|
||||
void updateColorButton();
|
||||
void updateCutRotationButton();
|
||||
void updateCutTemplateButton();
|
||||
void updateCheckedState(bool checked);
|
||||
void updateUnnormalState(bool unnormal);
|
||||
static QSize preferredSize();
|
||||
|
@ -53,6 +55,7 @@ protected:
|
|||
public slots:
|
||||
void showDeformSettingPopup(const QPoint &pos);
|
||||
void showCutRotationSettingPopup(const QPoint &pos);
|
||||
void showCutTemplateSettingPopup(const QPoint &pos);
|
||||
void showColorSettingPopup(const QPoint &pos);
|
||||
private: // need initialize
|
||||
const Document *m_document;
|
||||
|
@ -69,7 +72,8 @@ private:
|
|||
QPushButton *m_deformButton;
|
||||
QPushButton *m_roundButton;
|
||||
QPushButton *m_colorButton;
|
||||
QPushButton *m_cutButton;
|
||||
QPushButton *m_cutRotationButton;
|
||||
QPushButton *m_cutTemplateButton;
|
||||
QWidget *m_backgroundWidget;
|
||||
private:
|
||||
void initToolButton(QPushButton *button);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "bonemark.h"
|
||||
#include "theme.h"
|
||||
#include "meshloader.h"
|
||||
#include "cuttemplate.h"
|
||||
|
||||
class SkeletonNode
|
||||
{
|
||||
|
@ -84,6 +85,8 @@ public:
|
|||
std::vector<QUuid> nodeIds;
|
||||
bool dirty;
|
||||
float cutRotation;
|
||||
std::vector<QVector2D> cutTemplate;
|
||||
bool cutTemplateChanged;
|
||||
QUuid materialId;
|
||||
SkeletonPart(const QUuid &withId=QUuid()) :
|
||||
visible(true),
|
||||
|
@ -98,7 +101,9 @@ public:
|
|||
color(Theme::white),
|
||||
hasColor(false),
|
||||
dirty(true),
|
||||
cutRotation(0.0)
|
||||
cutRotation(0.0),
|
||||
cutTemplate(CutTemplateToPoints(CutTemplate::Quad)),
|
||||
cutTemplateChanged(false)
|
||||
{
|
||||
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||
}
|
||||
|
@ -126,6 +131,11 @@ public:
|
|||
toRotation = 1;
|
||||
cutRotation = toRotation;
|
||||
}
|
||||
void setCutTemplate(std::vector<QVector2D> points)
|
||||
{
|
||||
cutTemplate = points;
|
||||
cutTemplateChanged = true;
|
||||
}
|
||||
bool deformThicknessAdjusted() const
|
||||
{
|
||||
return fabs(deformThickness - 1.0) >= 0.01;
|
||||
|
@ -142,6 +152,10 @@ public:
|
|||
{
|
||||
return fabs(cutRotation - 0.0) >= 0.01;
|
||||
}
|
||||
bool cutTemplateAdjusted() const
|
||||
{
|
||||
return cutTemplateChanged;
|
||||
}
|
||||
bool materialAdjusted() const
|
||||
{
|
||||
return !materialId.isNull();
|
||||
|
@ -164,6 +178,8 @@ public:
|
|||
color = other.color;
|
||||
hasColor = other.hasColor;
|
||||
cutRotation = other.cutRotation;
|
||||
cutTemplate = other.cutTemplate;
|
||||
cutTemplateChanged = other.cutTemplateChanged;
|
||||
componentId = other.componentId;
|
||||
dirty = other.dirty;
|
||||
materialId = other.materialId;
|
||||
|
|
|
@ -47,6 +47,7 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
|
|||
m_inTempDragMode(false),
|
||||
m_modeBeforeEnterTempDragMode(SkeletonDocumentEditMode::Select),
|
||||
m_nodePositionModifyOnly(false),
|
||||
m_mainProfileOnly(false),
|
||||
m_turnaroundOpacity(0.25)
|
||||
{
|
||||
setRenderHint(QPainter::Antialiasing, false);
|
||||
|
@ -171,7 +172,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
|||
}
|
||||
|
||||
QAction copyAction(tr("Copy"), this);
|
||||
if (hasNodeSelection()) {
|
||||
if (!m_mainProfileOnly && hasNodeSelection()) {
|
||||
connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
|
||||
contextMenu.addAction(©Action);
|
||||
}
|
||||
|
@ -213,7 +214,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
|||
}
|
||||
|
||||
QAction switchChainSideAction(tr("Switch Chain Side"), this);
|
||||
if (m_nodePositionModifyOnly && hasNodeSelection()) {
|
||||
if (m_nodePositionModifyOnly && !m_mainProfileOnly && hasNodeSelection()) {
|
||||
connect(&switchChainSideAction, &QAction::triggered, this, &SkeletonGraphicsWidget::switchSelectedChainSide);
|
||||
contextMenu.addAction(&switchChainSideAction);
|
||||
}
|
||||
|
@ -1747,6 +1748,8 @@ void SkeletonGraphicsWidget::nodeAdded(QUuid nodeId)
|
|||
sideProfileItem->setMarkColor(markColor);
|
||||
mainProfileItem->setId(nodeId);
|
||||
sideProfileItem->setId(nodeId);
|
||||
if (m_mainProfileOnly)
|
||||
sideProfileItem->hide();
|
||||
scene()->addItem(mainProfileItem);
|
||||
scene()->addItem(sideProfileItem);
|
||||
nodeItemMap[nodeId] = std::make_pair(mainProfileItem, sideProfileItem);
|
||||
|
@ -1796,6 +1799,8 @@ void SkeletonGraphicsWidget::edgeAdded(QUuid edgeId)
|
|||
sideProfileEdgeItem->setId(edgeId);
|
||||
mainProfileEdgeItem->setEndpoints(fromIt->second.first, toIt->second.first);
|
||||
sideProfileEdgeItem->setEndpoints(fromIt->second.second, toIt->second.second);
|
||||
if (m_mainProfileOnly)
|
||||
sideProfileEdgeItem->hide();
|
||||
scene()->addItem(mainProfileEdgeItem);
|
||||
scene()->addItem(sideProfileEdgeItem);
|
||||
edgeItemMap[edgeId] = std::make_pair(mainProfileEdgeItem, sideProfileEdgeItem);
|
||||
|
@ -2554,3 +2559,10 @@ void SkeletonGraphicsWidget::setNodePositionModifyOnly(bool nodePositionModifyOn
|
|||
{
|
||||
m_nodePositionModifyOnly = nodePositionModifyOnly;
|
||||
}
|
||||
|
||||
void SkeletonGraphicsWidget::setMainProfileOnly(bool mainProfileOnly)
|
||||
{
|
||||
m_mainProfileOnly = mainProfileOnly;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -411,6 +411,7 @@ public:
|
|||
bool hasTwoDisconnectedNodesSelection();
|
||||
void setModelWidget(ModelWidget *modelWidget);
|
||||
void setNodePositionModifyOnly(bool nodePositionModifyOnly);
|
||||
void setMainProfileOnly(bool mainProfileOnly);
|
||||
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
@ -564,6 +565,7 @@ private: //need initalize
|
|||
bool m_inTempDragMode;
|
||||
SkeletonDocumentEditMode m_modeBeforeEnterTempDragMode;
|
||||
bool m_nodePositionModifyOnly;
|
||||
bool m_mainProfileOnly;
|
||||
float m_turnaroundOpacity;
|
||||
private:
|
||||
QVector3D m_ikMoveTarget;
|
||||
|
|
|
@ -164,6 +164,17 @@ void Theme::initAwesomeToolButton(QPushButton *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 *hrLightWidget = new QWidget;
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
static void initAwesomeToolButton(QPushButton *button);
|
||||
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
|
||||
static void initAwsomeBaseSizes();
|
||||
static void initToolButton(QPushButton *button);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -574,11 +574,18 @@ void Builder::makeCut(const QVector3D &position,
|
|||
}
|
||||
}
|
||||
baseNormal = orientedBaseNormal.normalized();
|
||||
QVector3D u = QVector3D::crossProduct(cutNormal, orientedBaseNormal).normalized();
|
||||
QVector3D v = QVector3D::crossProduct(u, cutNormal).normalized();
|
||||
auto finalCutTemplate = cutTemplate;
|
||||
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 vFactor = v * radius;
|
||||
for (const auto &t: cutTemplate) {
|
||||
for (const auto &t: finalCutTemplate) {
|
||||
resultCut.push_back(uFactor * t.x() + vFactor * t.y());
|
||||
}
|
||||
if (!qFuzzyIsNull(m_cutRotation)) {
|
||||
|
|
Loading…
Reference in New Issue