Add part mirror

master
Jeremy Hu 2018-04-15 20:11:51 +08:00
parent 7437ddb597
commit 44a8ca6351
17 changed files with 565 additions and 120 deletions

View File

@ -75,55 +75,9 @@ QImage *MeshGenerator::takePartPreview(const QString &partId)
return resultImage;
}
void MeshGenerator::resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile)
void MeshGenerator::resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile, const QString &partId)
{
float left = 0;
bool leftFirstTime = true;
float right = 0;
bool rightFirstTime = true;
float top = 0;
bool topFirstTime = true;
float bottom = 0;
bool bottomFirstTime = true;
float zLeft = 0;
bool zLeftFirstTime = true;
float zRight = 0;
bool zRightFirstTime = true;
for (const auto &nodeIt: m_snapshot->nodes) {
float radius = valueOfKeyInMapOrEmpty(nodeIt.second, "radius").toFloat();
float x = valueOfKeyInMapOrEmpty(nodeIt.second, "x").toFloat();
float y = valueOfKeyInMapOrEmpty(nodeIt.second, "y").toFloat();
float z = valueOfKeyInMapOrEmpty(nodeIt.second, "z").toFloat();
if (leftFirstTime || x - radius < left) {
left = x - radius;
leftFirstTime = false;
}
if (topFirstTime || y - radius < top) {
top = y - radius;
topFirstTime = false;
}
if (rightFirstTime || x + radius > right) {
right = x + radius;
rightFirstTime = false;
}
if (bottomFirstTime || y + radius > bottom) {
bottom = y + radius;
bottomFirstTime = false;
}
if (zLeftFirstTime || z - radius < zLeft) {
zLeft = z - radius;
zLeftFirstTime = false;
}
if (zRightFirstTime || z + radius > zRight) {
zRight = z + radius;
zRightFirstTime = false;
}
}
*mainProfile = QRectF(QPointF(left, top), QPointF(right, bottom));
*sideProfile = QRectF(QPointF(zLeft, top), QPointF(zRight, bottom));
qDebug() << "resolveBoundingBox left:" << left << "top:" << top << "right:" << right << "bottom:" << bottom << " zLeft:" << zLeft << "zRight:" << zRight;
qDebug() << "mainHeight:" << mainProfile->height() << "mainWidth:" << mainProfile->width();
qDebug() << "sideHeight:" << sideProfile->height() << "sideWidth:" << sideProfile->width();
m_snapshot->resolveBoundingBox(mainProfile, sideProfile, partId);
}
void MeshGenerator::process()
@ -145,6 +99,19 @@ void MeshGenerator::process()
float mainProfileMiddleX = mainProfile.x() + mainProfile.width() / 2;
float sideProfileMiddleX = sideProfile.x() + sideProfile.width() / 2;
float mainProfileMiddleY = mainProfile.y() + mainProfile.height() / 2;
float originX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
float originY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
float originZ = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
bool originSettled = false;
if (originX > 0 && originY > 0 && originZ > 0) {
qDebug() << "Use settled origin: " << originX << originY << originZ << " calculated:" << mainProfileMiddleX << mainProfileMiddleY << sideProfileMiddleX;
mainProfileMiddleX = originX;
mainProfileMiddleY = originY;
sideProfileMiddleX = originZ;
originSettled = true;
} else {
qDebug() << "No settled origin, calculated:" << mainProfileMiddleX << mainProfileMiddleY << sideProfileMiddleX;
}
for (const auto &partIdIt: m_snapshot->partIdList) {
const auto &part = m_snapshot->parts.find(partIdIt);
@ -241,7 +208,19 @@ void MeshGenerator::process()
int meshId = meshlite_bmesh_generate_mesh(meshliteContext, bmeshId);
if (meshlite_bmesh_error_count(meshliteContext, bmeshId) != 0)
broken = true;
bool subdived = isTrueValueString(part->second["subdived"]);
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part->second, "xMirrored"));
bool zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part->second, "zMirrored"));
int xMirroredMeshId = 0;
int zMirroredMeshId = 0;
if (xMirrored || zMirrored) {
if (xMirrored) {
xMirroredMeshId = meshlite_mirror_in_x(meshliteContext, meshId, 0);
}
if (zMirrored) {
zMirroredMeshId = meshlite_mirror_in_z(meshliteContext, meshId, 0);
}
}
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part->second, "subdived"));
if (m_requirePartPreviewMap.find(partIdIt) != m_requirePartPreviewMap.end()) {
ModelOfflineRender *render = m_partPreviewRenderMap[partIdIt];
int trimedMeshId = meshlite_trim(meshliteContext, meshId, 1);
@ -255,10 +234,19 @@ void MeshGenerator::process()
QImage *image = new QImage(render->toImage(QSize(Theme::previewImageSize, Theme::previewImageSize)));
m_partPreviewMap[partIdIt] = image;
}
if (subdived)
if (subdived) {
subdivMeshIds.push_back(meshId);
else
if (xMirroredMeshId)
subdivMeshIds.push_back(xMirroredMeshId);
if (zMirroredMeshId)
subdivMeshIds.push_back(zMirroredMeshId);
} else {
meshIds.push_back(meshId);
if (xMirroredMeshId)
meshIds.push_back(xMirroredMeshId);
if (zMirroredMeshId)
meshIds.push_back(zMirroredMeshId);
}
}
if (!subdivMeshIds.empty()) {
@ -307,8 +295,11 @@ void MeshGenerator::process()
QImage *image = new QImage(m_previewRender->toImage(QSize(Theme::previewImageSize, Theme::previewImageSize)));
m_preview = image;
}
int trimedMeshId = meshlite_trim(meshliteContext, mergedMeshId, 1);
m_mesh = new Mesh(meshliteContext, trimedMeshId, broken);
int finalMeshId = mergedMeshId;
if (!originSettled) {
finalMeshId = meshlite_trim(meshliteContext, mergedMeshId, 1);
}
m_mesh = new Mesh(meshliteContext, finalMeshId, broken);
}
if (m_previewRender) {

View File

@ -36,7 +36,7 @@ private:
std::map<QString, ModelOfflineRender *> m_partPreviewRenderMap;
QThread *m_thread;
private:
void resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile);
void resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile, const QString &partId=QString());
static bool enableDebug;
};

View File

@ -13,11 +13,16 @@
unsigned long SkeletonDocument::m_maxSnapshot = 1000;
SkeletonDocument::SkeletonDocument() :
// public
originX(0),
originY(0),
originZ(0),
editMode(SkeletonDocumentEditMode::Select),
// private
m_resultMeshIsObsolete(false),
m_resultMesh(nullptr),
m_meshGenerator(nullptr),
m_batchChangeRefCount(0),
editMode(SkeletonDocumentEditMode::Select)
m_resultMesh(nullptr),
m_batchChangeRefCount(0)
{
}
@ -285,6 +290,11 @@ const SkeletonEdge *SkeletonDocument::findEdgeByNodes(QUuid firstNodeId, QUuid s
return nullptr;
}
bool SkeletonDocument::originSettled() const
{
return originX > 0 && originY > 0 && originZ > 0;
}
void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
{
if (findEdgeByNodes(fromNodeId, toNodeId)) {
@ -431,6 +441,15 @@ void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
emit skeletonChanged();
}
void SkeletonDocument::moveOriginBy(float x, float y, float z)
{
originX += x;
originY += y;
originZ += z;
emit originChanged();
emit skeletonChanged();
}
void SkeletonDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
{
auto it = nodeMap.find(nodeId);
@ -552,6 +571,8 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
part["locked"] = partIt.second.locked ? "true" : "false";
part["subdived"] = partIt.second.subdived ? "true" : "false";
part["disabled"] = partIt.second.disabled ? "true" : "false";
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
part["zMirrored"] = partIt.second.zMirrored ? "true" : "false";
if (!partIt.second.name.isEmpty())
part["name"] = partIt.second.name;
snapshot->parts[part["id"]] = part;
@ -591,10 +612,26 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
continue;
snapshot->partIdList.push_back(partIdIt.toString());
}
std::map<QString, QString> canvas;
canvas["originX"] = QString::number(originX);
canvas["originY"] = QString::number(originY);
canvas["originZ"] = QString::number(originZ);
snapshot->canvas = canvas;
}
void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
{
const auto &originXit = snapshot.canvas.find("originX");
const auto &originYit = snapshot.canvas.find("originY");
const auto &originZit = snapshot.canvas.find("originZ");
if (originXit != snapshot.canvas.end() &&
originYit != snapshot.canvas.end() &&
originZit != snapshot.canvas.end()) {
originX = originXit->second.toFloat();
originY = originYit->second.toFloat();
originZ = originZit->second.toFloat();
}
std::map<QUuid, QUuid> oldNewIdMap;
for (const auto &partKv : snapshot.parts) {
SkeletonPart part;
@ -604,6 +641,8 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
part.locked = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "locked"));
part.subdived = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "subdived"));
part.disabled = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "disabled"));
part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored"));
part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored"));
partMap[part.id] = part;
}
for (const auto &nodeKv : snapshot.nodes) {
@ -664,11 +703,15 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
}
emit partListChanged();
emit originChanged();
emit skeletonChanged();
}
void SkeletonDocument::reset()
{
originX = 0;
originY = 0;
originZ = 0;
nodeMap.clear();
edgeMap.clear();
partMap.clear();
@ -824,6 +867,51 @@ void SkeletonDocument::setPartDisableState(QUuid partId, bool disabled)
emit skeletonChanged();
}
void SkeletonDocument::settleOrigin()
{
if (originSettled())
return;
SkeletonSnapshot snapshot;
toSnapshot(&snapshot);
QRectF mainProfile;
QRectF sideProfile;
snapshot.resolveBoundingBox(&mainProfile, &sideProfile);
originX = mainProfile.x() + mainProfile.width() / 2;
originY = mainProfile.y() + mainProfile.height() / 2;
originZ = sideProfile.x() + sideProfile.width() / 2;
emit originChanged();
}
void SkeletonDocument::setPartXmirrorState(QUuid partId, bool mirrored)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.xMirrored == mirrored)
return;
part->second.xMirrored = mirrored;
settleOrigin();
emit partXmirrorStateChanged(partId);
emit skeletonChanged();
}
void SkeletonDocument::setPartZmirrorState(QUuid partId, bool mirrored)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.zMirrored == mirrored)
return;
part->second.zMirrored = mirrored;
settleOrigin();
emit partZmirrorStateChanged(partId);
emit skeletonChanged();
}
void SkeletonDocument::saveSnapshot()
{
if (m_undoItems.size() + 1 > m_maxSnapshot)

View File

@ -68,13 +68,17 @@ public:
bool locked;
bool subdived;
bool disabled;
bool xMirrored;
bool zMirrored;
QImage preview;
std::vector<QUuid> nodeIds;
SkeletonPart(const QUuid &withId=QUuid()) :
visible(true),
locked(false),
subdived(false),
disabled(false)
disabled(false),
xMirrored(false),
zMirrored(false)
{
id = withId.isNull() ? QUuid::createUuid() : withId;
}
@ -84,6 +88,8 @@ public:
locked = other.locked;
subdived = other.subdived;
disabled = other.disabled;
xMirrored = other.xMirrored;
zMirrored = other.zMirrored;
}
};
@ -133,7 +139,15 @@ signals:
void partVisibleStateChanged(QUuid partId);
void partSubdivStateChanged(QUuid partId);
void partDisableStateChanged(QUuid partId);
void partXmirrorStateChanged(QUuid partId);
void partZmirrorStateChanged(QUuid partId);
void cleanup();
void originChanged();
public: // need initialize
float originX;
float originY;
float originZ;
SkeletonDocumentEditMode editMode;
public:
SkeletonDocument();
~SkeletonDocument();
@ -142,7 +156,7 @@ public:
std::map<QUuid, SkeletonEdge> edgeMap;
std::vector<QUuid> partIds;
QImage turnaround;
SkeletonDocumentEditMode editMode;
QImage preview;
void toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUuid> &limitNodeIds=std::set<QUuid>()) const;
void fromSnapshot(const SkeletonSnapshot &snapshot);
void addFromSnapshot(const SkeletonSnapshot &snapshot);
@ -151,13 +165,13 @@ public:
const SkeletonPart *findPart(QUuid partId) const;
const SkeletonEdge *findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const;
Mesh *takeResultMesh();
QImage preview;
void updateTurnaround(const QImage &image);
bool hasPastableContentInClipboard() const;
bool undoable() const;
bool redoable() const;
bool isNodeEditable(QUuid nodeId) const;
bool isEdgeEditable(QUuid edgeId) const;
bool originSettled() const;
public slots:
void removeNode(QUuid nodeId);
void removeEdge(QUuid edgeId);
@ -167,6 +181,7 @@ public slots:
void moveNodeBy(QUuid nodeId, float x, float y, float z);
void setNodeOrigin(QUuid nodeId, float x, float y, float z);
void setNodeRadius(QUuid nodeId, float radius);
void moveOriginBy(float x, float y, float z);
void addEdge(QUuid fromNodeId, QUuid toNodeId);
void setEditMode(SkeletonDocumentEditMode mode);
void uiReady();
@ -176,6 +191,8 @@ public slots:
void setPartVisibleState(QUuid partId, bool visible);
void setPartSubdivState(QUuid partId, bool subdived);
void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartZmirrorState(QUuid partId, bool mirrored);
void saveSnapshot();
void undo();
void redo();
@ -190,14 +207,16 @@ private:
void splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId);
bool isPartReadonly(QUuid partId) const;
QUuid createNode(float x, float y, float z, float radius, QUuid fromNodeId);
private:
void settleOrigin();
private: // need initialize
bool m_resultMeshIsObsolete;
MeshGenerator *m_meshGenerator;
Mesh *m_resultMesh;
int m_batchChangeRefCount;
private:
static unsigned long m_maxSnapshot;
std::deque<SkeletonHistoryItem> m_undoItems;
std::deque<SkeletonHistoryItem> m_redoItems;
int m_batchChangeRefCount;
};
#endif

View File

@ -360,6 +360,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &SkeletonDocument::batchChangeBegin);
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &SkeletonDocument::batchChangeEnd);
connect(graphicsWidget, &SkeletonGraphicsWidget::breakEdge, m_document, &SkeletonDocument::breakEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::moveOriginBy, m_document, &SkeletonDocument::moveOriginBy);
connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround);
connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &SkeletonDocumentWindow::save);
@ -374,6 +375,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
connect(m_document, &SkeletonDocument::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged);
connect(m_document, &SkeletonDocument::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent);
connect(m_document, &SkeletonDocument::originChanged, graphicsWidget, &SkeletonGraphicsWidget::originChanged);
connect(m_document, &SkeletonDocument::partListChanged, partListWidget, &SkeletonPartListWidget::partListChanged);
connect(m_document, &SkeletonDocument::partPreviewChanged, partListWidget, &SkeletonPartListWidget::partPreviewChanged);
@ -381,6 +383,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::partVisibleStateChanged, partListWidget, &SkeletonPartListWidget::partVisibleStateChanged);
connect(m_document, &SkeletonDocument::partSubdivStateChanged, partListWidget, &SkeletonPartListWidget::partSubdivStateChanged);
connect(m_document, &SkeletonDocument::partDisableStateChanged, partListWidget, &SkeletonPartListWidget::partDisableStateChanged);
connect(m_document, &SkeletonDocument::partXmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partXmirrorStateChanged);
connect(m_document, &SkeletonDocument::partZmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partZmirrorStateChanged);
connect(m_document, &SkeletonDocument::cleanup, partListWidget, &SkeletonPartListWidget::partListChanged);
connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh);

View File

@ -16,22 +16,28 @@
SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document) :
m_document(document),
m_backgroundItem(nullptr),
m_turnaroundChanged(false),
m_turnaroundLoader(nullptr),
m_dragStarted(false),
m_moveStarted(false),
m_cursorNodeItem(nullptr),
m_cursorEdgeItem(nullptr),
m_addFromNodeItem(nullptr),
m_moveStarted(false),
m_hoveredNodeItem(nullptr),
m_hoveredEdgeItem(nullptr),
m_lastAddedX(0),
m_lastAddedY(0),
m_lastAddedZ(0),
m_lastAddedX(false),
m_lastAddedY(false),
m_lastAddedZ(false),
m_selectionItem(nullptr),
m_rangeSelectionStarted(false),
m_mouseEventFromSelf(false),
m_lastRot(0)
m_moveHappened(false),
m_lastRot(0),
m_mainOriginItem(nullptr),
m_sideOriginItem(nullptr),
m_hoveredOriginItem(nullptr),
m_checkedOriginItem(nullptr)
{
setRenderHint(QPainter::Antialiasing, false);
setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern));
@ -60,6 +66,14 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
m_selectionItem->hide();
scene()->addItem(m_selectionItem);
m_mainOriginItem = new SkeletonGraphicsOriginItem(SkeletonProfile::Main);
m_mainOriginItem->hide();
scene()->addItem(m_mainOriginItem);
m_sideOriginItem = new SkeletonGraphicsOriginItem(SkeletonProfile::Side);
m_sideOriginItem->hide();
scene()->addItem(m_sideOriginItem);
scene()->setSceneRect(rect());
setMouseTracking(true);
@ -432,19 +446,30 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
SkeletonDocumentEditMode::Add == m_document->editMode) {
SkeletonGraphicsNodeItem *newHoverNodeItem = nullptr;
SkeletonGraphicsEdgeItem *newHoverEdgeItem = nullptr;
SkeletonGraphicsOriginItem *newHoverOriginItem = nullptr;
QList<QGraphicsItem *> items = scene()->items(mouseEventScenePos(event));
for (auto it = items.begin(); it != items.end(); it++) {
QGraphicsItem *item = *it;
if (item->data(0) == "node") {
newHoverNodeItem = (SkeletonGraphicsNodeItem *)item;
break;
} else if (item->data(0) == "edge") {
newHoverEdgeItem = (SkeletonGraphicsEdgeItem *)item;
} else if (item->data(0) == "origin") {
newHoverOriginItem = (SkeletonGraphicsOriginItem *)item;
}
}
if (newHoverNodeItem) {
newHoverEdgeItem = nullptr;
}
if (newHoverOriginItem != m_hoveredOriginItem) {
if (nullptr != m_hoveredOriginItem) {
m_hoveredOriginItem->setHovered(false);
}
m_hoveredOriginItem = newHoverOriginItem;
if (nullptr != m_hoveredOriginItem) {
m_hoveredOriginItem->setHovered(true);
}
}
if (newHoverNodeItem != m_hoveredNodeItem) {
if (nullptr != m_hoveredNodeItem) {
m_hoveredNodeItem->setHovered(false);
@ -481,7 +506,16 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
}
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
if (m_moveStarted && !m_rangeSelectionSet.empty()) {
if (m_moveStarted) {
if (m_checkedOriginItem) {
QPointF mouseScenePos = mouseEventScenePos(event);
float byX = mouseScenePos.x() - m_lastScenePos.x();
float byY = mouseScenePos.y() - m_lastScenePos.y();
moveCheckedOrigin(byX, byY);
m_lastScenePos = mouseScenePos;
m_moveHappened = true;
return true;
} else if (!m_rangeSelectionSet.empty()) {
QPointF mouseScenePos = mouseEventScenePos(event);
float byX = mouseScenePos.x() - m_lastScenePos.x();
float byY = mouseScenePos.y() - m_lastScenePos.y();
@ -495,6 +529,7 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
return true;
}
}
}
return false;
}
@ -529,6 +564,19 @@ void SkeletonGraphicsWidget::rotateSelected(int degree)
}
}
void SkeletonGraphicsWidget::moveCheckedOrigin(float byX, float byY)
{
if (!m_checkedOriginItem)
return;
byX = sceneRadiusToUnified(byX);
byY = sceneRadiusToUnified(byY);
if (SkeletonProfile::Main == m_checkedOriginItem->profile()) {
emit moveOriginBy(byX, byY, 0);
} else {
emit moveOriginBy(0, byY, byX);
}
}
void SkeletonGraphicsWidget::moveSelected(float byX, float byY)
{
if (m_rangeSelectionSet.empty())
@ -760,8 +808,25 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
return true;
}
} else if (SkeletonDocumentEditMode::Select == m_document->editMode) {
//if (m_mouseEventFromSelf) {
bool processed = false;
if (m_hoveredOriginItem != m_checkedOriginItem) {
if (m_checkedOriginItem) {
m_checkedOriginItem->setChecked(false);
m_checkedOriginItem->setHovered(false);
}
m_checkedOriginItem = m_hoveredOriginItem;
if (m_checkedOriginItem) {
m_checkedOriginItem->setChecked(true);
}
}
if (m_checkedOriginItem) {
if (!m_moveStarted) {
m_moveStarted = true;
m_lastScenePos = mouseEventScenePos(event);
m_moveHappened = false;
processed = true;
}
} else {
if ((nullptr == m_hoveredNodeItem || m_rangeSelectionSet.find(m_hoveredNodeItem) == m_rangeSelectionSet.end()) &&
(nullptr == m_hoveredEdgeItem || m_rangeSelectionSet.find(m_hoveredEdgeItem) == m_rangeSelectionSet.end())) {
if (!QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
@ -792,10 +857,10 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
}
}
}
}
if (processed) {
return true;
}
//}
}
}
@ -938,25 +1003,45 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
emit groupOperationAdded();
}
} else if (event->key() == Qt::Key_Left) {
if (SkeletonDocumentEditMode::Select == m_document->editMode && hasSelection()) {
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
if (m_checkedOriginItem) {
moveCheckedOrigin(-1, 0);
emit groupOperationAdded();
} else if (hasSelection()) {
moveSelected(-1, 0);
emit groupOperationAdded();
}
}
} else if (event->key() == Qt::Key_Right) {
if (SkeletonDocumentEditMode::Select == m_document->editMode && hasSelection()) {
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
if (m_checkedOriginItem) {
moveCheckedOrigin(1, 0);
emit groupOperationAdded();
} else if (hasSelection()) {
moveSelected(1, 0);
emit groupOperationAdded();
}
}
} else if (event->key() == Qt::Key_Up) {
if (SkeletonDocumentEditMode::Select == m_document->editMode && hasSelection()) {
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
if (m_checkedOriginItem) {
moveCheckedOrigin(0, -1);
emit groupOperationAdded();
} else if (hasSelection()) {
moveSelected(0, -1);
emit groupOperationAdded();
}
}
} else if (event->key() == Qt::Key_Down) {
if (SkeletonDocumentEditMode::Select == m_document->editMode && hasSelection()) {
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
if (m_checkedOriginItem) {
moveCheckedOrigin(0, 1);
emit groupOperationAdded();
} else if (hasSelection()) {
moveSelected(0, 1);
emit groupOperationAdded();
}
}
} else if (event->key() == Qt::Key_BracketLeft) {
if (SkeletonDocumentEditMode::Select == m_document->editMode && hasSelection()) {
scaleSelected(-1);
@ -971,6 +1056,19 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
return false;
}
void SkeletonGraphicsWidget::originChanged()
{
if (!m_document->originSettled()) {
m_mainOriginItem->hide();
m_sideOriginItem->hide();
return;
}
m_mainOriginItem->setOrigin(scenePosFromUnified(QPointF(m_document->originX, m_document->originY)));
m_sideOriginItem->setOrigin(scenePosFromUnified(QPointF(m_document->originZ, m_document->originY)));
m_mainOriginItem->show();
m_sideOriginItem->show();
}
void SkeletonGraphicsWidget::nodeAdded(QUuid nodeId)
{
const SkeletonNode *node = m_document->findNode(nodeId);

View File

@ -7,6 +7,7 @@
#include <QGraphicsLineItem>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QGraphicsPolygonItem>
#include <QThread>
#include <cmath>
#include <set>
@ -15,6 +16,88 @@
#include "theme.h"
#include "util.h"
class SkeletonGraphicsOriginItem : public QGraphicsPolygonItem
{
public:
SkeletonGraphicsOriginItem(SkeletonProfile profile=SkeletonProfile::Unknown) :
m_profile(profile),
m_hovered(false),
m_checked(false)
{
setData(0, "origin");
}
void updateAppearance()
{
QColor color = Theme::white;
switch (m_profile)
{
case SkeletonProfile::Unknown:
break;
case SkeletonProfile::Main:
color = Theme::red;
break;
case SkeletonProfile::Side:
color = Theme::green;
break;
}
QColor penColor = color;
penColor.setAlphaF(m_checked ? Theme::checkedAlpha : Theme::normalAlpha);
QPen pen(penColor);
pen.setWidth(0);
setPen(pen);
QColor brushColor = color;
brushColor.setAlphaF((m_checked || m_hovered) ? Theme::checkedAlpha : Theme::normalAlpha);
QBrush brush(brushColor);
setBrush(brush);
}
void setOrigin(QPointF point)
{
m_origin = point;
QPolygonF triangle;
const int triangleRadius = 10;
triangle.append(QPointF(point.x() - triangleRadius, point.y()));
triangle.append(QPointF(point.x() + triangleRadius, point.y()));
triangle.append(QPointF(point.x(), point.y() - triangleRadius));
triangle.append(QPointF(point.x() - triangleRadius, point.y()));
setPolygon(triangle);
updateAppearance();
}
QPointF origin()
{
return m_origin;
}
SkeletonProfile profile()
{
return m_profile;
}
void setHovered(bool hovered)
{
m_hovered = hovered;
updateAppearance();
}
void setChecked(bool checked)
{
m_checked = checked;
updateAppearance();
}
bool checked()
{
return m_checked;
}
bool hovered()
{
return m_hovered;
}
private:
SkeletonProfile m_profile;
bool m_hovered;
bool m_checked;
QPointF m_origin;
};
class SkeletonGraphicsSelectionItem : public QGraphicsRectItem
{
public:
@ -271,6 +354,7 @@ signals:
void open();
void exportResult();
void breakEdge(QUuid edgeId);
void moveOriginBy(float x, float y, float z);
public:
SkeletonGraphicsWidget(const SkeletonDocument *document);
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
@ -327,6 +411,8 @@ public slots:
void zoomSelected(float delta);
void scaleSelected(float delta);
void moveSelected(float byX, float byY);
void moveCheckedOrigin(float byX, float byY);
void originChanged();
private slots:
void turnaroundImageReady();
private:
@ -344,15 +430,13 @@ private:
bool isSingleNodeSelected();
void addItemToRangeSelection(QGraphicsItem *item);
void removeItemFromRangeSelection(QGraphicsItem *item);
private:
QGraphicsPixmapItem *m_backgroundItem;
private: //need initalize
const SkeletonDocument *m_document;
QGraphicsPixmapItem *m_backgroundItem;
bool m_turnaroundChanged;
TurnaroundLoader *m_turnaroundLoader;
bool m_dragStarted;
QPoint m_lastGlobalPos;
bool m_moveStarted;
QPointF m_lastScenePos;
SkeletonGraphicsNodeItem *m_cursorNodeItem;
SkeletonGraphicsEdgeItem *m_cursorEdgeItem;
SkeletonGraphicsNodeItem *m_addFromNodeItem;
@ -362,12 +446,19 @@ private:
float m_lastAddedY;
float m_lastAddedZ;
SkeletonGraphicsSelectionItem *m_selectionItem;
QPointF m_rangeSelectionStartPos;
bool m_rangeSelectionStarted;
std::set<QGraphicsItem *> m_rangeSelectionSet;
bool m_mouseEventFromSelf;
bool m_moveHappened;
int m_lastRot;
SkeletonGraphicsOriginItem *m_mainOriginItem;
SkeletonGraphicsOriginItem *m_sideOriginItem;
SkeletonGraphicsOriginItem *m_hoveredOriginItem;
SkeletonGraphicsOriginItem *m_checkedOriginItem;
private:
std::set<QGraphicsItem *> m_rangeSelectionSet;
QPoint m_lastGlobalPos;
QPointF m_lastScenePos;
QPointF m_rangeSelectionStartPos;
};
class SkeletonGraphicsContainerWidget : public QWidget

View File

@ -24,6 +24,14 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
updateButton(m_disableButton, QChar(fa::toggleon), false);
initButton(m_disableButton);
m_xMirrorButton = new QPushButton();
updateButton(m_xMirrorButton, QChar(fa::quoteleft), false);
initButton(m_xMirrorButton);
m_zMirrorButton = new QPushButton();
updateButton(m_zMirrorButton, QChar(fa::quoteright), false);
initButton(m_zMirrorButton);
m_previewLabel = new QLabel;
QHBoxLayout *miniTopToolLayout = new QHBoxLayout;
@ -38,6 +46,8 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
miniBottomToolLayout->setSpacing(0);
miniBottomToolLayout->setContentsMargins(0, 0, 0, 0);
miniBottomToolLayout->addWidget(m_disableButton);
miniBottomToolLayout->addWidget(m_xMirrorButton);
miniBottomToolLayout->addWidget(m_zMirrorButton);
miniBottomToolLayout->addStretch();
QWidget *hrWidget = new QWidget;
@ -60,37 +70,61 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
connect(this, &SkeletonPartWidget::setPartVisibleState, m_document, &SkeletonDocument::setPartVisibleState);
connect(this, &SkeletonPartWidget::setPartSubdivState, m_document, &SkeletonDocument::setPartSubdivState);
connect(this, &SkeletonPartWidget::setPartDisableState, m_document, &SkeletonDocument::setPartDisableState);
connect(this, &SkeletonPartWidget::setPartXmirrorState, m_document, &SkeletonDocument::setPartXmirrorState);
connect(this, &SkeletonPartWidget::setPartZmirrorState, m_document, &SkeletonDocument::setPartZmirrorState);
connect(m_lockButton, &QPushButton::clicked, [=]() {
if (m_lockButton->text() == QChar(fa::lock)) {
emit setPartLockState(m_partId, false);
} else {
emit setPartLockState(m_partId, true);
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartLockState(m_partId, !part->locked);
});
connect(m_visibleButton, &QPushButton::clicked, [=]() {
if (m_visibleButton->text() == QChar(fa::eye)) {
emit setPartVisibleState(m_partId, false);
} else {
emit setPartVisibleState(m_partId, true);
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartVisibleState(m_partId, !part->visible);
});
connect(m_subdivButton, &QPushButton::clicked, [=]() {
if (m_subdivButton->text() == QChar(fa::cube)) {
emit setPartSubdivState(m_partId, true);
} else {
emit setPartSubdivState(m_partId, false);
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartSubdivState(m_partId, !part->subdived);
});
connect(m_disableButton, &QPushButton::clicked, [=]() {
if (m_disableButton->text() == QChar(fa::link)) {
emit setPartDisableState(m_partId, true);
} else {
emit setPartDisableState(m_partId, false);
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartDisableState(m_partId, !part->disabled);
});
connect(m_xMirrorButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartXmirrorState(m_partId, !part->xMirrored);
});
connect(m_zMirrorButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartZmirrorState(m_partId, !part->zMirrored);
});
}
@ -172,6 +206,32 @@ void SkeletonPartWidget::updateDisableButton()
updateButton(m_disableButton, QChar(fa::link), false);
}
void SkeletonPartWidget::updateXmirrorButton()
{
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
if (part->xMirrored)
updateButton(m_xMirrorButton, QChar(fa::quoteleft), true);
else
updateButton(m_xMirrorButton, QChar(fa::quoteleft), false);
}
void SkeletonPartWidget::updateZmirrorButton()
{
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
if (part->zMirrored)
updateButton(m_zMirrorButton, QChar(fa::quoteright), true);
else
updateButton(m_zMirrorButton, QChar(fa::quoteright), false);
}
void SkeletonPartWidget::reload()
{
updatePreview();
@ -179,6 +239,8 @@ void SkeletonPartWidget::reload()
updateVisibleButton();
updateSubdivButton();
updateDisableButton();
updateXmirrorButton();
updateZmirrorButton();
}
SkeletonPartListWidget::SkeletonPartListWidget(const SkeletonDocument *document) :
@ -273,3 +335,25 @@ void SkeletonPartListWidget::partDisableStateChanged(QUuid partId)
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second);
widget->updateDisableButton();
}
void SkeletonPartListWidget::partXmirrorStateChanged(QUuid partId)
{
auto item = m_itemMap.find(partId);
if (item == m_itemMap.end()) {
qDebug() << "Part item not found:" << partId;
return;
}
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second);
widget->updateXmirrorButton();
}
void SkeletonPartListWidget::partZmirrorStateChanged(QUuid partId)
{
auto item = m_itemMap.find(partId);
if (item == m_itemMap.end()) {
qDebug() << "Part item not found:" << partId;
return;
}
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second);
widget->updateZmirrorButton();
}

View File

@ -14,6 +14,8 @@ signals:
void setPartVisibleState(QUuid partId, bool visible);
void setPartSubdivState(QUuid partId, bool subdived);
void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartZmirrorState(QUuid partId, bool mirrored);
public:
SkeletonPartWidget(const SkeletonDocument *document, QUuid partId);
void reload();
@ -22,6 +24,8 @@ public:
void updateVisibleButton();
void updateSubdivButton();
void updateDisableButton();
void updateXmirrorButton();
void updateZmirrorButton();
private:
const SkeletonDocument *m_document;
QUuid m_partId;
@ -30,6 +34,8 @@ private:
QPushButton *m_lockButton;
QPushButton *m_subdivButton;
QPushButton *m_disableButton;
QPushButton *m_xMirrorButton;
QPushButton *m_zMirrorButton;
QLabel *m_nameLabel;
private:
void initButton(QPushButton *button);
@ -49,6 +55,8 @@ public slots:
void partVisibleStateChanged(QUuid partId);
void partSubdivStateChanged(QUuid partId);
void partDisableStateChanged(QUuid partId);
void partXmirrorStateChanged(QUuid partId);
void partZmirrorStateChanged(QUuid partId);
private:
const SkeletonDocument *m_document;
std::map<QUuid, QListWidgetItem *> m_itemMap;

View File

@ -1,2 +1,58 @@
#include <QDebug>
#include "skeletonsnapshot.h"
#include <assert.h>
#include "util.h"
void SkeletonSnapshot::resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile, const QString &partId)
{
float left = 0;
bool leftFirstTime = true;
float right = 0;
bool rightFirstTime = true;
float top = 0;
bool topFirstTime = true;
float bottom = 0;
bool bottomFirstTime = true;
float zLeft = 0;
bool zLeftFirstTime = true;
float zRight = 0;
bool zRightFirstTime = true;
for (const auto &nodeIt: nodes) {
if (!partId.isEmpty() && partId != valueOfKeyInMapOrEmpty(nodeIt.second, "partId"))
continue;
float radius = valueOfKeyInMapOrEmpty(nodeIt.second, "radius").toFloat();
float x = valueOfKeyInMapOrEmpty(nodeIt.second, "x").toFloat();
float y = valueOfKeyInMapOrEmpty(nodeIt.second, "y").toFloat();
float z = valueOfKeyInMapOrEmpty(nodeIt.second, "z").toFloat();
if (leftFirstTime || x - radius < left) {
left = x - radius;
leftFirstTime = false;
}
if (topFirstTime || y - radius < top) {
top = y - radius;
topFirstTime = false;
}
if (rightFirstTime || x + radius > right) {
right = x + radius;
rightFirstTime = false;
}
if (bottomFirstTime || y + radius > bottom) {
bottom = y + radius;
bottomFirstTime = false;
}
if (zLeftFirstTime || z - radius < zLeft) {
zLeft = z - radius;
zLeftFirstTime = false;
}
if (zRightFirstTime || z + radius > zRight) {
zRight = z + radius;
zRightFirstTime = false;
}
}
*mainProfile = QRectF(QPointF(left, top), QPointF(right, bottom));
*sideProfile = QRectF(QPointF(zLeft, top), QPointF(zRight, bottom));
qDebug() << "resolveBoundingBox left:" << left << "top:" << top << "right:" << right << "bottom:" << bottom << " zLeft:" << zLeft << "zRight:" << zRight;
qDebug() << "mainHeight:" << mainProfile->height() << "mainWidth:" << mainProfile->width();
qDebug() << "sideHeight:" << sideProfile->height() << "sideWidth:" << sideProfile->width();
}

View File

@ -14,6 +14,8 @@ public:
std::map<QString, std::map<QString, QString>> edges;
std::map<QString, std::map<QString, QString>> parts;
std::vector<QString> partIdList;
public:
void resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile, const QString &partId=QString());
};
#endif

BIN
thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll vendored Normal file → Executable file

Binary file not shown.

BIN
thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib vendored Normal file → Executable file

Binary file not shown.

2
thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h vendored Normal file → Executable file
View File

@ -42,6 +42,8 @@ int meshlite_bmesh_error_count(void *context, int bmesh_id);
int meshlite_combine_adj_faces(void *context, int mesh_id);
int meshlite_combine_coplanar_faces(void *context, int mesh_id);
int meshlite_trim(void *context, int mesh_id, int normalize);
int meshlite_mirror_in_x(void *context, int mesh_id, float center_x);
int meshlite_mirror_in_z(void *context, int mesh_id, float center_z);
#ifdef __cplusplus
}

BIN
thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll vendored Normal file → Executable file

Binary file not shown.

BIN
thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib vendored Normal file → Executable file

Binary file not shown.

2
thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h vendored Normal file → Executable file
View File

@ -42,6 +42,8 @@ int meshlite_bmesh_error_count(void *context, int bmesh_id);
int meshlite_combine_adj_faces(void *context, int mesh_id);
int meshlite_combine_coplanar_faces(void *context, int mesh_id);
int meshlite_trim(void *context, int mesh_id, int normalize);
int meshlite_mirror_in_x(void *context, int mesh_id, float center_x);
int meshlite_mirror_in_z(void *context, int mesh_id, float center_z);
#ifdef __cplusplus
}