Remove unused markpen code
parent
fac30fc6a4
commit
7817cde381
|
@ -439,12 +439,6 @@ HEADERS += src/triangulatefaces.h
|
||||||
SOURCES += src/booleanmesh.cpp
|
SOURCES += src/booleanmesh.cpp
|
||||||
HEADERS += src/booleanmesh.h
|
HEADERS += src/booleanmesh.h
|
||||||
|
|
||||||
SOURCES += src/imageskeletonextractor.cpp
|
|
||||||
HEADERS += src/imageskeletonextractor.h
|
|
||||||
|
|
||||||
SOURCES += src/contourtopartconverter.cpp
|
|
||||||
HEADERS += src/contourtopartconverter.h
|
|
||||||
|
|
||||||
SOURCES += src/remesher.cpp
|
SOURCES += src/remesher.cpp
|
||||||
HEADERS += src/remesher.h
|
HEADERS += src/remesher.h
|
||||||
|
|
||||||
|
|
|
@ -1,297 +0,0 @@
|
||||||
#include <QImage>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPainterPath>
|
|
||||||
#include <cmath>
|
|
||||||
#include <QUuid>
|
|
||||||
#include <cmath>
|
|
||||||
#include <QTransform>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include "contourtopartconverter.h"
|
|
||||||
#include "imageskeletonextractor.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
const float ContourToPartConverter::m_targetImageHeight = 64.0f;
|
|
||||||
const float ContourToPartConverter::m_minEdgeLength = 0.025;
|
|
||||||
const float ContourToPartConverter::m_radiusEpsilon = 0.0025;
|
|
||||||
|
|
||||||
ContourToPartConverter::ContourToPartConverter(const QPolygonF &mainProfile,
|
|
||||||
const QPolygonF &sideProfile, const QSizeF &canvasSize) :
|
|
||||||
m_mainProfile(mainProfile),
|
|
||||||
m_sideProfile(sideProfile),
|
|
||||||
m_canvasSize(canvasSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::process()
|
|
||||||
{
|
|
||||||
convert();
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Snapshot &ContourToPartConverter::getSnapshot()
|
|
||||||
{
|
|
||||||
return m_snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::extractSkeleton(const QPolygonF &polygon,
|
|
||||||
std::vector<std::pair<QVector2D, float>> *skeleton)
|
|
||||||
{
|
|
||||||
auto originalBoundingBox = polygon.boundingRect();
|
|
||||||
QPointF polygonTopLeft = originalBoundingBox.topLeft();
|
|
||||||
|
|
||||||
float scaleFactor = m_targetImageHeight / originalBoundingBox.height();
|
|
||||||
|
|
||||||
QTransform transform;
|
|
||||||
transform = transform.scale(scaleFactor, scaleFactor);
|
|
||||||
QPolygonF scaledPolygon = transform.map(polygon);
|
|
||||||
|
|
||||||
QRectF boundingBox = scaledPolygon.boundingRect();
|
|
||||||
QImage *image = new QImage(boundingBox.width() + 4, boundingBox.height() + 4, QImage::Format_Grayscale8);
|
|
||||||
image->fill(QColor(255, 255, 255));
|
|
||||||
|
|
||||||
qreal offsetX = 1 - boundingBox.left();
|
|
||||||
qreal offsetY = 1 - boundingBox.top();
|
|
||||||
|
|
||||||
QPainterPath path;
|
|
||||||
path.addPolygon(scaledPolygon.translated(offsetX, offsetY));
|
|
||||||
|
|
||||||
QPainter painter;
|
|
||||||
painter.begin(image);
|
|
||||||
painter.setPen(Qt::PenStyle::NoPen);
|
|
||||||
painter.fillPath(path, Qt::black);
|
|
||||||
painter.end();
|
|
||||||
|
|
||||||
ImageSkeletonExtractor imageSkeletonExtractor;
|
|
||||||
imageSkeletonExtractor.setImage(image);
|
|
||||||
imageSkeletonExtractor.extract();
|
|
||||||
|
|
||||||
std::vector<std::pair<int, int>> imageSkeleton;
|
|
||||||
int imageArea = imageSkeletonExtractor.getArea();
|
|
||||||
imageSkeletonExtractor.getSkeleton(&imageSkeleton);
|
|
||||||
const std::set<std::pair<int, int>> &blackPixels = imageSkeletonExtractor.getBlackPixels();
|
|
||||||
|
|
||||||
std::vector<std::pair<int, int>> selectedNodes;
|
|
||||||
if (imageSkeleton.size() >= 2) {
|
|
||||||
int targetLength = std::ceil(0.5 * (float)imageArea / imageSkeleton.size());
|
|
||||||
int minLength = m_minEdgeLength * imageSkeleton.size();
|
|
||||||
if (targetLength < minLength)
|
|
||||||
targetLength = minLength;
|
|
||||||
size_t newInsertNum = imageSkeleton.size() / targetLength;
|
|
||||||
if (newInsertNum < 1)
|
|
||||||
newInsertNum = 1;
|
|
||||||
if (newInsertNum > 100)
|
|
||||||
newInsertNum = 100;
|
|
||||||
float stepFactor = 1.0 / (newInsertNum + 1);
|
|
||||||
float factor = stepFactor;
|
|
||||||
selectedNodes.push_back(imageSkeleton[0]);
|
|
||||||
for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) {
|
|
||||||
size_t index = factor * imageSkeleton.size();
|
|
||||||
if (index <= 0 || index >= imageSkeleton.size())
|
|
||||||
continue;
|
|
||||||
selectedNodes.push_back(imageSkeleton[index]);
|
|
||||||
}
|
|
||||||
selectedNodes.push_back(imageSkeleton[imageSkeleton.size() - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<QVector3D> selectedPositions;
|
|
||||||
selectedPositions.reserve(selectedNodes.size());
|
|
||||||
for (const auto &it: selectedNodes)
|
|
||||||
selectedPositions.push_back(QVector3D(it.first, it.second, 0.0f));
|
|
||||||
|
|
||||||
std::vector<QVector3D> selectedDirections;
|
|
||||||
selectedDirections.reserve(selectedNodes.size());
|
|
||||||
for (size_t i = 0; i < selectedPositions.size(); ++i) {
|
|
||||||
QVector3D sumOfDirections;
|
|
||||||
if (i > 0) {
|
|
||||||
sumOfDirections += selectedPositions[i] - selectedPositions[i - 1];
|
|
||||||
}
|
|
||||||
if (i + 1 < selectedPositions.size()) {
|
|
||||||
sumOfDirections += selectedPositions[i + 1] - selectedPositions[i];
|
|
||||||
}
|
|
||||||
selectedDirections.push_back(sumOfDirections.normalized());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> selectedRadius;
|
|
||||||
selectedRadius.reserve(selectedNodes.size());
|
|
||||||
for (size_t i = 0; i < selectedDirections.size(); ++i) {
|
|
||||||
selectedRadius.push_back(calculateNodeRadius(selectedPositions[i], selectedDirections[i],
|
|
||||||
blackPixels));
|
|
||||||
}
|
|
||||||
|
|
||||||
skeleton->resize(selectedRadius.size());
|
|
||||||
auto canvasHeight = m_canvasSize.height();
|
|
||||||
for (size_t i = 0; i < skeleton->size(); ++i) {
|
|
||||||
const auto &node = selectedNodes[i];
|
|
||||||
(*skeleton)[i] = std::make_pair(QVector2D(node.first / scaleFactor + polygonTopLeft.x(),
|
|
||||||
node.second / scaleFactor + polygonTopLeft.y()) / canvasHeight,
|
|
||||||
selectedRadius[i] / scaleFactor / canvasHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ContourToPartConverter::calculateNodeRadius(const QVector3D &node,
|
|
||||||
const QVector3D &direction,
|
|
||||||
const std::set<std::pair<int, int>> &black)
|
|
||||||
{
|
|
||||||
const QVector3D pointer = {0.0f, 0.0f, 1.0f};
|
|
||||||
QVector3D offsetDirection = QVector3D::crossProduct(direction, pointer);
|
|
||||||
int radius = 1;
|
|
||||||
while (true) {
|
|
||||||
QVector3D offset = radius * offsetDirection;
|
|
||||||
QVector3D sidePosition = node + offset;
|
|
||||||
QVector3D otherSidePosition = node - offset;
|
|
||||||
if (black.find(std::make_pair((int)sidePosition.x(), (int)sidePosition.y())) == black.end())
|
|
||||||
break;
|
|
||||||
if (black.find(std::make_pair((int)otherSidePosition.x(), (int)otherSidePosition.y())) == black.end())
|
|
||||||
break;
|
|
||||||
++radius;
|
|
||||||
}
|
|
||||||
return radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::smoothRadius(std::vector<std::pair<QVector2D, float>> *skeleton)
|
|
||||||
{
|
|
||||||
if (skeleton->empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<float> newRadius;
|
|
||||||
newRadius.reserve(skeleton->size() + 2);
|
|
||||||
newRadius.push_back(skeleton->front().second);
|
|
||||||
for (const auto &it: (*skeleton)) {
|
|
||||||
newRadius.push_back(it.second);
|
|
||||||
}
|
|
||||||
newRadius.push_back(skeleton->back().second);
|
|
||||||
for (size_t h = 0; h < skeleton->size(); ++h) {
|
|
||||||
size_t i = h + 1;
|
|
||||||
size_t j = h + 2;
|
|
||||||
(*skeleton)[h].second = (newRadius[h] +
|
|
||||||
newRadius[i] + newRadius[j]) / 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::optimizeNodes()
|
|
||||||
{
|
|
||||||
auto oldNodes = m_nodes;
|
|
||||||
m_nodes.clear();
|
|
||||||
m_nodes.reserve(oldNodes.size());
|
|
||||||
for (size_t i = 0; i < oldNodes.size(); ++i) {
|
|
||||||
if (i > 0 && i + 1 < oldNodes.size()) {
|
|
||||||
size_t h = i - 1;
|
|
||||||
size_t j = i + 1;
|
|
||||||
if (std::abs(oldNodes[i].second - oldNodes[h].second) < m_radiusEpsilon &&
|
|
||||||
std::abs(oldNodes[i].second - oldNodes[j].second) < m_radiusEpsilon) {
|
|
||||||
auto degrees = degreesBetweenVectors((oldNodes[i].first - oldNodes[h].first).normalized(),
|
|
||||||
(oldNodes[j].first - oldNodes[i].first).normalized());
|
|
||||||
if (degrees < 15)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_nodes.push_back(oldNodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::alignSkeleton(const std::vector<std::pair<QVector2D, float>> &referenceSkeleton,
|
|
||||||
std::vector<std::pair<QVector2D, float>> &adjustSkeleton)
|
|
||||||
{
|
|
||||||
if (referenceSkeleton.empty() || adjustSkeleton.empty())
|
|
||||||
return;
|
|
||||||
float sumOfDistance2 = 0.0;
|
|
||||||
float reversedSumOfDistance2 = 0.0;
|
|
||||||
for (size_t i = 0; i < adjustSkeleton.size(); ++i) {
|
|
||||||
size_t j = ((float)i / adjustSkeleton.size()) * referenceSkeleton.size();
|
|
||||||
if (j >= referenceSkeleton.size())
|
|
||||||
continue;
|
|
||||||
size_t k = referenceSkeleton.size() - 1 - j;
|
|
||||||
sumOfDistance2 += std::pow(adjustSkeleton[i].first.y() - referenceSkeleton[j].first.y(), 2.0f);
|
|
||||||
reversedSumOfDistance2 += std::pow(adjustSkeleton[i].first.y() - referenceSkeleton[k].first.y(), 2.0f);
|
|
||||||
}
|
|
||||||
if (sumOfDistance2 <= reversedSumOfDistance2)
|
|
||||||
return;
|
|
||||||
std::reverse(adjustSkeleton.begin(), adjustSkeleton.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::convert()
|
|
||||||
{
|
|
||||||
std::vector<std::pair<QVector2D, float>> sideSkeleton;
|
|
||||||
std::vector<std::pair<QVector2D, float>> mainSkeleton;
|
|
||||||
auto mainBoundingBox = m_mainProfile.boundingRect();
|
|
||||||
extractSkeleton(m_sideProfile, &sideSkeleton);
|
|
||||||
extractSkeleton(m_mainProfile, &mainSkeleton);
|
|
||||||
smoothRadius(&sideSkeleton);
|
|
||||||
smoothRadius(&mainSkeleton);
|
|
||||||
if (!sideSkeleton.empty()) {
|
|
||||||
alignSkeleton(sideSkeleton, mainSkeleton);
|
|
||||||
float defaultX = mainBoundingBox.center().x() / m_canvasSize.height();
|
|
||||||
float area = mainBoundingBox.width() * mainBoundingBox.height();
|
|
||||||
float mainBoundingBoxWidthHeightOffset = std::abs(mainBoundingBox.width() - mainBoundingBox.height());
|
|
||||||
float rectRadius = std::sqrt(area) * 0.5;
|
|
||||||
bool useCalculatedX = mainBoundingBoxWidthHeightOffset >= rectRadius;
|
|
||||||
m_nodes.reserve(sideSkeleton.size());
|
|
||||||
for (size_t i = 0; i < sideSkeleton.size(); ++i) {
|
|
||||||
const auto &it = sideSkeleton[i];
|
|
||||||
float x = defaultX;
|
|
||||||
if (useCalculatedX) {
|
|
||||||
size_t j = ((float)i / sideSkeleton.size()) * mainSkeleton.size();
|
|
||||||
x = j < mainSkeleton.size() ? mainSkeleton[j].first.x() : defaultX;
|
|
||||||
}
|
|
||||||
m_nodes.push_back(std::make_pair(QVector3D(x, it.first.y(), it.first.x()),
|
|
||||||
it.second));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
optimizeNodes();
|
|
||||||
nodesToSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContourToPartConverter::nodesToSnapshot()
|
|
||||||
{
|
|
||||||
if (m_nodes.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto partId = QUuid::createUuid();
|
|
||||||
auto partIdString = partId.toString();
|
|
||||||
std::map<QString, QString> snapshotPart;
|
|
||||||
snapshotPart["id"] = partIdString;
|
|
||||||
snapshotPart["subdived"] = "true";
|
|
||||||
snapshotPart["rounded"] = "true";
|
|
||||||
snapshotPart["base"] = "YZ";
|
|
||||||
m_snapshot.parts[partIdString] = snapshotPart;
|
|
||||||
|
|
||||||
auto componentId = QUuid::createUuid();
|
|
||||||
auto componentIdString = componentId.toString();
|
|
||||||
std::map<QString, QString> snapshotComponent;
|
|
||||||
snapshotComponent["id"] = componentIdString;
|
|
||||||
snapshotComponent["combineMode"] = "Normal";
|
|
||||||
snapshotComponent["linkDataType"] = "partId";
|
|
||||||
snapshotComponent["linkData"] = partIdString;
|
|
||||||
m_snapshot.components[componentIdString] = snapshotComponent;
|
|
||||||
|
|
||||||
m_snapshot.rootComponent["children"] = componentIdString;
|
|
||||||
|
|
||||||
std::vector<QString> nodeIdStrings;
|
|
||||||
nodeIdStrings.reserve(m_nodes.size());
|
|
||||||
for (const auto &it: m_nodes) {
|
|
||||||
auto nodeId = QUuid::createUuid();
|
|
||||||
auto nodeIdString = nodeId.toString();
|
|
||||||
std::map<QString, QString> snapshotNode;
|
|
||||||
snapshotNode["id"] = nodeIdString;
|
|
||||||
snapshotNode["x"] = QString::number(it.first.x());
|
|
||||||
snapshotNode["y"] = QString::number(it.first.y());
|
|
||||||
snapshotNode["z"] = QString::number(it.first.z());
|
|
||||||
snapshotNode["radius"] = QString::number(it.second);
|
|
||||||
snapshotNode["partId"] = partIdString;
|
|
||||||
m_snapshot.nodes[nodeIdString] = snapshotNode;
|
|
||||||
nodeIdStrings.push_back(nodeIdString);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 1; i < nodeIdStrings.size(); ++i) {
|
|
||||||
size_t h = i - 1;
|
|
||||||
auto edgeId = QUuid::createUuid();
|
|
||||||
auto edgeIdString = edgeId.toString();
|
|
||||||
std::map<QString, QString> snapshotEdge;
|
|
||||||
snapshotEdge["id"] = edgeIdString;
|
|
||||||
snapshotEdge["from"] = nodeIdStrings[h];
|
|
||||||
snapshotEdge["to"] = nodeIdStrings[i];
|
|
||||||
snapshotEdge["partId"] = partIdString;
|
|
||||||
m_snapshot.edges[edgeIdString] = snapshotEdge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
#ifndef DUST3D_CONTOUR_TO_PART_CONVERTER_H
|
|
||||||
#define DUST3D_CONTOUR_TO_PART_CONVERTER_H
|
|
||||||
#include <QObject>
|
|
||||||
#include <QPolygonF>
|
|
||||||
#include <QVector3D>
|
|
||||||
#include <QVector2D>
|
|
||||||
#include <set>
|
|
||||||
#include "snapshot.h"
|
|
||||||
|
|
||||||
class ContourToPartConverter : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ContourToPartConverter(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize);
|
|
||||||
const Snapshot &getSnapshot();
|
|
||||||
signals:
|
|
||||||
void finished();
|
|
||||||
public slots:
|
|
||||||
void process();
|
|
||||||
private:
|
|
||||||
QPolygonF m_mainProfile;
|
|
||||||
QPolygonF m_sideProfile;
|
|
||||||
QSizeF m_canvasSize;
|
|
||||||
Snapshot m_snapshot;
|
|
||||||
static const float m_targetImageHeight;
|
|
||||||
static const float m_minEdgeLength;
|
|
||||||
static const float m_radiusEpsilon;
|
|
||||||
std::vector<std::pair<QVector3D, float>> m_nodes;
|
|
||||||
void convert();
|
|
||||||
void extractSkeleton(const QPolygonF &polygon,
|
|
||||||
std::vector<std::pair<QVector2D, float>> *skeleton);
|
|
||||||
int calculateNodeRadius(const QVector3D &node,
|
|
||||||
const QVector3D &direction,
|
|
||||||
const std::set<std::pair<int, int>> &black);
|
|
||||||
void nodesToSnapshot();
|
|
||||||
void smoothRadius(std::vector<std::pair<QVector2D, float>> *skeleton);
|
|
||||||
void alignSkeleton(const std::vector<std::pair<QVector2D, float>> &referenceSkeleton,
|
|
||||||
std::vector<std::pair<QVector2D, float>> &adjustSkeleton);
|
|
||||||
void optimizeNodes();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "skeletonside.h"
|
#include "skeletonside.h"
|
||||||
#include "scriptrunner.h"
|
#include "scriptrunner.h"
|
||||||
#include "imageforever.h"
|
#include "imageforever.h"
|
||||||
#include "contourtopartconverter.h"
|
|
||||||
#include "meshgenerator.h"
|
#include "meshgenerator.h"
|
||||||
|
|
||||||
unsigned long Document::m_maxSnapshot = 1000;
|
unsigned long Document::m_maxSnapshot = 1000;
|
||||||
|
@ -391,28 +390,6 @@ void Document::addNode(float x, float y, float z, float radius, QUuid fromNodeId
|
||||||
createNode(QUuid::createUuid(), x, y, z, radius, fromNodeId);
|
createNode(QUuid::createUuid(), x, y, z, radius, fromNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Document::addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize)
|
|
||||||
{
|
|
||||||
if (mainProfile.empty() || sideProfile.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QThread *thread = new QThread;
|
|
||||||
ContourToPartConverter *contourToPartConverter = new ContourToPartConverter(mainProfile, sideProfile, canvasSize);
|
|
||||||
contourToPartConverter->moveToThread(thread);
|
|
||||||
connect(thread, &QThread::started, contourToPartConverter, &ContourToPartConverter::process);
|
|
||||||
connect(contourToPartConverter, &ContourToPartConverter::finished, this, [=]() {
|
|
||||||
const auto &snapshot = contourToPartConverter->getSnapshot();
|
|
||||||
if (!snapshot.nodes.empty()) {
|
|
||||||
addFromSnapshot(snapshot, SnapshotSource::Paste);
|
|
||||||
saveSnapshot();
|
|
||||||
}
|
|
||||||
delete contourToPartConverter;
|
|
||||||
});
|
|
||||||
connect(contourToPartConverter, &ContourToPartConverter::finished, thread, &QThread::quit);
|
|
||||||
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
|
||||||
thread->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Document::addNodeWithId(QUuid nodeId, float x, float y, float z, float radius, QUuid fromNodeId)
|
void Document::addNodeWithId(QUuid nodeId, float x, float y, float z, float radius, QUuid fromNodeId)
|
||||||
{
|
{
|
||||||
createNode(nodeId, x, y, z, radius, fromNodeId);
|
createNode(nodeId, x, y, z, radius, fromNodeId);
|
||||||
|
|
|
@ -506,7 +506,6 @@ public slots:
|
||||||
void removeNode(QUuid nodeId);
|
void removeNode(QUuid nodeId);
|
||||||
void removeEdge(QUuid edgeId);
|
void removeEdge(QUuid edgeId);
|
||||||
void removePart(QUuid partId);
|
void removePart(QUuid partId);
|
||||||
void addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize);
|
|
||||||
void addNodeWithId(QUuid nodeId, float x, float y, float z, float radius, QUuid fromNodeId);
|
void addNodeWithId(QUuid nodeId, float x, float y, float z, float radius, QUuid fromNodeId);
|
||||||
void addNode(float x, float y, float z, float radius, QUuid fromNodeId);
|
void addNode(float x, float y, float z, float radius, QUuid fromNodeId);
|
||||||
void scaleNodeByAddRadius(QUuid nodeId, float amount);
|
void scaleNodeByAddRadius(QUuid nodeId, float amount);
|
||||||
|
|
|
@ -941,7 +941,6 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartXmirrorState, m_document, &Document::setPartXmirrorState);
|
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartXmirrorState, m_document, &Document::setPartXmirrorState);
|
||||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState);
|
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState);
|
||||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartWrapState, m_document, &Document::setPartCutRotation);
|
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartWrapState, m_document, &Document::setPartCutRotation);
|
||||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::addPartByPolygons, m_document, &Document::addPartByPolygons);
|
|
||||||
|
|
||||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setXlockState, m_document, &Document::setXlockState);
|
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setXlockState, m_document, &Document::setXlockState);
|
||||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setYlockState, m_document, &Document::setYlockState);
|
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setYlockState, m_document, &Document::setYlockState);
|
||||||
|
|
|
@ -1,260 +0,0 @@
|
||||||
#include <QDebug>
|
|
||||||
#include <queue>
|
|
||||||
#include <set>
|
|
||||||
#include "imageskeletonextractor.h"
|
|
||||||
|
|
||||||
// This is an implementation of the following paper:
|
|
||||||
// <A Fast Parallel Algorithm for Thinning Digital Patterns>
|
|
||||||
// T. Y. ZHANG and C. Y. SUEN
|
|
||||||
|
|
||||||
ImageSkeletonExtractor::~ImageSkeletonExtractor()
|
|
||||||
{
|
|
||||||
delete m_image;
|
|
||||||
delete m_grayscaleImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageSkeletonExtractor::setImage(QImage *image)
|
|
||||||
{
|
|
||||||
delete m_image;
|
|
||||||
m_image = image;
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage *ImageSkeletonExtractor::takeResultGrayscaleImage()
|
|
||||||
{
|
|
||||||
QImage *resultImage = m_grayscaleImage;
|
|
||||||
m_grayscaleImage = nullptr;
|
|
||||||
return resultImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ImageSkeletonExtractor::firstSubiterationSatisfied(int i, int j)
|
|
||||||
{
|
|
||||||
if (!isBlack(i, j))
|
|
||||||
return false;
|
|
||||||
auto blackNeighbors = countBlackNeighbors(i, j);
|
|
||||||
if (blackNeighbors < 2 || blackNeighbors > 6)
|
|
||||||
return false;
|
|
||||||
auto neighborTransitions = countNeighborTransitions(i, j);
|
|
||||||
if (1 != neighborTransitions)
|
|
||||||
return false;
|
|
||||||
if (isBlack(i + neighborOffsets[P2].first, j + neighborOffsets[P2].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P4].first, j + neighborOffsets[P4].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P6].first, j + neighborOffsets[P6].second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isBlack(i + neighborOffsets[P4].first, j + neighborOffsets[P4].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P6].first, j + neighborOffsets[P6].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P8].first, j + neighborOffsets[P8].second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ImageSkeletonExtractor::secondSubiterationSatisfied(int i, int j)
|
|
||||||
{
|
|
||||||
if (!isBlack(i, j))
|
|
||||||
return false;
|
|
||||||
auto blackNeighbors = countBlackNeighbors(i, j);
|
|
||||||
if (blackNeighbors < 2 || blackNeighbors > 6)
|
|
||||||
return false;
|
|
||||||
auto neighborTransitions = countNeighborTransitions(i, j);
|
|
||||||
if (1 != neighborTransitions)
|
|
||||||
return false;
|
|
||||||
if (isBlack(i + neighborOffsets[P2].first, j + neighborOffsets[P2].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P4].first, j + neighborOffsets[P4].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P8].first, j + neighborOffsets[P8].second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isBlack(i + neighborOffsets[P2].first, j + neighborOffsets[P2].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P6].first, j + neighborOffsets[P6].second) &&
|
|
||||||
isBlack(i + neighborOffsets[P8].first, j + neighborOffsets[P8].second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageSkeletonExtractor::calculateAreaAndBlackPixels()
|
|
||||||
{
|
|
||||||
m_area = 0;
|
|
||||||
m_blackPixels.clear();
|
|
||||||
for (int i = 1; i < (int)m_grayscaleImage->width() - 1; ++i) {
|
|
||||||
for (int j = 1; j < (int)m_grayscaleImage->height() - 1; ++j) {
|
|
||||||
if (isBlack(i, j)) {
|
|
||||||
++m_area;
|
|
||||||
m_blackPixels.insert({i, j});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::set<std::pair<int, int>> &ImageSkeletonExtractor::getBlackPixels()
|
|
||||||
{
|
|
||||||
return m_blackPixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ImageSkeletonExtractor::getArea()
|
|
||||||
{
|
|
||||||
return m_area;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageSkeletonExtractor::extract()
|
|
||||||
{
|
|
||||||
m_grayscaleImage = new QImage(m_image->convertToFormat(QImage::Format_Grayscale8));
|
|
||||||
calculateAreaAndBlackPixels();
|
|
||||||
while (true) {
|
|
||||||
std::vector<std::pair<int, int>> firstSatisfied;
|
|
||||||
for (int i = 1; i < (int)m_grayscaleImage->width() - 1; ++i) {
|
|
||||||
for (int j = 1; j < (int)m_grayscaleImage->height() - 1; ++j) {
|
|
||||||
if (firstSubiterationSatisfied(i, j))
|
|
||||||
firstSatisfied.push_back(std::make_pair(i, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto &it: firstSatisfied)
|
|
||||||
setWhite(it.first, it.second);
|
|
||||||
std::vector<std::pair<int, int>> secondSatisfied;
|
|
||||||
for (int i = 1; i < (int)m_grayscaleImage->width() - 1; ++i) {
|
|
||||||
for (int j = 1; j < (int)m_grayscaleImage->height() - 1; ++j) {
|
|
||||||
if (secondSubiterationSatisfied(i, j))
|
|
||||||
secondSatisfied.push_back(std::make_pair(i, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto &it: secondSatisfied)
|
|
||||||
setWhite(it.first, it.second);
|
|
||||||
if (firstSatisfied.empty() && secondSatisfied.empty())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageSkeletonExtractor::getSkeleton(std::vector<std::pair<int, int>> *skeleton)
|
|
||||||
{
|
|
||||||
if (nullptr == m_grayscaleImage)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::map<std::pair<int, int>, std::vector<std::pair<int, int>>> links;
|
|
||||||
for (int i = 1; i < (int)m_grayscaleImage->width() - 1; ++i) {
|
|
||||||
for (int j = 1; j < (int)m_grayscaleImage->height() - 1; ++j) {
|
|
||||||
if (!isBlack(i, j))
|
|
||||||
continue;
|
|
||||||
auto ij = std::make_pair(i, j);
|
|
||||||
auto p2 = std::make_pair(i + neighborOffsets[P2].first, j + neighborOffsets[P2].second);
|
|
||||||
bool hasP3 = true;
|
|
||||||
bool hasP5 = true;
|
|
||||||
bool hasP7 = true;
|
|
||||||
bool hasP9 = true;
|
|
||||||
if (isBlack(p2.first, p2.second)) {
|
|
||||||
links[ij].push_back(p2);
|
|
||||||
hasP3 = false;
|
|
||||||
hasP9 = false;
|
|
||||||
}
|
|
||||||
auto p4 = std::make_pair(i + neighborOffsets[P4].first, j + neighborOffsets[P4].second);
|
|
||||||
if (isBlack(p4.first, p4.second)) {
|
|
||||||
links[ij].push_back(p4);
|
|
||||||
hasP3 = false;
|
|
||||||
hasP5 = false;
|
|
||||||
}
|
|
||||||
auto p6 = std::make_pair(i + neighborOffsets[P6].first, j + neighborOffsets[P6].second);
|
|
||||||
if (isBlack(p6.first, p6.second)) {
|
|
||||||
links[ij].push_back(p6);
|
|
||||||
hasP5 = false;
|
|
||||||
hasP7 = false;
|
|
||||||
}
|
|
||||||
auto p8 = std::make_pair(i + neighborOffsets[P8].first, j + neighborOffsets[P8].second);
|
|
||||||
if (isBlack(p8.first, p8.second)) {
|
|
||||||
links[ij].push_back(p8);
|
|
||||||
hasP7 = false;
|
|
||||||
hasP9 = false;
|
|
||||||
}
|
|
||||||
if (hasP3) {
|
|
||||||
auto p3 = std::make_pair(i + neighborOffsets[P3].first, j + neighborOffsets[P3].second);
|
|
||||||
if (isBlack(p3.first, p3.second)) {
|
|
||||||
links[ij].push_back(p3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasP5) {
|
|
||||||
auto p5 = std::make_pair(i + neighborOffsets[P5].first, j + neighborOffsets[P5].second);
|
|
||||||
if (isBlack(p5.first, p5.second)) {
|
|
||||||
links[ij].push_back(p5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasP7) {
|
|
||||||
auto p7 = std::make_pair(i + neighborOffsets[P7].first, j + neighborOffsets[P7].second);
|
|
||||||
if (isBlack(p7.first, p7.second)) {
|
|
||||||
links[ij].push_back(p7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasP9) {
|
|
||||||
auto p9 = std::make_pair(i + neighborOffsets[P9].first, j + neighborOffsets[P9].second);
|
|
||||||
if (isBlack(p9.first, p9.second)) {
|
|
||||||
links[ij].push_back(p9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto calculateRouteLength = [&](const std::pair<int, int> &branch, const std::pair<int, int> &start) {
|
|
||||||
std::set<std::pair<int, int>> visited;
|
|
||||||
visited.insert(branch);
|
|
||||||
std::queue<std::pair<int, int>> waitPoints;
|
|
||||||
waitPoints.push(start);
|
|
||||||
size_t addLength = 0;
|
|
||||||
while (!waitPoints.empty()) {
|
|
||||||
auto point = waitPoints.front();
|
|
||||||
waitPoints.pop();
|
|
||||||
if (visited.find(point) != visited.end())
|
|
||||||
continue;
|
|
||||||
visited.insert(point);
|
|
||||||
auto findLink = links.find(point);
|
|
||||||
if (findLink == links.end())
|
|
||||||
break;
|
|
||||||
if (findLink->second.size() > 2) {
|
|
||||||
addLength = links.size(); // This will make sure the branch node is not been removed
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (const auto &it: findLink->second) {
|
|
||||||
if (visited.find(it) != visited.end())
|
|
||||||
continue;
|
|
||||||
waitPoints.push(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return visited.size() + addLength;
|
|
||||||
};
|
|
||||||
for (auto &it: links) {
|
|
||||||
if (it.second.size() > 2) {
|
|
||||||
std::vector<std::pair<std::pair<int, int>, size_t>> routes;
|
|
||||||
routes.reserve(it.second.size());
|
|
||||||
for (size_t i = 0; i < it.second.size(); ++i) {
|
|
||||||
routes.push_back(std::make_pair(it.second[i], calculateRouteLength(it.first, it.second[i])));
|
|
||||||
}
|
|
||||||
std::sort(routes.begin(), routes.end(), [](const std::pair<std::pair<int, int>, size_t> &first,
|
|
||||||
const std::pair<std::pair<int, int>, size_t> &second) {
|
|
||||||
return first.second < second.second;
|
|
||||||
});
|
|
||||||
it.second = std::vector<std::pair<int, int>> {routes[routes.size() - 2].first,
|
|
||||||
routes[routes.size() - 1].first};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::queue<std::pair<int, int>> waitPoints;
|
|
||||||
for (const auto &it: links) {
|
|
||||||
if (1 == it.second.size()) {
|
|
||||||
waitPoints.push(it.first);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::set<std::pair<int, int>> visited;
|
|
||||||
while (!waitPoints.empty()) {
|
|
||||||
auto point = waitPoints.front();
|
|
||||||
waitPoints.pop();
|
|
||||||
if (visited.find(point) != visited.end())
|
|
||||||
continue;
|
|
||||||
visited.insert(point);
|
|
||||||
skeleton->push_back(point);
|
|
||||||
auto findLink = links.find(point);
|
|
||||||
if (findLink == links.end())
|
|
||||||
break;
|
|
||||||
for (const auto &it: findLink->second) {
|
|
||||||
if (visited.find(it) != visited.end())
|
|
||||||
continue;
|
|
||||||
waitPoints.push(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
#ifndef DUST3D_IMAGE_SKELETON_EXTRACTOR_H
|
|
||||||
#define DUST3D_IMAGE_SKELETON_EXTRACTOR_H
|
|
||||||
#include <QImage>
|
|
||||||
#include <QObject>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
class ImageSkeletonExtractor : QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
const std::vector<std::pair<int, int>> neighborOffsets = {
|
|
||||||
{ 0, -1},
|
|
||||||
{ 1, -1},
|
|
||||||
{ 1, 0},
|
|
||||||
{ 1, 1},
|
|
||||||
{ 0, 1},
|
|
||||||
{-1, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{-1, -1},
|
|
||||||
};
|
|
||||||
enum {
|
|
||||||
P2 = 0,
|
|
||||||
P3,
|
|
||||||
P4,
|
|
||||||
P5,
|
|
||||||
P6,
|
|
||||||
P7,
|
|
||||||
P8,
|
|
||||||
P9
|
|
||||||
};
|
|
||||||
|
|
||||||
~ImageSkeletonExtractor();
|
|
||||||
void setImage(QImage *image);
|
|
||||||
void extract();
|
|
||||||
QImage *takeResultGrayscaleImage();
|
|
||||||
void getSkeleton(std::vector<std::pair<int, int>> *skeleton);
|
|
||||||
int getArea();
|
|
||||||
const std::set<std::pair<int, int>> &getBlackPixels();
|
|
||||||
private:
|
|
||||||
QImage *m_image = nullptr;
|
|
||||||
QImage *m_grayscaleImage = nullptr;
|
|
||||||
int m_area = 0;
|
|
||||||
std::set<std::pair<int, int>> m_blackPixels;
|
|
||||||
|
|
||||||
bool isBlack(int i, int j)
|
|
||||||
{
|
|
||||||
return QColor(m_grayscaleImage->pixel(i, j)).black() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWhite(int i, int j)
|
|
||||||
{
|
|
||||||
return !isBlack(i, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setWhite(int i, int j)
|
|
||||||
{
|
|
||||||
m_grayscaleImage->setPixel(i, j, qRgb(255, 255, 255));
|
|
||||||
}
|
|
||||||
|
|
||||||
int countNeighborTransitions(int i, int j)
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
for (size_t m = 0; m < neighborOffsets.size(); ++m) {
|
|
||||||
size_t n = (m + 1) % neighborOffsets.size();
|
|
||||||
if (isWhite(i + neighborOffsets[m].first, j + neighborOffsets[m].second) &&
|
|
||||||
isBlack(i + neighborOffsets[n].first, j + neighborOffsets[n].second)) {
|
|
||||||
++num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
int countBlackNeighbors(int i, int j)
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
for (const auto &it: neighborOffsets) {
|
|
||||||
if (isBlack(i + it.first, j + it.second))
|
|
||||||
++num;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool firstSubiterationSatisfied(int i, int j);
|
|
||||||
bool secondSubiterationSatisfied(int i, int j);
|
|
||||||
void calculateAreaAndBlackPixels();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -429,7 +429,6 @@ enum class SkeletonDocumentEditMode
|
||||||
{
|
{
|
||||||
Add = 0,
|
Add = 0,
|
||||||
Select,
|
Select,
|
||||||
Mark,
|
|
||||||
Paint,
|
Paint,
|
||||||
Drag,
|
Drag,
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
|
|
|
@ -32,9 +32,7 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
|
||||||
m_lastAddedY(false),
|
m_lastAddedY(false),
|
||||||
m_lastAddedZ(false),
|
m_lastAddedZ(false),
|
||||||
m_selectionItem(nullptr),
|
m_selectionItem(nullptr),
|
||||||
m_markerItem(nullptr),
|
|
||||||
m_rangeSelectionStarted(false),
|
m_rangeSelectionStarted(false),
|
||||||
m_markerStarted(false),
|
|
||||||
m_mouseEventFromSelf(false),
|
m_mouseEventFromSelf(false),
|
||||||
m_moveHappened(false),
|
m_moveHappened(false),
|
||||||
m_lastRot(0),
|
m_lastRot(0),
|
||||||
|
@ -80,10 +78,6 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
|
||||||
m_selectionItem->hide();
|
m_selectionItem->hide();
|
||||||
scene()->addItem(m_selectionItem);
|
scene()->addItem(m_selectionItem);
|
||||||
|
|
||||||
m_markerItem = new SkeletonGraphicsMarkerItem();
|
|
||||||
m_markerItem->hide();
|
|
||||||
scene()->addItem(m_markerItem);
|
|
||||||
|
|
||||||
m_mainOriginItem = new SkeletonGraphicsOriginItem(SkeletonProfile::Main);
|
m_mainOriginItem = new SkeletonGraphicsOriginItem(SkeletonProfile::Main);
|
||||||
m_mainOriginItem->setRotated(m_rotated);
|
m_mainOriginItem->setRotated(m_rotated);
|
||||||
m_mainOriginItem->hide();
|
m_mainOriginItem->hide();
|
||||||
|
@ -140,7 +134,6 @@ void SkeletonGraphicsWidget::setBackgroundBlur(float turnaroundOpacity)
|
||||||
void SkeletonGraphicsWidget::shortcutEscape()
|
void SkeletonGraphicsWidget::shortcutEscape()
|
||||||
{
|
{
|
||||||
if (SkeletonDocumentEditMode::Add == m_document->editMode ||
|
if (SkeletonDocumentEditMode::Add == m_document->editMode ||
|
||||||
SkeletonDocumentEditMode::Mark == m_document->editMode ||
|
|
||||||
SkeletonDocumentEditMode::Paint == m_document->editMode) {
|
SkeletonDocumentEditMode::Paint == m_document->editMode) {
|
||||||
emit setEditMode(SkeletonDocumentEditMode::Select);
|
emit setEditMode(SkeletonDocumentEditMode::Select);
|
||||||
return;
|
return;
|
||||||
|
@ -762,10 +755,6 @@ void SkeletonGraphicsWidget::updateCursor()
|
||||||
m_cursorNodeItem->hide();
|
m_cursorNodeItem->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SkeletonDocumentEditMode::Mark != m_document->editMode) {
|
|
||||||
m_markerItem->reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (m_document->editMode) {
|
switch (m_document->editMode) {
|
||||||
case SkeletonDocumentEditMode::Add:
|
case SkeletonDocumentEditMode::Add:
|
||||||
setCursor(QCursor(Theme::awesome()->icon(fa::plus).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
setCursor(QCursor(Theme::awesome()->icon(fa::plus).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
@ -773,13 +762,6 @@ void SkeletonGraphicsWidget::updateCursor()
|
||||||
case SkeletonDocumentEditMode::Select:
|
case SkeletonDocumentEditMode::Select:
|
||||||
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize / 5, 0));
|
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize / 5, 0));
|
||||||
break;
|
break;
|
||||||
case SkeletonDocumentEditMode::Mark: {
|
|
||||||
auto pixmap = Theme::awesome()->icon(fa::pencil).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize);
|
|
||||||
QPixmap replacedPixmap(pixmap.size());
|
|
||||||
replacedPixmap.fill(m_markerItem->color());
|
|
||||||
replacedPixmap.setMask(pixmap.createMaskFromColor(Qt::transparent));
|
|
||||||
setCursor(QCursor(replacedPixmap, Theme::toolIconFontSize / 5, Theme::toolIconFontSize * 4 / 5));
|
|
||||||
} break;
|
|
||||||
case SkeletonDocumentEditMode::Paint:
|
case SkeletonDocumentEditMode::Paint:
|
||||||
setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize * 0.2, Theme::toolIconFontSize * 0.8));
|
setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize * 0.2, Theme::toolIconFontSize * 0.8));
|
||||||
break;
|
break;
|
||||||
|
@ -912,16 +894,6 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
|
||||||
if (m_markerStarted) {
|
|
||||||
QPointF mouseScenePos = mouseEventScenePos(event);
|
|
||||||
m_markerItem->addPoint(mouseScenePos);
|
|
||||||
if (!m_markerItem->isVisible())
|
|
||||||
m_markerItem->setVisible(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SkeletonDocumentEditMode::Select == m_document->editMode ||
|
if (SkeletonDocumentEditMode::Select == m_document->editMode ||
|
||||||
SkeletonDocumentEditMode::Add == m_document->editMode) {
|
SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||||
|
|
||||||
|
@ -1461,7 +1433,7 @@ void SkeletonGraphicsWidget::rotateAllMainProfileCounterclockwise90DegreeAlongOr
|
||||||
bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
bool processed = m_dragStarted || m_moveStarted || m_rangeSelectionStarted || m_markerStarted;
|
bool processed = m_dragStarted || m_moveStarted || m_rangeSelectionStarted;
|
||||||
if (m_dragStarted) {
|
if (m_dragStarted) {
|
||||||
m_dragStarted = false;
|
m_dragStarted = false;
|
||||||
updateCursor();
|
updateCursor();
|
||||||
|
@ -1476,28 +1448,6 @@ bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
||||||
m_selectionItem->hide();
|
m_selectionItem->hide();
|
||||||
m_rangeSelectionStarted = false;
|
m_rangeSelectionStarted = false;
|
||||||
}
|
}
|
||||||
if (m_markerStarted) {
|
|
||||||
auto boundingBox = m_markerItem->polygon().boundingRect();
|
|
||||||
if (boundingBox.width() * boundingBox.height() > 4) {
|
|
||||||
const QPolygonF &previousPolygon = m_markerItem->previousPolygon();
|
|
||||||
if (previousPolygon.empty()) {
|
|
||||||
m_markerItem->save();
|
|
||||||
m_markerItem->toggleProfile();
|
|
||||||
} else {
|
|
||||||
if (m_markerItem->isMainProfile()) {
|
|
||||||
emit addPartByPolygons(m_markerItem->polygon(), previousPolygon, sceneRect().size());
|
|
||||||
} else {
|
|
||||||
emit addPartByPolygons(previousPolygon, m_markerItem->polygon(), sceneRect().size());
|
|
||||||
}
|
|
||||||
m_markerItem->reset();
|
|
||||||
}
|
|
||||||
m_markerItem->hide();
|
|
||||||
updateCursor();
|
|
||||||
} else {
|
|
||||||
m_markerItem->clear();
|
|
||||||
}
|
|
||||||
m_markerStarted = false;
|
|
||||||
}
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1641,11 +1591,6 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
|
||||||
m_rangeSelectionStartPos = mouseEventScenePos(event);
|
m_rangeSelectionStartPos = mouseEventScenePos(event);
|
||||||
m_rangeSelectionStarted = true;
|
m_rangeSelectionStarted = true;
|
||||||
}
|
}
|
||||||
} else if (SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
|
||||||
if (!m_markerStarted) {
|
|
||||||
m_markerItem->addPoint(mouseEventScenePos(event));
|
|
||||||
m_markerStarted = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1817,11 +1762,6 @@ void SkeletonGraphicsWidget::shortcutAddMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//void SkeletonGraphicsWidget::shortcutMarkMode()
|
|
||||||
//{
|
|
||||||
// emit setEditMode(SkeletonDocumentEditMode::Mark);
|
|
||||||
//}
|
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutUndo()
|
void SkeletonGraphicsWidget::shortcutUndo()
|
||||||
{
|
{
|
||||||
emit undo();
|
emit undo();
|
||||||
|
@ -1908,7 +1848,7 @@ void SkeletonGraphicsWidget::shortcutZoomSelectedBy1()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutRotateSelectedByMinus1()
|
void SkeletonGraphicsWidget::shortcutRotateSelectedByMinus1()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
hasSelection()) {
|
hasSelection()) {
|
||||||
rotateSelected(-1);
|
rotateSelected(-1);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1917,7 +1857,7 @@ void SkeletonGraphicsWidget::shortcutRotateSelectedByMinus1()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutRotateSelectedBy1()
|
void SkeletonGraphicsWidget::shortcutRotateSelectedBy1()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
hasSelection()) {
|
hasSelection()) {
|
||||||
rotateSelected(1);
|
rotateSelected(1);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1926,7 +1866,7 @@ void SkeletonGraphicsWidget::shortcutRotateSelectedBy1()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToLeft()
|
void SkeletonGraphicsWidget::shortcutMoveSelectedToLeft()
|
||||||
{
|
{
|
||||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
if (m_checkedOriginItem) {
|
if (m_checkedOriginItem) {
|
||||||
moveCheckedOrigin(-1, 0);
|
moveCheckedOrigin(-1, 0);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1939,7 +1879,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToLeft()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToRight()
|
void SkeletonGraphicsWidget::shortcutMoveSelectedToRight()
|
||||||
{
|
{
|
||||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
if (m_checkedOriginItem) {
|
if (m_checkedOriginItem) {
|
||||||
moveCheckedOrigin(1, 0);
|
moveCheckedOrigin(1, 0);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1952,7 +1892,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToRight()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToUp()
|
void SkeletonGraphicsWidget::shortcutMoveSelectedToUp()
|
||||||
{
|
{
|
||||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
if (m_checkedOriginItem) {
|
if (m_checkedOriginItem) {
|
||||||
moveCheckedOrigin(0, -1);
|
moveCheckedOrigin(0, -1);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1965,7 +1905,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToUp()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToDown()
|
void SkeletonGraphicsWidget::shortcutMoveSelectedToDown()
|
||||||
{
|
{
|
||||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
if (m_checkedOriginItem) {
|
if (m_checkedOriginItem) {
|
||||||
moveCheckedOrigin(0, 1);
|
moveCheckedOrigin(0, 1);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1978,7 +1918,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToDown()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutScaleSelectedByMinus1()
|
void SkeletonGraphicsWidget::shortcutScaleSelectedByMinus1()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
hasSelection()) {
|
hasSelection()) {
|
||||||
scaleSelected(-1);
|
scaleSelected(-1);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1987,7 +1927,7 @@ void SkeletonGraphicsWidget::shortcutScaleSelectedByMinus1()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutScaleSelectedBy1()
|
void SkeletonGraphicsWidget::shortcutScaleSelectedBy1()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
hasSelection()) {
|
hasSelection()) {
|
||||||
scaleSelected(1);
|
scaleSelected(1);
|
||||||
emit groupOperationAdded();
|
emit groupOperationAdded();
|
||||||
|
@ -1996,7 +1936,7 @@ void SkeletonGraphicsWidget::shortcutScaleSelectedBy1()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutSwitchProfileOnSelected()
|
void SkeletonGraphicsWidget::shortcutSwitchProfileOnSelected()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
hasSelection()) {
|
hasSelection()) {
|
||||||
switchProfileOnRangeSelection();
|
switchProfileOnRangeSelection();
|
||||||
}
|
}
|
||||||
|
@ -2018,7 +1958,7 @@ void SkeletonGraphicsWidget::shortcutShowOrHideSelectedPart()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutEnableOrDisableSelectedPart()
|
void SkeletonGraphicsWidget::shortcutEnableOrDisableSelectedPart()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
!m_lastCheckedPart.isNull()) {
|
!m_lastCheckedPart.isNull()) {
|
||||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||||
bool partDisabled = part && part->disabled;
|
bool partDisabled = part && part->disabled;
|
||||||
|
@ -2029,7 +1969,7 @@ void SkeletonGraphicsWidget::shortcutEnableOrDisableSelectedPart()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutLockOrUnlockSelectedPart()
|
void SkeletonGraphicsWidget::shortcutLockOrUnlockSelectedPart()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
!m_lastCheckedPart.isNull()) {
|
!m_lastCheckedPart.isNull()) {
|
||||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||||
bool partLocked = part && part->locked;
|
bool partLocked = part && part->locked;
|
||||||
|
@ -2040,7 +1980,7 @@ void SkeletonGraphicsWidget::shortcutLockOrUnlockSelectedPart()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutXmirrorOnOrOffSelectedPart()
|
void SkeletonGraphicsWidget::shortcutXmirrorOnOrOffSelectedPart()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
!m_lastCheckedPart.isNull()) {
|
!m_lastCheckedPart.isNull()) {
|
||||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||||
bool partXmirrored = part && part->xMirrored;
|
bool partXmirrored = part && part->xMirrored;
|
||||||
|
@ -2051,7 +1991,7 @@ void SkeletonGraphicsWidget::shortcutXmirrorOnOrOffSelectedPart()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutSubdivedOrNotSelectedPart()
|
void SkeletonGraphicsWidget::shortcutSubdivedOrNotSelectedPart()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
!m_lastCheckedPart.isNull()) {
|
!m_lastCheckedPart.isNull()) {
|
||||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||||
bool partSubdived = part && part->subdived;
|
bool partSubdived = part && part->subdived;
|
||||||
|
@ -2062,7 +2002,7 @@ void SkeletonGraphicsWidget::shortcutSubdivedOrNotSelectedPart()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutChamferedOrNotSelectedPart()
|
void SkeletonGraphicsWidget::shortcutChamferedOrNotSelectedPart()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
!m_lastCheckedPart.isNull()) {
|
!m_lastCheckedPart.isNull()) {
|
||||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||||
bool partChamfered = part && part->chamfered;
|
bool partChamfered = part && part->chamfered;
|
||||||
|
@ -2078,7 +2018,7 @@ void SkeletonGraphicsWidget::shortcutSelectAll()
|
||||||
|
|
||||||
void SkeletonGraphicsWidget::shortcutRoundEndOrNotSelectedPart()
|
void SkeletonGraphicsWidget::shortcutRoundEndOrNotSelectedPart()
|
||||||
{
|
{
|
||||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||||
!m_lastCheckedPart.isNull()) {
|
!m_lastCheckedPart.isNull()) {
|
||||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||||
bool partRounded = part && part->rounded;
|
bool partRounded = part && part->rounded;
|
||||||
|
|
|
@ -124,72 +124,6 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SkeletonGraphicsMarkerItem : public QGraphicsPolygonItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SkeletonGraphicsMarkerItem()
|
|
||||||
{
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
void addPoint(const QPointF &point)
|
|
||||||
{
|
|
||||||
m_polygon.append(point);
|
|
||||||
setPolygon(m_polygon);
|
|
||||||
}
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
m_polygon.clear();
|
|
||||||
setPolygon(m_polygon);
|
|
||||||
}
|
|
||||||
QColor color()
|
|
||||||
{
|
|
||||||
return m_mainProfile ? Theme::red : Theme::green;
|
|
||||||
}
|
|
||||||
bool isMainProfile()
|
|
||||||
{
|
|
||||||
return m_mainProfile;
|
|
||||||
}
|
|
||||||
void toggleProfile()
|
|
||||||
{
|
|
||||||
m_mainProfile = !m_mainProfile;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
void save()
|
|
||||||
{
|
|
||||||
m_previousPolygon = m_polygon;
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
const QPolygonF &previousPolygon()
|
|
||||||
{
|
|
||||||
return m_previousPolygon;
|
|
||||||
}
|
|
||||||
const QPolygonF &polygon()
|
|
||||||
{
|
|
||||||
return m_polygon;
|
|
||||||
}
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
m_previousPolygon.clear();
|
|
||||||
clear();
|
|
||||||
if (!m_mainProfile) {
|
|
||||||
m_mainProfile = true;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
QPolygonF m_polygon;
|
|
||||||
QPolygonF m_previousPolygon;
|
|
||||||
bool m_mainProfile = true;
|
|
||||||
void updateAppearance()
|
|
||||||
{
|
|
||||||
QColor penColor(color());
|
|
||||||
QPen pen(penColor);
|
|
||||||
pen.setWidth(2);
|
|
||||||
pen.setStyle(Qt::SolidLine);
|
|
||||||
setPen(pen);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SkeletonGraphicsNodeItem : public QGraphicsEllipseItem
|
class SkeletonGraphicsNodeItem : public QGraphicsEllipseItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -513,8 +447,6 @@ signals:
|
||||||
void showOrHideAllComponents();
|
void showOrHideAllComponents();
|
||||||
void shortcutToggleFlatShading();
|
void shortcutToggleFlatShading();
|
||||||
void shortcutToggleRotation();
|
void shortcutToggleRotation();
|
||||||
void createGriddedPartsFromNodes(const std::set<QUuid> &nodeIds);
|
|
||||||
void addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize);
|
|
||||||
void loadedTurnaroundImageChanged();
|
void loadedTurnaroundImageChanged();
|
||||||
public:
|
public:
|
||||||
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
||||||
|
@ -692,9 +624,7 @@ private: //need initialize
|
||||||
float m_lastAddedY;
|
float m_lastAddedY;
|
||||||
float m_lastAddedZ;
|
float m_lastAddedZ;
|
||||||
SkeletonGraphicsSelectionItem *m_selectionItem;
|
SkeletonGraphicsSelectionItem *m_selectionItem;
|
||||||
SkeletonGraphicsMarkerItem *m_markerItem;
|
|
||||||
bool m_rangeSelectionStarted;
|
bool m_rangeSelectionStarted;
|
||||||
bool m_markerStarted;
|
|
||||||
bool m_mouseEventFromSelf;
|
bool m_mouseEventFromSelf;
|
||||||
bool m_moveHappened;
|
bool m_moveHappened;
|
||||||
int m_lastRot;
|
int m_lastRot;
|
||||||
|
|
Loading…
Reference in New Issue