Porting Magnetic Lasso to Krita
Open, NormalPublic

Description

1. Introduction

  • Krita already had a magnetic lasso but that got lost while it was being ported to Qt4.
  • This task was set up to port the tool but was later paused by the author who was working on it.
  • This project aims to continue the work and complete the porting to the current version of Krita.

2. Project Goals

  • Add the Magnetic Lasso Selection Tool in Krita.
  • The tool when used should stick to the edges found in the picture.
  • Starts after the user left clicks and makes an anchor, edges are scanned from the anchor point.
  • After the mouse moves a fixed amount of distance the tool automatically places another anchor point and uses it as a reference.
  • The user can also manually set an anchor point, him/herself.
  • The search radius which is used to determine, up to what area the tool would search for an edge, as well as the frequency of anchors can be adjusted by the user.
  • The above-mentioned parameters, as well as the threshold for determining edges, can be adjusted using the tool options widget.

The UI layout for the Tool Options Widget would be something like this

3. Implementation

How do we calculate the edge?

  • Draw a straight line hypothetically from the last node to the current mouse position.
  • Add the radius to get the approximate search area.
  • Apply LoG to get the possible edges.
  • Search area would be constant for a given radius and frequency of adding nodes.
  • Start searching from the last node to get the nearest edge point, if the pixel determined as the nearest edge is not in the immediate neighborhood of the last node, just draw a straight line to it.
  • Continue this process to the mouse position.
  • If the length of the edge exceeds the frequency of nodes, add a node and continue.

Laplacian of Gaussian (LoG)
Implementation of LoG can be found in kis_gauss_kernel.cpp inside KisGaussianKernel::applyLoG, which was written during the implementation of the colorize mask. The implementation of KisMagneticSelectionWorker will be done in 2 stages. In the first stage, a test will be implemented so that we can verify the accuracy of our algorithm. For that, LoG will be applied to the whole KisPaintDevice. Later on, after we have done implementing the mouse-events we can adapt the algorithm for the selected region.

A-star search
We can use boost::astar_search from “boost/graph/astar_search.hpp” to implement the required A* search for the algorithm. The heuristic function for the A* search algorithm would be, given pi(xi,yi) the point for which the heuristic will be calculated, based on the Euclidean distance of the mouse cursor, pm(xm,ym) and distance from the base-line from last node p0(x0,y0) to pm , let’s take it as di, where

di= abs((ym-y0)*xi - (xm-x0)*yi + xm*y0 + x0*ym)/sqrt((ym-y0)^2 + (xm-x0)^2)

And taking the Euclidean distance from mouse cursor dm we get,

dm = sqrt((ym-yi)^2 + (xm-xi)^2)

We would be wanting the minimum distance from the mouse cursor and at the same time for as little deviation from the baseline as possible. So we could have the final result as a combination of the previous values,
dv = a*di+b*dm
where the values of a and b need to be derived experimentally.

I have worked on a small proof of concept, though not complete, it shows out how the original image, after applying the first approximation, will transform into something like this. On top of which we will be applying the A-star algorithm based on the source and final points.

4. Timeline

Community Bonding Period [May 6 to May 27]

  • Know more about the development and the communication process in the Krita team.
  • Add my blog to planet.kde.org .
  • Create a task regarding the project on phabricator.
  • Learn more about how KisPaintDevice is prepared for boost::graph.
  • Prepare a skeleton for the test following KisWaterShedWorkerTest.
  • Create a separate branch for the feature.

Deliverables Timeline
Implement KisMagneticSelectionWorker and a base test for it [May 27 to June 24]

  • KisMagneticSelectionWorker should accept a KisPaintDevice and a radius for calculating edges for it.
  • Reuse KisGaussianKernel::applyLoG for the first approximation.
  • Develop the heuristic function for the A-star algorithm which would be used.
  • Implement computeEdge() which takes lastEdgePoint, currentMousePos and returns a vector of QPoints representing the edge.
  • KisMagneticSelectionWorkerTest should be implemented which would load a test image and run the algorithm.
  • Write a blog post about the feature progress and the results obtained.
  • Document the code.

Setup KisToolSelectMagnetic for basic line selections [June 25 to July 3]

  • Overload the mousePressEvent for drawing nodes on Left Mouse Click and delete points on Right Mouse Click.
  • Should be able to draw straight lines between the points.
  • Overload the mouseMoveEvent for making the line follow the Mouse Pointer.
  • Automatically place points after the mouse travels a certain distance.

Make room in KisMagneticSelectionWorker for KisToolSelectMagnetic [July 4 to July 22]

  • Overload computeEdge() for taking a KisPaintDevice and a search area along with the other parameters.
  • This will probably take some time to settle down, so need time for proper debugging.
  • Another blog post detailing the progress.
  • If possible a video on it too.

Add KisToolSelectMagneticOptionWidget for adjusting the constants [July 23 to August 5]

  • Add a Tools Option Widget just like the other tools.
  • Options to be adjusted: fuzziness, radius, and frequency of points.

Buffer Time [August 5 to August 26]

  • If something goes wrong, we have time for that.
  • Krita Sprints.
  • Experiment with Sobel and fuzz based system.
  • The final blog post.
  • Again a video on the feature if possible.
  • Prepare the branch for merging into master.
  • Complete missing documentation of code and also document the feature on the Krita Manual.

The complete proposal can be found here, https://docs.google.com/document/d/1AF1U1Qb8JKS6Ky8pC_daDgeMDBrzVU8C7ulK_-0kg28/edit?usp=sharing

Details

Commits
R37:c8f6f91425b9: Added signal compressor for radius change and mouse hover events
R37:a21e2acb0379: Anchor Gap is now based on image distance
R37:ffaaa4a4cbd2: First anchor is highlighted when the loop can be completed
R37:dcbe9cc0e521: Changed the cursors for the magnetic lasso
R37:350d28074b01: Added Shift+Z to undo points
R37:82404413e566: Added tool tips to the options widget
R37:3b31fd7029ee: Added some documentation about the checkpoint procedure
R37:97f25734381d: Minimum radius increased
R37:9109e9b74d59: Fixed the length calculation algorithm
R37:5cf36cfc285d: Can discover multiple anchors in a single event
R37:c74b40e3bed5: Updated the Tool Options widget to reflect the quantities better
R37:3c16c6d8c7ad: Takes relative distance into account instead of absolute distance
R37:78b3171c62c2: Changed handle style for drawing anchorpoints
R37:8a08cc46676e: Frequency controls the bounding box, radius the kernel size
R37:4e3e6d891686: Frequency is now based on screen distance
R37:8a5b9cb4f918: Fixed the checkpoint procedure
R37:1ed2c3fbfefd: Fixed crash on paint, suppressed warnings
R37:02c9f12ebe32: Fixed formatting and added an uncrustify config
R37:4b9df12df56d: Added the radius and threshold options to Options Widget
R37:739f1f59ff68: Overriden the createOptionWidget method
R37:0e5e553f34d7: Updated the cursor and adjusted the checkpoint procedure
R37:f430367d54d1: Reimplemented the frequency based checkpoint procedure
R37:6a3c01182d32: Fixed crashing while completing the selection, can finish selection without…
R37:faa1e5597cd0: Selection can be cancelled on the fly now
R37:34386d2f7ea0: Implemented dual checkpoint procedure, somewhat like PS
R37:1615e9129bcd: Checkpoints are drawn now, :)
R37:da0cf17c6c97: Selection works as expected
R37:b44404ee9bc6: Selection can be done with the Magnetic Lasso
R37:70b167f41b9f: Optimized the algorithm, implemented checkpoints
R37:79aeb7826c2c: LoG now runs on tool activation
R37:7b53f2e4659d: Creates a bouding rectangle before searching
R37:b64a646d303a: Reduced the number of calls KisPaintDevice::exactBounds() is called
R37:3f74df11d694: Initial take on the UI
R37:8fc4030bdcce: Set the initial structure for Magnetic Selection Tool
R37:58c2ab7acc3e: Added a couple of comments and completed the test
R37:463a9b189150: The algorithm is almost perfect now
R37:ca7340a0eef8: Not clean but the output is closer to what is expected
R37:958cc6b1e584: The algorithm works as expected now
R37:1ca04c3f8b3c: Fixed the intensity function
R37:51e0986a4748: Distance heuristic function takes threshold into account now
R37:5adb8eb6d6a9: AStar Search is doing its job, mostly likely
R37:09c77df3cce0: Implemented boost::astar_search using the KisMagneticGraph wrapper
R37:d8e844a362c6: Implemented the boost::graph interface for KisPaintDevice
R37:afcb9243c0a2: Added initial structure for KisMagneticWorkerTest
hellozee created this task.May 7 2019, 1:00 PM
hellozee triaged this task as Normal priority.
rempt added a subscriber: rempt.May 7 2019, 1:01 PM

Note: For some reason, the phab task is not reflecting the commits, another way to get the commits would be this https://invent.kde.org/kde/krita/commits/kuntalmajumder/T10894-magnetic-lasso?utf8=%E2%9C%93&search=T10894

Oo commits are showing up now

dkazakov added a subscriber: dkazakov.EditedJul 18 2019, 10:31 AM

The search algorithm works kind of correctly now, though there are the follwoing UIX problems:

  1. Esc and Enter keys must reset and apply the stroke correspondingly. Like it is done in KisToolPolylineBase (see requestStrokeEnd() and requestStrokeCancellation())
  2. When you hover the tail of the outline, the cursor should change, signaling that a click will close the path (like in KisToolPolylineBase)
  3. Tracing round objects still doesn't work. I have a feeling it is somehow related to contol-point setting algorithm. Please test it in a unittest:

  1. It also seems like the added control points are not painted on the canvas atm
This comment was removed by woltherav.
This comment was removed by woltherav.

Application: Krita (krita), signal: Aborted
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Current thread is 1 (Thread 0x7fb560029e80 (LWP 4662))]

Thread 8 (Thread 0x7fb5060a8700 (LWP 4876)):
#0 0x00007fb552a960b4 in GI_libc_read (fd=27, buf=0x7fb5060a7b50, nbytes=16) at ../sysdeps/unix/sysv/linux/read.c:27
#1 0x00007fb55eefffd0 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.4
#2 0x00007fb5493ac2d0 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#3 0x00007fb5493670b7 in g_main_context_check () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#4 0x00007fb549367570 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#5 0x00007fb5493676dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#6 0x00007fb5535f5dcb in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#7 0x00007fb55359703a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#8 0x00007fb5533be4ca in QThread::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#9 0x00007fb5533bfc72 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007fb5523626db in start_thread (arg=0x7fb5060a8700) at pthread_create.c:463
#11 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 7 (Thread 0x7fb4e57b8700 (LWP 4863)):
#0 0x00007fb5493674c8 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#1 0x00007fb5493676dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#2 0x00007fb5535f5dcb in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#3 0x00007fb55359703a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#4 0x00007fb5533be4ca in QThread::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#5 0x00007fb53259f926 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Quick.so.5
#6 0x00007fb5533bfc72 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#7 0x00007fb5523626db in start_thread (arg=0x7fb4e57b8700) at pthread_create.c:463
#8 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 6 (Thread 0x7fb50a075700 (LWP 4862)):
#0 0x00007fb552a960b4 in GI_libc_read (fd=24, buf=0x7fb50a074b30, nbytes=16) at ../sysdeps/unix/sysv/linux/read.c:27
#1 0x00007fb55eefffd0 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.4
#2 0x00007fb5493ac2d0 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#3 0x00007fb5493670b7 in g_main_context_check () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#4 0x00007fb549367570 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#5 0x00007fb5493676dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#6 0x00007fb5535f5dcb in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#7 0x00007fb55359703a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#8 0x00007fb5533be4ca in QThread::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#9 0x00007fb5320da115 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5
#10 0x00007fb5533bfc72 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#11 0x00007fb5523626db in start_thread (arg=0x7fb50a075700) at pthread_create.c:463
#12 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 5 (Thread 0x7fb4fb2a7700 (LWP 4845)):
#0 syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
#1 0x00007fb5533c2630 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#2 0x00007fb5533c24a2 in QSemaphore::tryAcquire(int, int) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#3 0x00007fb555a3e27f in KisTileDataSwapper::waitForWork (this=0x7fb556cbbb80 <(anonymous namespace)::Q_QGS_s_instance::innerFunction()::holder+64>) at /home/wolthera/krita/src/libs/image/tiles3/swap/kis_tile_data_swapper.cpp:86
#4 0x00007fb555a3e29a in KisTileDataSwapper::run (this=0x7fb556cbbb80 <(anonymous namespace)::Q_QGS_s_instance::innerFunction()::holder+64>) at /home/wolthera/krita/src/libs/image/tiles3/swap/kis_tile_data_swapper.cpp:92
#5 0x00007fb5533bfc72 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#6 0x00007fb5523626db in start_thread (arg=0x7fb4fb2a7700) at pthread_create.c:463
#7 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 4 (Thread 0x7fb4fd4aa700 (LWP 4839)):
#0 0x00007fb5493ad664 in g_mutex_unlock () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#1 0x00007fb549367586 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#2 0x00007fb5493676dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#3 0x00007fb5535f5dcb in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#4 0x00007fb55359703a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#5 0x00007fb5533be4ca in QThread::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#6 0x00007fb5533bfc72 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#7 0x00007fb5523626db in start_thread (arg=0x7fb4fd4aa700) at pthread_create.c:463
#8 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 3 (Thread 0x7fb52cf80700 (LWP 4677)):
#0 0x00007fb552a960b4 in GI_libc_read (fd=9, buf=0x7fb52cf7fb20, nbytes=16) at ../sysdeps/unix/sysv/linux/read.c:27
#1 0x00007fb55eefffd0 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.4
#2 0x00007fb5493ac2d0 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#3 0x00007fb5493670b7 in g_main_context_check () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#4 0x00007fb549367570 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#5 0x00007fb5493676dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#6 0x00007fb5535f5dcb in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#7 0x00007fb55359703a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#8 0x00007fb5533be4ca in QThread::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#9 0x00007fb54aa6f015 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5DBus.so.5
#10 0x00007fb5533bfc72 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#11 0x00007fb5523626db in start_thread (arg=0x7fb52cf80700) at pthread_create.c:463
#12 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 2 (Thread 0x7fb53bd2e700 (LWP 4676)):
#0 0x00007fb5523689f3 in futex_wait_cancelable (private=<optimized out>, expected=0, futex_word=0x614000005108) at ../sysdeps/unix/sysv/linux/futex-internal.h:88
#1 pthread_cond_wait_common (abstime=0x0, mutex=0x6140000050b8, cond=0x6140000050e0) at pthread_cond_wait.c:502
#2
pthread_cond_wait (cond=0x6140000050e0, mutex=0x6140000050b8) at pthread_cond_wait.c:655
#3 0x00007fb538a583da in ?? () from /usr/lib/x86_64-linux-gnu/dri/i965_dri.so
#4 0x00007fb538a58107 in ?? () from /usr/lib/x86_64-linux-gnu/dri/i965_dri.so
#5 0x00007fb5523626db in start_thread (arg=0x7fb53bd2e700) at pthread_create.c:463
#6 0x00007fb552aa788f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 1 (Thread 0x7fb560029e80 (LWP 4662)):
[KCrash Handler]
#7 GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#8 0x00007fb5529c6801 in
GI_abort () at abort.c:79
#9 0x00007fb55339bf2b in QMessageLogger::fatal(char const*, ...) const () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007fb55339b77b in qt_assert_x(char const*, char const*, char const*, int) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#11 0x00007fb50bf665d9 in QVector<QPointF>::operator[] (this=0x612000265750, i=-1) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qvector.h:437
#12 0x00007fb50bfad649 in KisToolSelectMagnetic::paint (this=0x6120002656c0, gc=..., converter=...) at /home/wolthera/krita/src/plugins/tools/selectiontools/KisToolSelectMagnetic.cc:239
#13 0x00007fb54f8e68c0 in KoToolProxy::paint (this=0x615000128ee0, painter=..., converter=...) at /home/wolthera/krita/src/libs/flake/KoToolProxy.cpp:138
#14 0x00007fb5599677a2 in KisCanvasWidgetBase::drawDecorations (this=0x6070002119d8, gc=..., updateWidgetRect=...) at /home/wolthera/krita/src/libs/ui/canvas/kis_canvas_widget_base.cpp:159
#15 0x00007fb559fed15f in KisOpenGLCanvas2::renderDecorations (this=0x6070002119a0, painter=0x7ffdd024cb10) at /home/wolthera/krita/src/libs/ui/opengl/kis_opengl_canvas2.cpp:953
#16 0x00007fb559fe5258 in KisOpenGLCanvas2::paintGL (this=0x6070002119a0) at /home/wolthera/krita/src/libs/ui/opengl/kis_opengl_canvas2.cpp:410
#17 0x00007fb5543f5b1d in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#18 0x00007fb5543d49c8 in QWidget::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#19 0x00007fb55439465c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#20 0x00007fb55439bb90 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#21 0x00007fb55a52fac3 in KisApplication::notify (this=0x7ffdd0252740, receiver=0x6070002119a0, event=0x7ffdd024d1e0) at /home/wolthera/krita/src/libs/ui/KisApplication.cpp:653
#22 0x00007fb553598d18 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#23 0x00007fb5543cd595 in QWidgetPrivate::sendPaintEvent(QRegion const&) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#24 0x00007fb5543cdd6d in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#25 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#26 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#27 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#28 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#29 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#30 0x00007fb5543ceb59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#31 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#32 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#33 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#34 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#35 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#36 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#37 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#38 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#39 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#40 0x00007fb5543ce9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#41 0x00007fb5543cd75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#42 0x00007fb5543a4266 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#43 0x00007fb5543a44a5 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#44 0x00007fb5543bc8bf in QWidgetPrivate::syncBackingStore() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#45 0x00007fb5543d4b38 in QWidget::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#46 0x00007fb5544dde50 in QMainWindow::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#47 0x00007fb5551f73fc in KMainWindow::event (this=0x60c001be2540, ev=0x603002be1660) at /home/wolthera/krita/src/libs/widgetutils/xmlgui/kmainwindow.cpp:765
#48 0x00007fb555291afd in KXmlGuiWindow::event (this=0x60c001be2540, ev=0x603002be1660) at /home/wolthera/krita/src/libs/widgetutils/xmlgui/kxmlguiwindow.cpp:125
#49 0x00007fb55439465c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#50 0x00007fb55439bb90 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#51 0x00007fb55a52fac3 in KisApplication::notify (this=0x7ffdd0252740, receiver=0x60c001be2540, event=0x603002be1660) at /home/wolthera/krita/src/libs/ui/KisApplication.cpp:653
#52 0x00007fb553598d18 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#53 0x00007fb55359b8d7 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#54 0x00007fb5535f6793 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#55 0x00007fb549367417 in g_main_context_dispatch () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#56 0x00007fb549367650 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#57 0x00007fb5493676dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#58 0x00007fb5535f5daf in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#59 0x00007fb55359703a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#60 0x00007fb5535a0170 in QCoreApplication::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#61 0x0000556c36d5b431 in main (argc=1, argv=0x7ffdd02528d8) at /home/wolthera/krita/src/krita/main.cc:535

Hi, @hellozee!

The GUI seem to work fine. Though the algorithm started to cretate some weird knots, like this:

@dkazakov hush, my checkpoint procedure, I have to work on that again, 🙃

rempt added a comment.Mon, Jul 29, 1:00 PM

I still get a crash though if I build with a Qt that has asserts enabled:

hread 1 (Thread 0x7f7b4766c800 (LWP 19789)):
[KCrash Handler]
#6 GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#7 0x00007f7b41f85801 in
GI_abort () at abort.c:79
#8 0x00007f7b4295af2b in QMessageLogger::fatal(char const*, ...) const () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#9 0x00007f7b4295a77b in qt_assert_x(char const*, char const*, char const*, int) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007f7b0968a929 in QVector<QPointF>::operator[] (i=-1, this=0x55701b59b5c0) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qglobal.h:973
#11 KisToolSelectMagnetic::paint (this=0x55701b59b530, gc=..., converter=...) at /home/boud/dev/hellozee/plugins/tools/selectiontools/KisToolSelectMagnetic.cc:239
#12 0x00007f7b45ea6000 in KisCanvasWidgetBase::drawDecorations(QPainter&, QRect const&) const () at /home/boud/dev/hellozee/libs/ui/canvas/kis_canvas_widget_base.cpp:260
#13 0x00007f7b4613c15d in KisOpenGLCanvas2::renderDecorations (this=this@entry=0x55701bd51110, painter=painter@entry=0x7ffd9183d550) at /home/boud/dev/hellozee/libs/ui/opengl/kis_opengl_canvas2.cpp:953
#14 0x00007f7b46143e53 in KisOpenGLCanvas2::paintGL() () at /home/boud/dev/hellozee/libs/ui/opengl/kis_opengl_canvas2.cpp:410
#15 0x00007f7b439b4b1d in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#16 0x00007f7b439939c8 in QWidget::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#17 0x00007f7b4395365c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#18 0x00007f7b4395ab90 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#19 0x00007f7b4630a117 in KisApplication::notify(QObject*, QEvent*) () at /home/boud/dev/hellozee/libs/ui/KisApplication.cpp:653
#20 0x00007f7b42b57d18 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#21 0x00007f7b4398c595 in QWidgetPrivate::sendPaintEvent(QRegion const&) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#22 0x00007f7b4398cd6d in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#23 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#24 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#25 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#26 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#27 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#28 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#29 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#30 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#31 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#32 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#33 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#34 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#35 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#36 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#37 0x00007f7b4398d9d0 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#38 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#39 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#40 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#41 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#42 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#43 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#44 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#45 0x00007f7b4398db59 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#46 0x00007f7b4398c75e in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#47 0x00007f7b43963266 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#48 0x00007f7b439634a5 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#49 0x00007f7b4397b8bf in QWidgetPrivate::syncBackingStore() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#50 0x00007f7b43993b38 in QWidget::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#51 0x00007f7b43a9ce50 in QMainWindow::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#52 0x00007f7b445db55b in KMainWindow::event (this=this@entry=0x557013538b50, ev=ev@entry=0x557019d84f80) at /home/boud/dev/hellozee/libs/widgetutils/xmlgui/kmainwindow.cpp:765
#53 0x00007f7b4461cbe9 in KXmlGuiWindow::event(QEvent*) () at /home/boud/dev/hellozee/libs/widgetutils/xmlgui/kxmlguiwindow.cpp:125
#54 0x00007f7b4395365c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#55 0x00007f7b4395ab90 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#56 0x00007f7b4630a117 in KisApplication::notify(QObject*, QEvent*) () at /home/boud/dev/hellozee/libs/ui/KisApplication.cpp:653
#57 0x00007f7b42b57d18 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#58 0x00007f7b42b5a8d7 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#59 0x00007f7b42bb5793 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#60 0x00007f7b3a24a417 in g_main_context_dispatch () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#61 0x00007f7b3a24a650 in ?? () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#62 0x00007f7b3a24a6dc in g_main_context_iteration () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#63 0x00007f7b42bb4daf in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#64 0x00007f7b42b5603a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#65 0x00007f7b42b5f170 in QCoreApplication::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#66 0x0000557008baa849 in main () at /home/boud/dev/hellozee/krita/main.cc:535
#67 0x00007f7b41f66b97 in __libc_start_main (main=0x557008ba9460 <main>, argc=1, argv=0x7ffd91840718, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffd91840708) at ../csu/libc-start.c:310
#68 0x0000557008babb5a in _start () at /home/boud/dev/hellozee/krita/main.cc:410

Sample icons:

And these two have the magnet angled, which might be a bit more dynamic.

And here's some simple tool cursors to go with it. @Deevad did the last batch, but I feel they're in line with his.