2018-04-07 08:44:39 +00:00
|
|
|
#ifndef SKELETON_GRAPHICS_VIEW_H
|
|
|
|
#define SKELETON_GRAPHICS_VIEW_H
|
|
|
|
#include <map>
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QKeyEvent>
|
|
|
|
#include <QGraphicsEllipseItem>
|
|
|
|
#include <QGraphicsLineItem>
|
|
|
|
#include <QGraphicsView>
|
|
|
|
#include <QGraphicsPixmapItem>
|
|
|
|
#include <QThread>
|
|
|
|
#include <cmath>
|
2018-04-08 15:08:23 +00:00
|
|
|
#include <set>
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "skeletondocument.h"
|
|
|
|
#include "turnaroundloader.h"
|
|
|
|
#include "theme.h"
|
2018-04-12 08:34:00 +00:00
|
|
|
#include "util.h"
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-04-08 15:08:23 +00:00
|
|
|
class SkeletonGraphicsSelectionItem : public QGraphicsRectItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SkeletonGraphicsSelectionItem()
|
|
|
|
{
|
|
|
|
QColor penColor = Theme::white;
|
|
|
|
QPen pen(penColor);
|
|
|
|
pen.setWidth(0);
|
|
|
|
pen.setStyle(Qt::DashLine);
|
|
|
|
setPen(pen);
|
|
|
|
}
|
|
|
|
void updateRange(QPointF beginPos, QPointF endPos)
|
|
|
|
{
|
|
|
|
setRect(QRectF(beginPos, endPos).normalized());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-07 08:44:39 +00:00
|
|
|
class SkeletonGraphicsNodeItem : public QGraphicsEllipseItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SkeletonGraphicsNodeItem(SkeletonProfile profile=SkeletonProfile::Unknown) :
|
|
|
|
m_profile(profile),
|
|
|
|
m_hovered(false),
|
|
|
|
m_checked(false)
|
|
|
|
{
|
|
|
|
setData(0, "node");
|
|
|
|
setRadius(32);
|
|
|
|
}
|
|
|
|
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::fillAlpha : 0);
|
|
|
|
QBrush brush(brushColor);
|
|
|
|
setBrush(brush);
|
|
|
|
}
|
|
|
|
void setOrigin(QPointF point)
|
|
|
|
{
|
|
|
|
QPointF moveBy = point - origin();
|
|
|
|
QRectF newRect = rect();
|
|
|
|
newRect.adjust(moveBy.x(), moveBy.y(), moveBy.x(), moveBy.y());
|
|
|
|
setRect(newRect);
|
|
|
|
updateAppearance();
|
|
|
|
}
|
|
|
|
QPointF origin()
|
|
|
|
{
|
|
|
|
return QPointF(rect().x() + rect().width() / 2,
|
|
|
|
rect().y() + rect().height() / 2);
|
|
|
|
}
|
|
|
|
float radius()
|
|
|
|
{
|
|
|
|
return rect().width() / 2;
|
|
|
|
}
|
|
|
|
void setRadius(float radius)
|
|
|
|
{
|
|
|
|
if (radius < 4)
|
|
|
|
radius = 4;
|
|
|
|
QPointF oldOrigin = origin();
|
|
|
|
setRect(oldOrigin.x() - radius, oldOrigin.y() - radius,
|
|
|
|
radius * 2, radius * 2);
|
|
|
|
updateAppearance();
|
|
|
|
}
|
|
|
|
SkeletonProfile profile()
|
|
|
|
{
|
|
|
|
return m_profile;
|
|
|
|
}
|
|
|
|
QUuid id()
|
|
|
|
{
|
|
|
|
return m_uuid;
|
|
|
|
}
|
|
|
|
void setId(QUuid id)
|
|
|
|
{
|
|
|
|
m_uuid = id;
|
|
|
|
}
|
|
|
|
void setHovered(bool hovered)
|
|
|
|
{
|
|
|
|
m_hovered = hovered;
|
|
|
|
updateAppearance();
|
|
|
|
}
|
|
|
|
void setChecked(bool checked)
|
|
|
|
{
|
|
|
|
m_checked = checked;
|
|
|
|
updateAppearance();
|
|
|
|
}
|
|
|
|
bool checked()
|
|
|
|
{
|
|
|
|
return m_checked;
|
|
|
|
}
|
2018-04-08 15:08:23 +00:00
|
|
|
bool hovered()
|
|
|
|
{
|
|
|
|
return m_hovered;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
private:
|
|
|
|
QUuid m_uuid;
|
|
|
|
SkeletonProfile m_profile;
|
|
|
|
bool m_hovered;
|
|
|
|
bool m_checked;
|
|
|
|
};
|
|
|
|
|
|
|
|
class SkeletonGraphicsEdgeItem : public QGraphicsPolygonItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SkeletonGraphicsEdgeItem() :
|
|
|
|
m_firstItem(nullptr),
|
|
|
|
m_secondItem(nullptr),
|
|
|
|
m_hovered(false),
|
2018-04-08 15:08:23 +00:00
|
|
|
m_checked(false),
|
|
|
|
m_profile(SkeletonProfile::Unknown)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
|
|
|
setData(0, "edge");
|
|
|
|
}
|
|
|
|
void setEndpoints(SkeletonGraphicsNodeItem *first, SkeletonGraphicsNodeItem *second)
|
|
|
|
{
|
|
|
|
m_firstItem = first;
|
|
|
|
m_secondItem = second;
|
|
|
|
updateAppearance();
|
|
|
|
}
|
2018-04-08 15:08:23 +00:00
|
|
|
SkeletonGraphicsNodeItem *firstItem()
|
|
|
|
{
|
|
|
|
return m_firstItem;
|
|
|
|
}
|
|
|
|
SkeletonGraphicsNodeItem *secondItem()
|
|
|
|
{
|
|
|
|
return m_secondItem;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
void updateAppearance()
|
|
|
|
{
|
|
|
|
if (nullptr == m_firstItem || nullptr == m_secondItem)
|
|
|
|
return;
|
|
|
|
|
2018-04-08 15:08:23 +00:00
|
|
|
m_profile = m_firstItem->profile();
|
|
|
|
|
2018-04-07 08:44:39 +00:00
|
|
|
QLineF line(m_firstItem->origin(), m_secondItem->origin());
|
|
|
|
|
|
|
|
QPolygonF polygon;
|
|
|
|
float radAngle = line.angle() * M_PI / 180;
|
2018-04-08 08:05:12 +00:00
|
|
|
float dx = 2 * sin(radAngle);
|
|
|
|
float dy = 2 * cos(radAngle);
|
2018-04-07 08:44:39 +00:00
|
|
|
QPointF offset1 = QPointF(dx, dy);
|
|
|
|
QPointF offset2 = QPointF(-dx, -dy);
|
|
|
|
polygon << line.p1() + offset1 << line.p1() + offset2 << line.p2() + offset2 << line.p2() + offset1;
|
|
|
|
setPolygon(polygon);
|
|
|
|
|
|
|
|
QColor color = Theme::white;
|
|
|
|
|
|
|
|
switch (m_firstItem->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 || m_hovered) ? Theme::checkedAlpha : Theme::normalAlpha);
|
|
|
|
QPen pen(penColor);
|
|
|
|
pen.setWidth(0);
|
|
|
|
setPen(pen);
|
|
|
|
}
|
|
|
|
QUuid id()
|
|
|
|
{
|
|
|
|
return m_uuid;
|
|
|
|
}
|
|
|
|
void setId(QUuid id)
|
|
|
|
{
|
|
|
|
m_uuid = id;
|
|
|
|
}
|
2018-04-08 15:08:23 +00:00
|
|
|
SkeletonProfile profile()
|
|
|
|
{
|
|
|
|
return m_profile;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
void setHovered(bool hovered)
|
|
|
|
{
|
|
|
|
m_hovered = hovered;
|
|
|
|
updateAppearance();
|
|
|
|
}
|
|
|
|
void setChecked(bool checked)
|
|
|
|
{
|
|
|
|
m_checked = checked;
|
|
|
|
updateAppearance();
|
|
|
|
}
|
2018-04-08 15:08:23 +00:00
|
|
|
bool checked()
|
|
|
|
{
|
|
|
|
return m_checked;
|
|
|
|
}
|
|
|
|
bool hovered()
|
|
|
|
{
|
|
|
|
return m_hovered;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
private:
|
|
|
|
QUuid m_uuid;
|
|
|
|
SkeletonGraphicsNodeItem *m_firstItem;
|
|
|
|
SkeletonGraphicsNodeItem *m_secondItem;
|
|
|
|
QPolygonF m_selectionPolygon;
|
|
|
|
bool m_hovered;
|
|
|
|
bool m_checked;
|
2018-04-08 15:08:23 +00:00
|
|
|
SkeletonProfile m_profile;
|
2018-04-07 08:44:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SkeletonGraphicsFunctions
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual bool mouseMove(QMouseEvent *event) = 0;
|
|
|
|
virtual bool wheel(QWheelEvent *event) = 0;
|
|
|
|
virtual bool mouseRelease(QMouseEvent *event) = 0;
|
|
|
|
virtual bool mousePress(QMouseEvent *event) = 0;
|
|
|
|
virtual bool mouseDoubleClick(QMouseEvent *event) = 0;
|
|
|
|
virtual bool keyPress(QKeyEvent *event) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
class SkeletonGraphicsWidget : public QGraphicsView, public SkeletonGraphicsFunctions
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
signals:
|
|
|
|
void addNode(float x, float y, float z, float radius, QUuid fromNodeId);
|
|
|
|
void scaleNodeByAddRadius(QUuid nodeId, float amount);
|
|
|
|
void moveNodeBy(QUuid nodeId, float x, float y, float z);
|
|
|
|
void removeNode(QUuid nodeId);
|
|
|
|
void setEditMode(SkeletonDocumentEditMode mode);
|
|
|
|
void removeEdge(QUuid edgeId);
|
|
|
|
void addEdge(QUuid fromNodeId, QUuid toNodeId);
|
|
|
|
void cursorChanged();
|
2018-04-08 23:34:46 +00:00
|
|
|
void groupOperationAdded();
|
|
|
|
void undo();
|
|
|
|
void redo();
|
2018-04-09 08:46:06 +00:00
|
|
|
void paste();
|
2018-04-09 11:13:34 +00:00
|
|
|
void changeTurnaround();
|
2018-04-09 14:24:30 +00:00
|
|
|
void batchChangeBegin();
|
|
|
|
void batchChangeEnd();
|
2018-04-10 07:59:20 +00:00
|
|
|
void save();
|
|
|
|
void open();
|
|
|
|
void exportResult();
|
2018-04-12 12:27:21 +00:00
|
|
|
void breakEdge(QUuid edgeId);
|
2018-04-07 08:44:39 +00:00
|
|
|
public:
|
|
|
|
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
|
|
|
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
|
|
|
|
std::map<QUuid, std::pair<SkeletonGraphicsEdgeItem *, SkeletonGraphicsEdgeItem *>> edgeItemMap;
|
|
|
|
bool mouseMove(QMouseEvent *event);
|
|
|
|
bool wheel(QWheelEvent *event);
|
|
|
|
bool mouseRelease(QMouseEvent *event);
|
|
|
|
bool mousePress(QMouseEvent *event);
|
|
|
|
bool mouseDoubleClick(QMouseEvent *event);
|
|
|
|
bool keyPress(QKeyEvent *event);
|
2018-04-08 15:08:23 +00:00
|
|
|
static bool checkSkeletonItem(QGraphicsItem *item, bool checked);
|
|
|
|
static SkeletonProfile readSkeletonItemProfile(QGraphicsItem *item);
|
|
|
|
void readMergedSkeletonNodeSetFromRangeSelection(std::set<SkeletonGraphicsNodeItem *> *nodeItemSet);
|
|
|
|
void readSkeletonNodeAndEdgeIdSetFromRangeSelection(std::set<QUuid> *nodeIdSet, std::set<QUuid> *edgeIdSet);
|
2018-04-09 11:13:34 +00:00
|
|
|
bool readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(SkeletonGraphicsNodeItem **nodeItem, SkeletonGraphicsEdgeItem **edgeItem);
|
2018-04-10 07:59:20 +00:00
|
|
|
bool hasSelection();
|
|
|
|
bool hasItems();
|
|
|
|
bool hasMultipleSelection();
|
2018-04-12 12:27:21 +00:00
|
|
|
bool hasEdgeSelection();
|
2018-04-07 08:44:39 +00:00
|
|
|
protected:
|
|
|
|
void mouseMoveEvent(QMouseEvent *event);
|
|
|
|
void wheelEvent(QWheelEvent *event);
|
|
|
|
void mouseReleaseEvent(QMouseEvent *event);
|
|
|
|
void mousePressEvent(QMouseEvent *event);
|
|
|
|
void mouseDoubleClickEvent(QMouseEvent *event);
|
|
|
|
void keyPressEvent(QKeyEvent *event);
|
|
|
|
public slots:
|
|
|
|
void nodeAdded(QUuid nodeId);
|
|
|
|
void edgeAdded(QUuid edgeId);
|
|
|
|
void nodeRemoved(QUuid nodeId);
|
|
|
|
void edgeRemoved(QUuid edgeId);
|
|
|
|
void nodeRadiusChanged(QUuid nodeId);
|
|
|
|
void nodeOriginChanged(QUuid nodeId);
|
|
|
|
void edgeChanged(QUuid edgeId);
|
|
|
|
void turnaroundChanged();
|
|
|
|
void canvasResized();
|
|
|
|
void editModeChanged();
|
|
|
|
void updateCursor();
|
2018-04-08 01:30:55 +00:00
|
|
|
void partVisibleStateChanged(QUuid partId);
|
2018-04-09 03:39:04 +00:00
|
|
|
void showContextMenu(const QPoint &pos);
|
|
|
|
void deleteSelected();
|
|
|
|
void selectAll();
|
|
|
|
void unselectAll();
|
2018-04-09 05:22:25 +00:00
|
|
|
void selectPartAll();
|
2018-04-09 08:46:06 +00:00
|
|
|
void cut();
|
|
|
|
void copy();
|
2018-04-09 14:24:30 +00:00
|
|
|
void flipHorizontally();
|
|
|
|
void flipVertically();
|
2018-04-10 07:59:20 +00:00
|
|
|
void removeAllContent();
|
2018-04-12 12:27:21 +00:00
|
|
|
void breakSelected();
|
2018-04-07 08:44:39 +00:00
|
|
|
private slots:
|
|
|
|
void turnaroundImageReady();
|
|
|
|
private:
|
|
|
|
QPointF mouseEventScenePos(QMouseEvent *event);
|
|
|
|
QPointF scenePosToUnified(QPointF pos);
|
|
|
|
QPointF scenePosFromUnified(QPointF pos);
|
|
|
|
float sceneRadiusToUnified(float radius);
|
|
|
|
float sceneRadiusFromUnified(float radius);
|
|
|
|
void updateTurnaround();
|
|
|
|
void updateItems();
|
2018-04-08 15:08:23 +00:00
|
|
|
void checkRangeSelection();
|
|
|
|
void clearRangeSelection();
|
|
|
|
void removeItem(QGraphicsItem *item);
|
2018-04-09 14:24:30 +00:00
|
|
|
QVector2D centerOfNodeItemSet(const std::set<SkeletonGraphicsNodeItem *> &set);
|
2018-04-09 14:37:20 +00:00
|
|
|
bool isSingleNodeSelected();
|
2018-04-10 08:44:02 +00:00
|
|
|
void addItemToRangeSelection(QGraphicsItem *item);
|
|
|
|
void removeItemFromRangeSelection(QGraphicsItem *item);
|
2018-04-07 08:44:39 +00:00
|
|
|
private:
|
|
|
|
QGraphicsPixmapItem *m_backgroundItem;
|
|
|
|
const SkeletonDocument *m_document;
|
|
|
|
bool m_turnaroundChanged;
|
|
|
|
TurnaroundLoader *m_turnaroundLoader;
|
|
|
|
bool m_dragStarted;
|
|
|
|
QPoint m_lastGlobalPos;
|
|
|
|
bool m_moveStarted;
|
|
|
|
QPointF m_lastScenePos;
|
|
|
|
SkeletonGraphicsNodeItem *m_cursorNodeItem;
|
|
|
|
SkeletonGraphicsEdgeItem *m_cursorEdgeItem;
|
2018-04-08 15:08:23 +00:00
|
|
|
SkeletonGraphicsNodeItem *m_addFromNodeItem;
|
2018-04-07 08:44:39 +00:00
|
|
|
SkeletonGraphicsNodeItem *m_hoveredNodeItem;
|
|
|
|
SkeletonGraphicsEdgeItem *m_hoveredEdgeItem;
|
|
|
|
float m_lastAddedX;
|
|
|
|
float m_lastAddedY;
|
|
|
|
float m_lastAddedZ;
|
2018-04-08 15:08:23 +00:00
|
|
|
SkeletonGraphicsSelectionItem *m_selectionItem;
|
|
|
|
QPointF m_rangeSelectionStartPos;
|
|
|
|
bool m_rangeSelectionStarted;
|
|
|
|
std::set<QGraphicsItem *> m_rangeSelectionSet;
|
|
|
|
bool m_mouseEventFromSelf;
|
2018-04-09 00:32:02 +00:00
|
|
|
bool m_moveHappened;
|
2018-04-07 08:44:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SkeletonGraphicsContainerWidget : public QWidget
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
signals:
|
|
|
|
void containerSizeChanged(QSize size);
|
|
|
|
public:
|
|
|
|
SkeletonGraphicsContainerWidget() :
|
|
|
|
m_graphicsWidget(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void resizeEvent(QResizeEvent *event) override
|
|
|
|
{
|
|
|
|
if (m_graphicsWidget && m_graphicsWidget->size() != event->size())
|
|
|
|
emit containerSizeChanged(event->size());
|
|
|
|
}
|
|
|
|
void setGraphicsWidget(SkeletonGraphicsWidget *graphicsWidget)
|
|
|
|
{
|
|
|
|
m_graphicsWidget = graphicsWidget;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
SkeletonGraphicsWidget *m_graphicsWidget;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|