[calligra] krita/ui/tool: Fixed a "screwed" lines bug

Dmitry Kazakov dimula73 at gmail.com
Sun Jun 23 15:40:03 UTC 2013


Git commit 9fa24c74cfba39852ce0e0956d75ff4d33c32eec by Dmitry Kazakov.
Committed on 23/06/2013 at 15:06.
Pushed by dkazakov into branch 'master'.

Fixed a "screwed" lines bug

It turned out that our "bended lines" bug consists from two different bugs.
1) Lack of tablet events (only in openGL mode)
2) A bug in Basic Smoothing algorithm (some whirls
   appeared even in QPainter canvas mode).

This patch fixes the second one. The bug was related to the fact that
the size of the control points for the bezier curves was *not limited*.
It resulted in lines bending, screwing and whirling effect. Now the size
of the control lines is always bounded, so the lines became much smoother.

But the first part of the "bended lines" bug is still not fixed, so openGL
mode will produce bends in lines.

CCMAIL:kimageshop at kde.org


The first one is the lack of tablet events in openGL mode, and the second
one was related to the

M  +0    -6    krita/ui/tool/kis_tool_freehand.cc
M  +129  -24   krita/ui/tool/kis_tool_freehand_helper.cpp
M  +4    -0    krita/ui/tool/kis_tool_freehand_helper.h

http://commits.kde.org/calligra/9fa24c74cfba39852ce0e0956d75ff4d33c32eec

diff --git a/krita/ui/tool/kis_tool_freehand.cc b/krita/ui/tool/kis_tool_freehand.cc
index 5656815..eba0dc3 100644
--- a/krita/ui/tool/kis_tool_freehand.cc
+++ b/krita/ui/tool/kis_tool_freehand.cc
@@ -278,12 +278,6 @@ void KisToolFreehand::mouseReleaseEvent(KoPointerEvent* e)
 {
     if (mode() == KisTool::PAINT_MODE &&
             e->button() == Qt::LeftButton) {
-        if (m_smoothingOptions.smoothingType == KisSmoothingOptions::WEIGHTED_SMOOTHING) {
-            m_smoothingOptions.smoothingType = KisSmoothingOptions::SIMPLE_SMOOTHING;
-            m_helper->setSmoothness(m_smoothingOptions);
-            doStroke(e);
-            m_smoothingOptions.smoothingType =  KisSmoothingOptions::WEIGHTED_SMOOTHING;
-        }
         endStroke();
 
         if (m_assistant) {
diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp b/krita/ui/tool/kis_tool_freehand_helper.cpp
index 188cb7c..40c56fd 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.cpp
+++ b/krita/ui/tool/kis_tool_freehand_helper.cpp
@@ -35,6 +35,9 @@
 #include <math.h>
 #include <qnumeric.h> // for qIsNaN
 
+//#define DEBUG_BEZIER_CURVES
+
+
 struct KisToolFreehandHelper::Private
 {
     KisPaintingInformationBuilder *infoBuilder;
@@ -143,6 +146,94 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
     }
 }
 
+void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
+                                               QPointF tangent1, QPointF tangent2)
+{
+    if (tangent1.isNull() || tangent2.isNull()) return;
+
+    const qreal maxSanePoint = 1e6;
+
+    QPointF controlTarget1;
+    QPointF controlTarget2;
+
+    // Shows the direction in which control points go
+    QPointF controlDirection1 = pi1.pos() + tangent1;
+    QPointF controlDirection2 = pi2.pos() - tangent2;
+
+    // Lines in the direction of the control points
+    QLineF line1(pi1.pos(), controlDirection1);
+    QLineF line2(pi2.pos(), controlDirection2);
+
+    // Lines to check whether the control points lay on the opposite
+    // side of the line
+    QLineF line3(controlDirection1, controlDirection2);
+    QLineF line4(pi1.pos(), pi2.pos());
+
+    QPointF intersection;
+    if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
+        qreal controlLength = line4.length() / 2;
+
+        line1.setLength(controlLength);
+        line2.setLength(controlLength);
+
+        controlTarget1 = line1.p2();
+        controlTarget2 = line2.p2();
+    } else {
+        QLineF::IntersectType type = line1.intersect(line2, &intersection);
+
+        if (type == QLineF::NoIntersection ||
+            intersection.manhattanLength() > maxSanePoint) {
+
+            intersection = 0.5 * (pi1.pos() + pi2.pos());
+            qDebug() << "WARINING: there is no intersection point "
+                     << "in the basic smoothing algoriths";
+        }
+
+        controlTarget1 = intersection;
+        controlTarget2 = intersection;
+    }
+
+    // shows how near to the controlTarget the value raises
+    qreal coeff = 0.8;
+
+    qreal velocity1 = QLineF(QPointF(), tangent1).length();
+    qreal velocity2 = QLineF(QPointF(), tangent2).length();
+
+    Q_ASSERT(velocity1 > 0);
+    Q_ASSERT(velocity2 > 0);
+
+    qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
+
+    // the controls should not differ more than 50%
+    similarity = qMax(similarity, 0.5);
+
+    // when the controls are symmetric, their size should be smaller
+    // to avoid corner-like curves
+    coeff *= 1 - qMax(0.0, similarity - 0.8);
+
+    Q_ASSERT(coeff > 0);
+
+
+    QPointF control1;
+    QPointF control2;
+
+    if (velocity1 > velocity2) {
+        control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
+        coeff *= similarity;
+        control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
+    } else {
+        control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
+        coeff *= similarity;
+        control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
+    }
+
+    paintBezierCurve(m_d->painterInfos,
+                     pi1,
+                     control1,
+                     control2,
+                     pi2);
+}
+
 void KisToolFreehandHelper::paint(KoPointerEvent *event)
 {
     KisPaintInformation info =
@@ -218,29 +309,21 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
     }
 
     if (m_d->smoothingOptions.smoothingType == KisSmoothingOptions::SIMPLE_SMOOTHING
-            || m_d->smoothingOptions.smoothingType == KisSmoothingOptions::WEIGHTED_SMOOTHING)
+        || m_d->smoothingOptions.smoothingType == KisSmoothingOptions::WEIGHTED_SMOOTHING)
     {
         // Now paint between the coordinates, using the bezier curve interpolation
         if (!m_d->haveTangent) {
             m_d->haveTangent = true;
-
-            // XXX: 3.0 is a magic number I don't know anything about
-            //      1.0 was the old default value for smoothness, and anything lower than that
-            //      gave horrible results, so remove that setting.
             m_d->previousTangent =
                     (info.pos() - m_d->previousPaintInformation.pos()) /
-                    (3.0 * (info.currentTime() - m_d->previousPaintInformation.currentTime()));
+                    (info.currentTime() - m_d->previousPaintInformation.currentTime());
         } else {
             QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
-                    (3.0 * (info.currentTime() - m_d->olderPaintInformation.currentTime()));
-            qreal scaleFactor = (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
-            QPointF control1 = m_d->olderPaintInformation.pos() + m_d->previousTangent * scaleFactor;
-            QPointF control2 = m_d->previousPaintInformation.pos() - newTangent * scaleFactor;
-            paintBezierCurve(m_d->painterInfos,
-                             m_d->olderPaintInformation,
-                             control1,
-                             control2,
-                             m_d->previousPaintInformation);
+                    (info.currentTime() - m_d->olderPaintInformation.currentTime());
+
+            paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation,
+                               m_d->previousTangent, newTangent);
+
             m_d->previousTangent = newTangent;
         }
         m_d->olderPaintInformation = m_d->previousPaintInformation;
@@ -296,15 +379,13 @@ void KisToolFreehandHelper::finishStroke()
     if (m_d->haveTangent) {
         m_d->haveTangent = false;
 
-        QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) / 3.0;
-        qreal scaleFactor = (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
-        QPointF control1 = m_d->olderPaintInformation.pos() + m_d->previousTangent * scaleFactor;
-        QPointF control2 = m_d->previousPaintInformation.pos() - newTangent;
-        paintBezierCurve(m_d->painterInfos,
-                         m_d->olderPaintInformation,
-                         control1,
-                         control2,
-                         m_d->previousPaintInformation);
+        QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) /
+            (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
+
+        paintBezierSegment(m_d->olderPaintInformation,
+                           m_d->previousPaintInformation,
+                           m_d->previousTangent,
+                           newTangent);
     }
 }
 
@@ -348,6 +429,30 @@ void KisToolFreehandHelper::paintBezierCurve(PainterInfo *painterInfo,
                                              const QPointF &control2,
                                              const KisPaintInformation &pi2)
 {
+#ifdef DEBUG_BEZIER_CURVES
+    KisPaintInformation tpi1;
+    KisPaintInformation tpi2;
+
+    tpi1 = pi1;
+    tpi2 = pi2;
+
+    tpi1.setPressure(0.3);
+    tpi2.setPressure(0.3);
+
+    paintLine(m_d->painterInfos, tpi1, tpi2);
+
+    tpi1.setPressure(0.6);
+    tpi2.setPressure(0.3);
+
+    tpi1.setPos(pi1.pos());
+    tpi2.setPos(control1);
+    paintLine(m_d->painterInfos, tpi1, tpi2);
+
+    tpi1.setPos(pi2.pos());
+    tpi2.setPos(control2);
+    paintLine(m_d->painterInfos, tpi1, tpi2);
+#endif
+
     m_d->hasPaintAtLeastOnce = true;
     m_d->strokesFacade->addJob(m_d->strokeId,
                                new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
diff --git a/krita/ui/tool/kis_tool_freehand_helper.h b/krita/ui/tool/kis_tool_freehand_helper.h
index 5ee035e..61f0567 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.h
+++ b/krita/ui/tool/kis_tool_freehand_helper.h
@@ -95,6 +95,10 @@ protected:
                           const QPointF &control2,
                           const KisPaintInformation &pi2);
 
+private:
+    void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
+                                                   QPointF tangent1, QPointF tangent2);
+
 private slots:
 
     void finishStroke();


More information about the kimageshop mailing list