diff --git a/plugins/tools/selectiontools/KisMagneticWorker.cc b/plugins/tools/selectiontools/KisMagneticWorker.cc index f117ef3e86..384aa6e85a 100644 --- a/plugins/tools/selectiontools/KisMagneticWorker.cc +++ b/plugins/tools/selectiontools/KisMagneticWorker.cc @@ -1,239 +1,235 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisMagneticWorker.h" #include #include #include #include #include #include #include #include #include "KisMagneticGraph.h" struct DistanceMap { typedef VertexDescriptor key_type; typedef double data_type; typedef std::pair value_type; explicit DistanceMap(double const& dval) : m_default(dval) { } data_type &operator [] (key_type const& k) { if (m.find(k) == m.end()) m[k] = m_default; return m[k]; } private: std::map m; data_type const m_default; }; struct PredecessorMap { PredecessorMap() = default; PredecessorMap(PredecessorMap const& that) = default; typedef VertexDescriptor key_type; typedef VertexDescriptor value_type; typedef boost::read_write_property_map_tag category; VertexDescriptor &operator [] (VertexDescriptor v) { return m_map[v]; } std::map m_map; }; VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v) { auto found = m.m_map.find(v); return found != m.m_map.end() ? found->second : v; } void put(PredecessorMap &m, VertexDescriptor key, VertexDescriptor value) { m.m_map[key] = value; } double EuclideanDistance(VertexDescriptor p1, VertexDescriptor p2) { return std::sqrt(std::pow(p1.y - p2.y, 2) + std::pow(p1.x - p2.x, 2)); } class AStarHeuristic : public boost::astar_heuristic { private: VertexDescriptor m_goal; public: explicit AStarHeuristic(VertexDescriptor goal) : m_goal(goal) { } double operator () (VertexDescriptor v) { return EuclideanDistance(v, m_goal); } }; struct GoalFound { }; class AStarGoalVisitor : public boost::default_astar_visitor { public: explicit AStarGoalVisitor(VertexDescriptor goal) : m_goal(goal){ } void examine_vertex(VertexDescriptor u, KisMagneticGraph const &g) { Q_UNUSED(g) if (u == m_goal) { throw GoalFound(); } } private: VertexDescriptor m_goal; }; struct WeightMap { typedef std::pair key_type; typedef double data_type; typedef std::pair value_type; WeightMap() = default; explicit WeightMap(const KisMagneticGraph &g) : m_graph(g) { } data_type& operator [] (key_type const& k) { if (m_map.find(k) == m_map.end()) { double edge_gradient = (m_graph.getIntensity(k.first) + m_graph.getIntensity(k.second)) / 2; m_map[k] = EuclideanDistance(k.first, k.second) + 255.0 - edge_gradient; } return m_map[k]; } private: std::map m_map; KisMagneticGraph m_graph; }; -KisMagneticWorker::KisMagneticWorker(const KisPaintDeviceSP& dev, qreal radius) +KisMagneticWorker::KisMagneticWorker(const KisPaintDeviceSP& dev) { m_dev = KisPainter::convertToAlphaAsGray(dev); KisPainter::copyAreaOptimized(dev->exactBounds().topLeft(), dev, m_dev, dev->exactBounds()); - KisGaussianKernel::applyTightLoG(m_dev, m_dev->exactBounds(), radius, -1.0, QBitArray(), nullptr); - //KisGaussianKernel::applyLoG(m_dev, m_dev->exactBounds(), radius, -1.0, QBitArray(), nullptr); - KisLazyFillTools::normalizeAlpha8Device(m_dev, m_dev->exactBounds()); - +} - m_graph = new KisMagneticGraph(m_dev); +void KisMagneticWorker::filterDevice(qreal radius, QRect &bounds) +{ + KisGaussianKernel::applyTightLoG(m_dev, bounds, radius, -1.0, QBitArray(), nullptr); + KisLazyFillTools::normalizeAlpha8Device(m_dev, bounds); } -QVector KisMagneticWorker::computeEdge(int radius, QPoint begin, QPoint end) +QVector KisMagneticWorker::computeEdge(int extraBounds, QPoint begin, QPoint end, qreal radius) { QRect rect; KisAlgebra2D::accumulateBounds(QVector { begin, end }, &rect); - rect = kisGrowRect(rect, radius); - + rect = kisGrowRect(rect, extraBounds); + filterDevice(radius, rect); VertexDescriptor goal(end); VertexDescriptor start(begin); - m_graph->m_rect = rect; + + KisMagneticGraph m_graph(m_dev, rect); // How many maps does it require? // Take a look here, if it doesn't make sense, https://www.boost.org/doc/libs/1_70_0/libs/graph/doc/astar_search.html PredecessorMap pmap; DistanceMap dmap(std::numeric_limits::max()); dmap[start] = 0; std::map rmap; std::map cmap; std::map imap; - WeightMap wmap(*m_graph); + WeightMap wmap(m_graph); AStarHeuristic heuristic(goal); QVector result; try{ boost::astar_search_no_init( - *m_graph, start, heuristic, + m_graph, start, heuristic, boost::visitor(AStarGoalVisitor(goal)) .distance_map(boost::associative_property_map(dmap)) .predecessor_map(boost::ref(pmap)) .weight_map(boost::associative_property_map(wmap)) .vertex_index_map(boost::associative_property_map >(imap)) .rank_map(boost::associative_property_map >(rmap)) .color_map(boost::associative_property_map >(cmap)) .distance_combine(std::plus()) .distance_compare(std::less()) ); }catch (GoalFound const&) { for (VertexDescriptor u = goal; u != start; u = pmap[u]) { result.push_front(QPointF(u.x, u.y)); } } result.push_front(QPoint(start.x, start.y)); return result; } // KisMagneticWorker::computeEdge void KisMagneticWorker::saveTheImage(vQPointF points) { QImage img = m_dev->convertToQImage(0, m_dev->exactBounds()); const QPointF offset = m_dev->exactBounds().topLeft(); for (QPointF &pt : points) { pt -= offset; } img = img.convertToFormat(QImage::Format_ARGB32); QPainter gc(&img); QPainterPath path; for (int i = 0; i < points.size(); i++) { if (i == 0) { path.moveTo(points[i]); } else { path.lineTo(points[i]); } } gc.setPen(Qt::blue); gc.drawPath(path); gc.setPen(Qt::green); gc.drawEllipse(points[0], 3, 3); gc.setPen(Qt::red); gc.drawEllipse(points[points.count() -1], 2, 2); img.save("result.png"); } - -quint8 KisMagneticWorker::intensity(QPoint pt) -{ - return m_graph->getIntensity(VertexDescriptor(pt)); -} diff --git a/plugins/tools/selectiontools/KisMagneticWorker.h b/plugins/tools/selectiontools/KisMagneticWorker.h index 9aac6e7b76..634cf1d2f6 100644 --- a/plugins/tools/selectiontools/KisMagneticWorker.h +++ b/plugins/tools/selectiontools/KisMagneticWorker.h @@ -1,44 +1,41 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISMAGNETICWORKER_H #define KISMAGNETICWORKER_H #include #include -class KisMagneticGraph; - class KRITASELECTIONTOOLS_EXPORT KisMagneticWorker { public: KisMagneticWorker() { // Do not use this, just for making the compiler happy } - KisMagneticWorker(const KisPaintDeviceSP &dev, qreal radius); + KisMagneticWorker(const KisPaintDeviceSP &dev); - QVector computeEdge(int radius, QPoint start, QPoint end); - quint8 intensity(QPoint pt); + QVector computeEdge(int extraBounds, QPoint start, QPoint end, qreal radius); void saveTheImage(vQPointF points); private: - KisMagneticGraph *m_graph; + void filterDevice(qreal radius, QRect &bounds); KisPaintDeviceSP m_dev; }; #endif // ifndef KISMAGNETICWORKER_H diff --git a/plugins/tools/selectiontools/KisToolSelectMagnetic.cc b/plugins/tools/selectiontools/KisToolSelectMagnetic.cc index 135e15373b..5590480b01 100644 --- a/plugins/tools/selectiontools/KisToolSelectMagnetic.cc +++ b/plugins/tools/selectiontools/KisToolSelectMagnetic.cc @@ -1,399 +1,398 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisToolSelectMagnetic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_painter.h" #include #include "canvas/kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include "kis_algebra_2d.h" #include "KisHandlePainterHelper.h" #include #define FEEDBACK_LINE_WIDTH 2 KisToolSelectMagnetic::KisToolSelectMagnetic(KoCanvasBase *canvas) : KisToolSelect(canvas, KisCursor::load("tool_magnetic_selection_cursor.png", 5, 5), i18n("Magnetic Selection")), m_continuedMode(false), m_complete(false), m_threshold(70), m_checkPoint(-1), m_frequency(30), m_radius(3.0) { } void KisToolSelectMagnetic::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control) { m_continuedMode = true; } KisToolSelect::keyPressEvent(event); } void KisToolSelectMagnetic::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control || !(event->modifiers() & Qt::ControlModifier)) { m_continuedMode = false; if (mode() != PAINT_MODE && !m_points.isEmpty()) { finishSelectionAction(); } } KisToolSelect::keyReleaseEvent(event); } // the cursor is still tracked even when no mousebutton is pressed void KisToolSelectMagnetic::mouseMoveEvent(KoPointerEvent *event) { m_lastCursorPos = convertToPixelCoord(event); KisToolSelect::mouseMoveEvent(event); updatePaintPath(); } // KisToolSelectMagnetic::mouseMoveEvent // press primary mouse button void KisToolSelectMagnetic::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); QPointF temp(convertToPixelCoord(event)); if(m_points.count() != 0){ - vQPointF edge = m_worker.computeEdge(m_frequency, m_anchorPoints.last(), temp.toPoint()); + vQPointF edge = m_worker.computeEdge(m_frequency, m_anchorPoints.last(), temp.toPoint(), m_radius); m_points.append(edge); if(m_snapBound.contains(temp.toPoint())){ m_complete = true; return; } } else { m_points.push_back(temp.toPoint()); m_snapBound = QRectF(temp.toPoint(), QSize(5,5)); } m_lastAnchor = temp.toPoint(); m_anchorPoints.push_back(m_lastAnchor); updateCanvasPixelRect(image()->bounds()); m_complete = false; updatePaintPath(); } // drag while primary mouse button is pressed void KisToolSelectMagnetic::continuePrimaryAction(KoPointerEvent *event) { KisToolSelectBase::continuePrimaryAction(event); } // release primary mouse button void KisToolSelectMagnetic::endPrimaryAction(KoPointerEvent *event) { KisToolSelectBase::endPrimaryAction(event); } void KisToolSelectMagnetic::finishSelectionAction() { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); kisCanvas->updateCanvas(); setMode(KisTool::HOVER_MODE); m_complete = true; // just for testing out //m_worker.saveTheImage(m_points); QRectF boundingViewRect = pixelToView(KisAlgebra2D::accumulateBounds(m_points)); KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Magnetic Selection")); if (m_points.count() > 2 && !helper.tryDeselectCurrentSelection(boundingViewRect, selectionAction())) { QApplication::setOverrideCursor(KisCursor::waitCursor()); const SelectionMode mode = helper.tryOverrideSelectionMode(kisCanvas->viewManager()->selection(), selectionMode(), selectionAction()); if (mode == PIXEL_SELECTION) { KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); KisPainter painter(tmpSel); painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace())); painter.setAntiAliasPolygonFill(antiAliasSelection()); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setStrokeStyle(KisPainter::StrokeStyleNone); painter.paintPolygon(m_points); QPainterPath cache; cache.addPolygon(m_points); cache.closeSubpath(); tmpSel->setOutlineCache(cache); helper.selectPixelSelection(tmpSel, selectionAction()); } else { KoPathShape *path = new KoPathShape(); path->setShapeId(KoPathShapeId); QTransform resolutionMatrix; resolutionMatrix.scale(1 / currentImage()->xRes(), 1 / currentImage()->yRes()); path->moveTo(resolutionMatrix.map(m_points[0])); for (int i = 1; i < m_points.count(); i++) path->lineTo(resolutionMatrix.map(m_points[i])); path->close(); path->normalize(); helper.addSelectionShape(path, selectionAction()); } QApplication::restoreOverrideCursor(); } m_points.clear(); m_anchorPoints.clear(); m_paintPath = QPainterPath(); } // KisToolSelectMagnetic::finishSelectionAction void KisToolSelectMagnetic::updatePaintPath() { m_paintPath = QPainterPath(); m_paintPath.moveTo(pixelToView(m_points[0])); for (int i = 1; i < m_points.count(); i++) { m_paintPath.lineTo(pixelToView(m_points[i])); } updateFeedback(); if (m_continuedMode && mode() != PAINT_MODE) { updateContinuedMode(); } updateCanvasPixelRect(image()->bounds()); } void KisToolSelectMagnetic::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if ((mode() == KisTool::PAINT_MODE || m_continuedMode) && !m_points.isEmpty()) { QPainterPath outline = m_paintPath; if (m_continuedMode && mode() != KisTool::PAINT_MODE) { outline.lineTo(pixelToView(m_lastCursorPos)); } paintToolOutline(&gc, outline); Q_FOREACH (const QPoint pt, m_anchorPoints) { KisHandlePainterHelper helper(&gc, handleRadius()); if(m_complete && QRect(pt, QSize(5,5)).contains(m_lastCursorPos.toPoint())){ helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles()); }else{ helper.setHandleStyle(KisHandleStyle::primarySelection()); } helper.drawHandleRect(pixelToView(pt), 4, QPoint(0, 0)); } } } void KisToolSelectMagnetic::updateFeedback() { if (m_points.count() > 1) { qint32 lastPointIndex = m_points.count() - 1; QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_points[lastPointIndex]).normalized(); updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH); updateCanvasPixelRect(updateRect); } } void KisToolSelectMagnetic::updateContinuedMode() { if (!m_points.isEmpty()) { qint32 lastPointIndex = m_points.count() - 1; QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_lastCursorPos).normalized(); updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH); updateCanvasPixelRect(updateRect); } } void KisToolSelectMagnetic::activate(KoToolBase::ToolActivation activation, const QSet &shapes) { - m_worker = KisMagneticWorker(image()->projection(), m_radius); + m_worker = KisMagneticWorker(image()->projection()); m_configGroup = KSharedConfig::openConfig()->group(toolId()); connect(action("undo_polygon_selection"), SIGNAL(triggered()), SLOT(undoPoints()), Qt::UniqueConnection); KisToolSelect::activate(activation, shapes); } void KisToolSelectMagnetic::deactivate() { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); kisCanvas->updateCanvas(); m_continuedMode = false; m_complete = true; disconnect(action("undo_polygon_selection"), nullptr, this, nullptr); KisTool::deactivate(); } void KisToolSelectMagnetic::undoPoints() { if(m_complete) return; m_anchorPoints.pop_back(); if(m_anchorPoints.size() == 1){ m_points.resize(1); }else{ int last = m_points.count() - 1; for(; last>=0;last--){ if(m_points[last] == m_anchorPoints.last()){ break; } } m_points.resize(last); } updatePaintPath(); } void KisToolSelectMagnetic::requestStrokeEnd() { if(m_complete || m_anchorPoints.count() < 2) return; finishSelectionAction(); } void KisToolSelectMagnetic::requestStrokeCancellation() { m_complete = true; m_points.clear(); m_anchorPoints.clear(); m_paintPath = QPainterPath(); updatePaintPath(); } QWidget * KisToolSelectMagnetic::createOptionWidget() { KisToolSelectBase::createOptionWidget(); KisSelectionOptions *selectionWidget = selectionOptionWidget(); QHBoxLayout *f1 = new QHBoxLayout(); QLabel *lblRad = new QLabel(i18n("Radius: "), selectionWidget); f1->addWidget(lblRad); KisDoubleSliderSpinBox *radInput = new KisDoubleSliderSpinBox(selectionWidget); radInput->setObjectName("radius"); radInput->setRange(2.5, 100.0, 2); radInput->setSingleStep(0.5); radInput->setToolTip("Radius of the filter for the detecting edges, might take some time to calculate"); f1->addWidget(radInput); connect(radInput, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetRadius(qreal))); QHBoxLayout *f2 = new QHBoxLayout(); QLabel *lblThreshold = new QLabel(i18n("Threshold: "), selectionWidget); f2->addWidget(lblThreshold); KisSliderSpinBox *threshInput = new KisSliderSpinBox(selectionWidget); threshInput->setObjectName("threshold"); threshInput->setRange(1, 255); threshInput->setSingleStep(10); threshInput->setToolTip("Threshold for determining the minimum intensity of the edges"); f2->addWidget(threshInput); connect(threshInput, SIGNAL(valueChanged(int)), this, SLOT(slotSetThreshold(int))); QHBoxLayout *f3 = new QHBoxLayout(); QLabel *lblFrquency = new QLabel(i18n("Anchor Gap: "), selectionWidget); f3->addWidget(lblFrquency); KisSliderSpinBox *freqInput = new KisSliderSpinBox(selectionWidget); freqInput->setObjectName("frequency"); freqInput->setRange(20, 200); freqInput->setSingleStep(10); freqInput->setToolTip("Average distance between 2 anchors in pixels"); freqInput->setSuffix(" px"); f3->addWidget(freqInput); connect(freqInput, SIGNAL(valueChanged(int)), this, SLOT(slotSetFrequency(int))); QVBoxLayout *l = dynamic_cast(selectionWidget->layout()); Q_ASSERT(l); l->insertLayout(1, f1); l->insertLayout(2, f2); l->insertLayout(3, f3); radInput->setValue(m_configGroup.readEntry("radius", 3.0)); threshInput->setValue(m_configGroup.readEntry("threshold", 100)); freqInput->setValue(m_configGroup.readEntry("frequency", 30)); return selectionWidget; } // KisToolSelectMagnetic::createOptionWidget void KisToolSelectMagnetic::slotSetRadius(qreal r) { m_radius = r; m_configGroup.writeEntry("radius", r); - m_worker = KisMagneticWorker(image()->projection(), m_radius); } void KisToolSelectMagnetic::slotSetThreshold(int t) { m_threshold = t; m_configGroup.writeEntry("threshold", t); } void KisToolSelectMagnetic::slotSetFrequency(int f) { m_frequency = f; m_configGroup.writeEntry("frequency", f); } void KisToolSelectMagnetic::resetCursorStyle() { if (selectionAction() == SELECTION_ADD) { useCursor(KisCursor::load("tool_magnetic_selection_cursor_add.png", 6, 6)); } else if (selectionAction() == SELECTION_SUBTRACT) { useCursor(KisCursor::load("tool_magnetic_selection_cursor_sub.png", 6, 6)); } else { KisToolSelect::resetCursorStyle(); } } diff --git a/plugins/tools/selectiontools/tests/KisMagneticWorkerTest.cc b/plugins/tools/selectiontools/tests/KisMagneticWorkerTest.cc index 094bba9a63..33c9006f66 100644 --- a/plugins/tools/selectiontools/tests/KisMagneticWorkerTest.cc +++ b/plugins/tools/selectiontools/tests/KisMagneticWorkerTest.cc @@ -1,142 +1,142 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisMagneticWorkerTest.h" #include #include #include #include #include #include inline KisPaintDeviceSP loadTestImage(const QString &name, bool convertToAlpha) { QImage image(TestUtil::fetchDataFileLazy(name)); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(image, 0); if (convertToAlpha) { dev = KisPainter::convertToAlphaAsAlpha(dev); } return dev; } void KisMagneticWorkerTest::testWorker() { KisPaintDeviceSP dev = loadTestImage("test_main.png", false); const QRect rect = dev->exactBounds(); KisPaintDeviceSP grayscaleDev = KisPainter::convertToAlphaAsGray(dev); - KisMagneticWorker worker(grayscaleDev, 2.0); + KisMagneticWorker worker(grayscaleDev); const QPoint startPos(80, 10); const QPoint endPos(10, 100); - auto points = worker.computeEdge(20, startPos, endPos); + auto points = worker.computeEdge(20, startPos, endPos, 3.0); KIS_DUMP_DEVICE_2(grayscaleDev, rect, "draw", "dd"); /* QVector result = { QPointF(50,65), QPointF(49,64), QPointF(48,63), QPointF(47,62), QPointF(46,61), QPointF(45,60), QPointF(44,59), QPointF(44,58), QPointF(44,57), QPointF(44,56), QPointF(44,55), QPointF(44,54), QPointF(44,53), QPointF(44,52), QPointF(44,51), QPointF(44,50), QPointF(44,49), QPointF(44,48), QPointF(44,47), QPointF(44,46), QPointF(44,45), QPointF(44,44), QPointF(44,43), QPointF(44,42), QPointF(43,41), QPointF(43,40), QPointF(43,39), QPointF(44,38), QPointF(44,37), QPointF(44,36), QPointF(44,35), QPointF(44,34), QPointF(44,33), QPointF(44,32), QPointF(44,31), QPointF(44,30), QPointF(44,29), QPointF(44,28), QPointF(44,27), QPointF(44,26), QPointF(44,25), QPointF(44,24), QPointF(44,23), QPointF(44,22), QPointF(44,21), QPointF(44,20), QPointF(44,19), QPointF(44,18), QPointF(43,17), QPointF(44,16), QPointF(44,15), QPointF(44,14), QPointF(44,13), QPointF(43,12), QPointF(42,11), QPointF(41,11), QPointF(40,10)}; QCOMPARE(result, points); */ QImage img = dev->convertToQImage(0, rect); img = img.convertToFormat(QImage::Format_ARGB32); QPainter gc(&img); QPainterPath path; for (int i = 0; i < points.size(); i++) { if (i == 0) { path.moveTo(points[i]); } else { path.lineTo(points[i]); } } gc.setPen(Qt::blue); gc.drawPath(path); gc.setPen(Qt::green); gc.drawEllipse(startPos, 3, 3); gc.setPen(Qt::red); gc.drawEllipse(endPos, 2, 2); img.save("result.png"); } QTEST_MAIN(KisMagneticWorkerTest)