diff --git a/dust3d.pro b/dust3d.pro index 0e3ddfe4..b3d99e27 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -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 diff --git a/src/contourtopartconverter.cpp b/src/contourtopartconverter.cpp deleted file mode 100644 index 9ddeb977..00000000 --- a/src/contourtopartconverter.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#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> *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> imageSkeleton; - int imageArea = imageSkeletonExtractor.getArea(); - imageSkeletonExtractor.getSkeleton(&imageSkeleton); - const std::set> &blackPixels = imageSkeletonExtractor.getBlackPixels(); - - std::vector> 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 selectedPositions; - selectedPositions.reserve(selectedNodes.size()); - for (const auto &it: selectedNodes) - selectedPositions.push_back(QVector3D(it.first, it.second, 0.0f)); - - std::vector 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 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> &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> *skeleton) -{ - if (skeleton->empty()) - return; - - std::vector 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> &referenceSkeleton, - std::vector> &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> sideSkeleton; - std::vector> 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 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 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 nodeIdStrings; - nodeIdStrings.reserve(m_nodes.size()); - for (const auto &it: m_nodes) { - auto nodeId = QUuid::createUuid(); - auto nodeIdString = nodeId.toString(); - std::map 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 snapshotEdge; - snapshotEdge["id"] = edgeIdString; - snapshotEdge["from"] = nodeIdStrings[h]; - snapshotEdge["to"] = nodeIdStrings[i]; - snapshotEdge["partId"] = partIdString; - m_snapshot.edges[edgeIdString] = snapshotEdge; - } -} - diff --git a/src/contourtopartconverter.h b/src/contourtopartconverter.h deleted file mode 100644 index 1dd02272..00000000 --- a/src/contourtopartconverter.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef DUST3D_CONTOUR_TO_PART_CONVERTER_H -#define DUST3D_CONTOUR_TO_PART_CONVERTER_H -#include -#include -#include -#include -#include -#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> m_nodes; - void convert(); - void extractSkeleton(const QPolygonF &polygon, - std::vector> *skeleton); - int calculateNodeRadius(const QVector3D &node, - const QVector3D &direction, - const std::set> &black); - void nodesToSnapshot(); - void smoothRadius(std::vector> *skeleton); - void alignSkeleton(const std::vector> &referenceSkeleton, - std::vector> &adjustSkeleton); - void optimizeNodes(); -}; - -#endif diff --git a/src/document.cpp b/src/document.cpp index 44e63f70..a74b30f5 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -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); diff --git a/src/document.h b/src/document.h index 4f9b1790..10809a8e 100644 --- a/src/document.h +++ b/src/document.h @@ -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); diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index 829619e8..7d322825 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -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); diff --git a/src/imageskeletonextractor.cpp b/src/imageskeletonextractor.cpp deleted file mode 100644 index 085988d9..00000000 --- a/src/imageskeletonextractor.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include -#include -#include -#include "imageskeletonextractor.h" - -// This is an implementation of the following paper: -// -// 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> &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> 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> 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> *skeleton) -{ - if (nullptr == m_grayscaleImage) - return; - - std::map, std::vector>> 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 &branch, const std::pair &start) { - std::set> visited; - visited.insert(branch); - std::queue> 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, 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, size_t> &first, - const std::pair, size_t> &second) { - return first.second < second.second; - }); - it.second = std::vector> {routes[routes.size() - 2].first, - routes[routes.size() - 1].first}; - } - } - - std::queue> waitPoints; - for (const auto &it: links) { - if (1 == it.second.size()) { - waitPoints.push(it.first); - break; - } - } - std::set> 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); - } - } -} diff --git a/src/imageskeletonextractor.h b/src/imageskeletonextractor.h deleted file mode 100644 index 2cc85e89..00000000 --- a/src/imageskeletonextractor.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef DUST3D_IMAGE_SKELETON_EXTRACTOR_H -#define DUST3D_IMAGE_SKELETON_EXTRACTOR_H -#include -#include -#include -#include - -class ImageSkeletonExtractor : QObject -{ - Q_OBJECT -public: - const std::vector> 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> *skeleton); - int getArea(); - const std::set> &getBlackPixels(); -private: - QImage *m_image = nullptr; - QImage *m_grayscaleImage = nullptr; - int m_area = 0; - std::set> 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 diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 071eade1..c30ceb77 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -429,7 +429,6 @@ enum class SkeletonDocumentEditMode { Add = 0, Select, - Mark, Paint, Drag, ZoomIn, diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index 15c13e8f..4b2f718a 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -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; diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index 8d158f76..3e43a2cc 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -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 &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;