Remove unused markpen code
parent
fac30fc6a4
commit
7817cde381
|
@ -439,12 +439,6 @@ HEADERS += src/triangulatefaces.h
|
|||
SOURCES += src/booleanmesh.cpp
|
||||
HEADERS += src/booleanmesh.h
|
||||
|
||||
SOURCES += src/imageskeletonextractor.cpp
|
||||
HEADERS += src/imageskeletonextractor.h
|
||||
|
||||
SOURCES += src/contourtopartconverter.cpp
|
||||
HEADERS += src/contourtopartconverter.h
|
||||
|
||||
SOURCES += src/remesher.cpp
|
||||
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 "scriptrunner.h"
|
||||
#include "imageforever.h"
|
||||
#include "contourtopartconverter.h"
|
||||
#include "meshgenerator.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
createNode(nodeId, x, y, z, radius, fromNodeId);
|
||||
|
|
|
@ -506,7 +506,6 @@ public slots:
|
|||
void removeNode(QUuid nodeId);
|
||||
void removeEdge(QUuid edgeId);
|
||||
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 addNode(float x, float y, float z, float radius, QUuid fromNodeId);
|
||||
void scaleNodeByAddRadius(QUuid nodeId, float amount);
|
||||
|
|
|
@ -941,7 +941,6 @@ DocumentWindow::DocumentWindow() :
|
|||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartXmirrorState, m_document, &Document::setPartXmirrorState);
|
||||
connect(shapeGraphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState);
|
||||
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::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,
|
||||
Select,
|
||||
Mark,
|
||||
Paint,
|
||||
Drag,
|
||||
ZoomIn,
|
||||
|
|
|
@ -32,9 +32,7 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
|
|||
m_lastAddedY(false),
|
||||
m_lastAddedZ(false),
|
||||
m_selectionItem(nullptr),
|
||||
m_markerItem(nullptr),
|
||||
m_rangeSelectionStarted(false),
|
||||
m_markerStarted(false),
|
||||
m_mouseEventFromSelf(false),
|
||||
m_moveHappened(false),
|
||||
m_lastRot(0),
|
||||
|
@ -80,10 +78,6 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
|
|||
m_selectionItem->hide();
|
||||
scene()->addItem(m_selectionItem);
|
||||
|
||||
m_markerItem = new SkeletonGraphicsMarkerItem();
|
||||
m_markerItem->hide();
|
||||
scene()->addItem(m_markerItem);
|
||||
|
||||
m_mainOriginItem = new SkeletonGraphicsOriginItem(SkeletonProfile::Main);
|
||||
m_mainOriginItem->setRotated(m_rotated);
|
||||
m_mainOriginItem->hide();
|
||||
|
@ -140,7 +134,6 @@ void SkeletonGraphicsWidget::setBackgroundBlur(float turnaroundOpacity)
|
|||
void SkeletonGraphicsWidget::shortcutEscape()
|
||||
{
|
||||
if (SkeletonDocumentEditMode::Add == m_document->editMode ||
|
||||
SkeletonDocumentEditMode::Mark == m_document->editMode ||
|
||||
SkeletonDocumentEditMode::Paint == m_document->editMode) {
|
||||
emit setEditMode(SkeletonDocumentEditMode::Select);
|
||||
return;
|
||||
|
@ -762,10 +755,6 @@ void SkeletonGraphicsWidget::updateCursor()
|
|||
m_cursorNodeItem->hide();
|
||||
}
|
||||
|
||||
if (SkeletonDocumentEditMode::Mark != m_document->editMode) {
|
||||
m_markerItem->reset();
|
||||
}
|
||||
|
||||
switch (m_document->editMode) {
|
||||
case SkeletonDocumentEditMode::Add:
|
||||
setCursor(QCursor(Theme::awesome()->icon(fa::plus).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||
|
@ -773,13 +762,6 @@ void SkeletonGraphicsWidget::updateCursor()
|
|||
case SkeletonDocumentEditMode::Select:
|
||||
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize / 5, 0));
|
||||
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:
|
||||
setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize * 0.2, Theme::toolIconFontSize * 0.8));
|
||||
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 ||
|
||||
SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||
|
||||
|
@ -1461,7 +1433,7 @@ void SkeletonGraphicsWidget::rotateAllMainProfileCounterclockwise90DegreeAlongOr
|
|||
bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
||||
{
|
||||
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) {
|
||||
m_dragStarted = false;
|
||||
updateCursor();
|
||||
|
@ -1476,28 +1448,6 @@ bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
|||
m_selectionItem->hide();
|
||||
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 false;
|
||||
|
@ -1641,11 +1591,6 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
|
|||
m_rangeSelectionStartPos = mouseEventScenePos(event);
|
||||
m_rangeSelectionStarted = true;
|
||||
}
|
||||
} else if (SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
||||
if (!m_markerStarted) {
|
||||
m_markerItem->addPoint(mouseEventScenePos(event));
|
||||
m_markerStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1817,11 +1762,6 @@ void SkeletonGraphicsWidget::shortcutAddMode()
|
|||
}
|
||||
}
|
||||
|
||||
//void SkeletonGraphicsWidget::shortcutMarkMode()
|
||||
//{
|
||||
// emit setEditMode(SkeletonDocumentEditMode::Mark);
|
||||
//}
|
||||
|
||||
void SkeletonGraphicsWidget::shortcutUndo()
|
||||
{
|
||||
emit undo();
|
||||
|
@ -1908,7 +1848,7 @@ void SkeletonGraphicsWidget::shortcutZoomSelectedBy1()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutRotateSelectedByMinus1()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
hasSelection()) {
|
||||
rotateSelected(-1);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1917,7 +1857,7 @@ void SkeletonGraphicsWidget::shortcutRotateSelectedByMinus1()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutRotateSelectedBy1()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
hasSelection()) {
|
||||
rotateSelected(1);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1926,7 +1866,7 @@ void SkeletonGraphicsWidget::shortcutRotateSelectedBy1()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToLeft()
|
||||
{
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||
if (m_checkedOriginItem) {
|
||||
moveCheckedOrigin(-1, 0);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1939,7 +1879,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToLeft()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToRight()
|
||||
{
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||
if (m_checkedOriginItem) {
|
||||
moveCheckedOrigin(1, 0);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1952,7 +1892,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToRight()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToUp()
|
||||
{
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||
if (m_checkedOriginItem) {
|
||||
moveCheckedOrigin(0, -1);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1965,7 +1905,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToUp()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutMoveSelectedToDown()
|
||||
{
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) {
|
||||
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||
if (m_checkedOriginItem) {
|
||||
moveCheckedOrigin(0, 1);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1978,7 +1918,7 @@ void SkeletonGraphicsWidget::shortcutMoveSelectedToDown()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutScaleSelectedByMinus1()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
hasSelection()) {
|
||||
scaleSelected(-1);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1987,7 +1927,7 @@ void SkeletonGraphicsWidget::shortcutScaleSelectedByMinus1()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutScaleSelectedBy1()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
hasSelection()) {
|
||||
scaleSelected(1);
|
||||
emit groupOperationAdded();
|
||||
|
@ -1996,7 +1936,7 @@ void SkeletonGraphicsWidget::shortcutScaleSelectedBy1()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutSwitchProfileOnSelected()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
hasSelection()) {
|
||||
switchProfileOnRangeSelection();
|
||||
}
|
||||
|
@ -2018,7 +1958,7 @@ void SkeletonGraphicsWidget::shortcutShowOrHideSelectedPart()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutEnableOrDisableSelectedPart()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
!m_lastCheckedPart.isNull()) {
|
||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||
bool partDisabled = part && part->disabled;
|
||||
|
@ -2029,7 +1969,7 @@ void SkeletonGraphicsWidget::shortcutEnableOrDisableSelectedPart()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutLockOrUnlockSelectedPart()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
!m_lastCheckedPart.isNull()) {
|
||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||
bool partLocked = part && part->locked;
|
||||
|
@ -2040,7 +1980,7 @@ void SkeletonGraphicsWidget::shortcutLockOrUnlockSelectedPart()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutXmirrorOnOrOffSelectedPart()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
!m_lastCheckedPart.isNull()) {
|
||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||
bool partXmirrored = part && part->xMirrored;
|
||||
|
@ -2051,7 +1991,7 @@ void SkeletonGraphicsWidget::shortcutXmirrorOnOrOffSelectedPart()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutSubdivedOrNotSelectedPart()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
!m_lastCheckedPart.isNull()) {
|
||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||
bool partSubdived = part && part->subdived;
|
||||
|
@ -2062,7 +2002,7 @@ void SkeletonGraphicsWidget::shortcutSubdivedOrNotSelectedPart()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutChamferedOrNotSelectedPart()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
!m_lastCheckedPart.isNull()) {
|
||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||
bool partChamfered = part && part->chamfered;
|
||||
|
@ -2078,7 +2018,7 @@ void SkeletonGraphicsWidget::shortcutSelectAll()
|
|||
|
||||
void SkeletonGraphicsWidget::shortcutRoundEndOrNotSelectedPart()
|
||||
{
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode || SkeletonDocumentEditMode::Mark == m_document->editMode) &&
|
||||
if ((SkeletonDocumentEditMode::Select == m_document->editMode) &&
|
||||
!m_lastCheckedPart.isNull()) {
|
||||
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
@ -513,8 +447,6 @@ signals:
|
|||
void showOrHideAllComponents();
|
||||
void shortcutToggleFlatShading();
|
||||
void shortcutToggleRotation();
|
||||
void createGriddedPartsFromNodes(const std::set<QUuid> &nodeIds);
|
||||
void addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize);
|
||||
void loadedTurnaroundImageChanged();
|
||||
public:
|
||||
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
||||
|
@ -692,9 +624,7 @@ private: //need initialize
|
|||
float m_lastAddedY;
|
||||
float m_lastAddedZ;
|
||||
SkeletonGraphicsSelectionItem *m_selectionItem;
|
||||
SkeletonGraphicsMarkerItem *m_markerItem;
|
||||
bool m_rangeSelectionStarted;
|
||||
bool m_markerStarted;
|
||||
bool m_mouseEventFromSelf;
|
||||
bool m_moveHappened;
|
||||
int m_lastRot;
|
||||
|
|
Loading…
Reference in New Issue