void TestSegmentTypeCommand::changeToLine()
{
    KoPathShape path;
    path.moveTo( QPointF(0,0) );
    path.curveTo( QPointF(25,25), QPointF(75,25), QPointF(100,0) );

    KoPathPointData segment(&path, KoPathPointIndex(0,0));
    QList<KoPathPointData> segments;
    segments.append(segment);

    // get first segment
    KoPathSegment s = path.segmentByIndex(KoPathPointIndex(0,0));

    KoPathSegmentTypeCommand cmd(segments, KoPathSegmentTypeCommand::Line);

    QVERIFY(s.first()->activeControlPoint2());
    QVERIFY(s.second()->activeControlPoint1());

    cmd.redo();

    QVERIFY(!s.first()->activeControlPoint2());
    QVERIFY(!s.second()->activeControlPoint1());

    cmd.undo();

    QVERIFY(s.first()->activeControlPoint2());
    QVERIFY(s.second()->activeControlPoint1());
}
void KarbonCalligraphicShape::appendPointsToPathAux(const QPointF &p1,
        const QPointF &p2)
{
    KoPathPoint *pathPoint1 = new KoPathPoint(this, p1);
    KoPathPoint *pathPoint2 = new KoPathPoint(this, p2);

    // calculate the index of the insertion position
    int index = pointCount() / 2;

    insertPoint(pathPoint2, KoPathPointIndex(0, index));
    insertPoint(pathPoint1, KoPathPointIndex(0, index));
}
bool KarbonCalligraphicShape::flipDetected(const QPointF &p1, const QPointF &p2)
{
    // detect the flip caused by the angle changing 180 degrees
    // thus detect the boundary crossing
    int index = pointCount() / 2;
    QPointF last1 = pointByIndex(KoPathPointIndex(0, index - 1))->point();
    QPointF last2 = pointByIndex(KoPathPointIndex(0, index))->point();

    int sum1 = std::abs(ccw(p1, p2, last1) + ccw(p1, last2, last1));
    int sum2 = std::abs(ccw(p2, p1, last2) + ccw(p2, last1, last2));
    // if there was a flip
    return sum1 < 2 && sum2 < 2;
}
void KarbonCalligraphicShape::addCap(int index1, int index2, int pointIndex,
                                     bool inverted)
{
    QPointF p1 = m_points[index1]->point();
    QPointF p2 = m_points[index2]->point();

    // TODO: review why spikes can appear with a lower limit
    QPointF delta = p2 - p1;
    if (delta.manhattanLength() < 1.0)
        return;

    QPointF direction = QLineF(QPointF(0, 0), delta).unitVector().p2();
    qreal width = m_points[index2]->width();
    QPointF p = p2 + direction * m_caps * width;

    KoPathPoint * newPoint = new KoPathPoint(this, p);

    qreal angle = m_points[index2]->angle();
    if (inverted)
        angle += M_PI;

    qreal dx = std::cos(angle) * width;
    qreal dy = std::sin(angle) * width;
    newPoint->setControlPoint1(QPointF(p.x() - dx / 2, p.y() - dy / 2));
    newPoint->setControlPoint2(QPointF(p.x() + dx / 2, p.y() + dy / 2));

    insertPoint(newPoint, KoPathPointIndex(0, pointIndex));
}
void KarbonPathRefineCommand::redo()
{
    // check if we have to create the insert points commands
    if (! d->initialized) {
        // create insert point commands, one for each point to insert
        // into each segment
        for (uint iteration = 0; iteration < d->insertCount; ++iteration) {
            // in each iteration collect the (iteration+1)th point which starts a segments
            // into which we insert the point of this iteration
            QList<KoPathPointData> pointData;
            // calculate the segment position where to insert the point
            qreal insertPosition = 1.0 / (d->insertCount + 1 - iteration);
            int subpathCount = d->path->subpathCount();
            // iterate over the paths subpaths
            for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
                int pointCount = d->path->subpathPointCount(subpathIndex);
                // iterate over the subpaths points
                for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
                    // we only collect the (iteration+1)th point
                    if ((pointIndex + 1) % (iteration + 1) != 0)
                        continue;
                    pointData.append(KoPathPointData(d->path, KoPathPointIndex(subpathIndex, pointIndex)));
                }
            }
            // create the command and execute it
            KUndo2Command * cmd = new KoPathPointInsertCommand(pointData, insertPosition, this);
            cmd->redo();
        }
        d->initialized = true;
    } else {
        KUndo2Command::redo();
    }
    d->path->update();
}
/*
 * The algorithm to break a multiple open or closed subpaths is:
 * Subpath is closed
 * - open behind the last point in the subpath
 * - go on like as described in Not closed
 * Not closed
 * - break from the back of the subpath
 */
KoPathBreakAtPointCommand::KoPathBreakAtPointCommand(const QList<KoPathPointData> & pointDataList, KUndo2Command *parent)
        : KUndo2Command(parent)
        , m_deletePoints(true)
{
    QList<KoPathPointData> sortedPointDataList(pointDataList);
    qSort(sortedPointDataList);
    setText(i18nc("(qtundo-format)", "Break subpath at points"));

    QList<KoPathPointData>::const_iterator it(sortedPointDataList.constBegin());
    for (; it != sortedPointDataList.constEnd(); ++it) {
        KoPathShape * pathShape = it->pathShape;
        KoPathPoint * point = pathShape->pointByIndex(it->pointIndex);
        if(! point)
            continue;

        // check if subpath is closed and the point is start or end point of the subpath
        if(! pathShape->isClosedSubpath(it->pointIndex.first)) {
            if(it->pointIndex.second == 0
                || it->pointIndex.second == pathShape->subpathPointCount(it->pointIndex.first)) {
                continue;
            }
        }

        m_pointDataList.append(*it);
        m_points.push_back(new KoPathPoint(*point));
        m_closedIndex.push_back(KoPathPointIndex(-1, 0));
    }

    KoPathPointData last(0, KoPathPointIndex(-1, -1));
    for (int i = m_pointDataList.size() - 1; i >= 0; --i) {
        const KoPathPointData &current = m_pointDataList.at(i);

        if (last.pathShape != current.pathShape || last.pointIndex.first != current.pointIndex.first) {
            last = current;
            if (current.pathShape->isClosedSubpath(current.pointIndex.first)) {
                // the break will happen before the inserted point so we have to increment by 1
                m_closedIndex[i] = current.pointIndex;
                ++m_closedIndex[i].second;
            }
        }
    }
}
const QRectF KarbonCalligraphicShape::lastPieceBoundingRect()
{
    if (pointCount() < 6)
        return QRectF();

    int index = pointCount() / 2;

    QPointF p1 = pointByIndex(KoPathPointIndex(0, index - 3))->point();
    QPointF p2 = pointByIndex(KoPathPointIndex(0, index - 2))->point();
    QPointF p3 = pointByIndex(KoPathPointIndex(0, index - 1))->point();
    QPointF p4 = pointByIndex(KoPathPointIndex(0, index))->point();
    QPointF p5 = pointByIndex(KoPathPointIndex(0, index + 1))->point();
    QPointF p6 = pointByIndex(KoPathPointIndex(0, index + 2))->point();

    // TODO: also take the control points into account
    QPainterPath p;
    p.moveTo(p1);
    p.lineTo(p2);
    p.lineTo(p3);
    p.lineTo(p4);
    p.lineTo(p5);
    p.lineTo(p6);

    return p.boundingRect().translated(position());
}
void KarbonWhirlPinchCommand::undo()
{
    d->pathShape->update();
    uint subpathCount = d->pathData.count();
    for( uint subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex )
    {
        uint pointCount = d->pathData[subpathIndex].count();
        for( uint pointIndex = 0; pointIndex < pointCount; ++pointIndex )
        {
            KoPathPoint * p = d->pathShape->pointByIndex( KoPathPointIndex( subpathIndex, pointIndex ) );
            d->pathData[subpathIndex][pointIndex].restorePoint( p );
        }
    }
    d->pathShape->normalize();
    d->pathShape->update();

    QUndoCommand::undo();
}
KarbonWhirlPinchCommand::KarbonWhirlPinchCommand( KoPathShape * path, qreal angle, qreal pinch, qreal radius, QUndoCommand *parent )
    : QUndoCommand( parent ), d( new Private( path, angle, pinch, radius ) )
{
    setText( i18n( "Whirl & pinch" ) );

    // save the path point data used for undo
    uint subpathCount = d->pathShape->subpathCount();
    for( uint subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex )
    {
        QList<PointData> subpathData;
        int pointCount = d->pathShape->pointCountSubpath( subpathIndex );
        for( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
        {
            KoPathPoint * p = d->pathShape->pointByIndex( KoPathPointIndex( subpathIndex, pointIndex ) );
            subpathData.append( PointData( p ) );
        }
        d->pathData.append( subpathData );
    }
}
void KoPathBreakAtPointCommand::redo()
{
    KUndo2Command::redo();
    KoPathPointData last(0, KoPathPointIndex(-1, -1));

    // offset, needed when path was opened
    int offset = 0;
    for (int i = m_pointDataList.size() - 1; i >= 0; --i) {
        const KoPathPointData & pd = m_pointDataList.at(i);
        KoPathShape * pathShape = pd.pathShape;

        KoPathPointIndex pointIndex = pd.pointIndex;
        if (last.pathShape != pathShape || last.pointIndex.first != pointIndex.first) {
            offset = 0;
        }

        pointIndex.second = pointIndex.second + offset + 1;
        pathShape->insertPoint(m_points[i], pointIndex);

        if (m_closedIndex.at(i).first != -1) {
            m_closedIndex[i] = pathShape->openSubpath(m_closedIndex.at(i));
            offset = m_closedIndex.at(i).second;
        } else {
            KoPathPointIndex breakIndex = pd.pointIndex;
            breakIndex.second += offset;
            pathShape->breakAfter(breakIndex);
            m_closedIndex[i].second = offset;
        }

        if (last.pathShape != pathShape) {
            if (last.pathShape) {
                last.pathShape->update();
            }
            last = pd;
        }
    }
    if (last.pathShape) {
        last.pathShape->update();
    }

    m_deletePoints = false;
}
예제 #11
0
void RoundCornersCommand::copyPath(KoPathShape * dst, KoPathShape * src)
{
    dst->clear();

    int subpathCount = src->subpathCount();
    for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
        int pointCount = src->subpathPointCount(subpathIndex);
        if (! pointCount)
            continue;

        KoSubpath * subpath = new KoSubpath;
        for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
            KoPathPoint * p = src->pointByIndex(KoPathPointIndex(subpathIndex, pointIndex));
            KoPathPoint * c = new KoPathPoint(*p);
            c->setParent(dst);
            subpath->append(c);
        }
        dst->addSubpath(subpath, subpathIndex);
    }
    dst->setTransformation(src->transformation());
}
예제 #12
0
KoSubpathJoinCommand::KoSubpathJoinCommand(const KoPathPointData &pointData1, const KoPathPointData &pointData2, KUndo2Command *parent)
        : KUndo2Command(parent)
        , m_pointData1(pointData1)
        , m_pointData2(pointData2)
        , m_splitIndex(KoPathPointIndex(-1, -1))
        , m_oldProperties1(KoPathPoint::Normal)
        , m_oldProperties2(KoPathPoint::Normal)
        , m_reverse(0)
{
    Q_ASSERT(m_pointData1.pathShape == m_pointData2.pathShape);
    KoPathShape * pathShape = m_pointData1.pathShape;
    Q_ASSERT(!pathShape->isClosedSubpath(m_pointData1.pointIndex.first));
    Q_ASSERT(m_pointData1.pointIndex.second == 0 ||
             m_pointData1.pointIndex.second == pathShape->subpathPointCount(m_pointData1.pointIndex.first) - 1);
    Q_ASSERT(!pathShape->isClosedSubpath(m_pointData2.pointIndex.first));
    Q_ASSERT(m_pointData2.pointIndex.second == 0 ||
             m_pointData2.pointIndex.second == pathShape->subpathPointCount(m_pointData2.pointIndex.first) - 1);
    //TODO check that points are not the same

    if (m_pointData2 < m_pointData1)
        qSwap(m_pointData1, m_pointData2);

    if (m_pointData1.pointIndex.first != m_pointData2.pointIndex.first) {
        if (m_pointData1.pointIndex.second == 0 && pathShape->subpathPointCount(m_pointData1.pointIndex.first) > 1)
            m_reverse |= ReverseFirst;
        if (m_pointData2.pointIndex.second != 0)
            m_reverse |= ReverseSecond;
        setText(kundo2_i18n("Close subpath"));
    } else {
        setText(kundo2_i18n("Join subpaths"));
    }

    KoPathPoint * point1 = pathShape->pointByIndex(m_pointData1.pointIndex);
    KoPathPoint * point2 = pathShape->pointByIndex(m_pointData2.pointIndex);

    m_oldControlPoint1 = QPointF(pathShape->shapeToDocument(m_reverse & 1 ? point1->controlPoint1() : point1->controlPoint2()));
    m_oldControlPoint2 = QPointF(pathShape->shapeToDocument(m_reverse & 2 ? point2->controlPoint1() : point2->controlPoint2()));
    m_oldProperties1 = point1->properties();
    m_oldProperties2 = point2->properties();
}
예제 #13
0
void KarbonWhirlPinchCommand::redo()
{
    d->pathShape->update();
    uint subpathCount = d->pathData.count();
    for( uint subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex )
    {
        uint pointCount = d->pathData[subpathIndex].count();
        for( uint pointIndex = 0; pointIndex < pointCount; ++pointIndex )
        {
            KoPathPoint * p = d->pathShape->pointByIndex( KoPathPointIndex( subpathIndex, pointIndex ) );
            p->setPoint( d->whirlPinch( p->point() ) );
            if( p->activeControlPoint1() )
                p->setControlPoint1( d->whirlPinch( p->controlPoint1() ) );
            if( p->activeControlPoint2() )
                p->setControlPoint2( d->whirlPinch( p->controlPoint2() ) );
        }
    }
    d->pathShape->normalize();
    d->pathShape->update();

    QUndoCommand::redo();
}
void KarbonCalligraphicShape::addCap(int index1, int index2, int pointIndex,
                                     bool inverted)
{
    QPointF p1 = m_points[index1]->point();
    QPointF p2 = m_points[index2]->point();
    qreal width = m_points[index2]->width();

    QPointF direction = QLineF(QPointF(0, 0), p2 - p1).unitVector().p2();
    QPointF p = p2 + direction * m_caps * width;

    KoPathPoint * newPoint = new KoPathPoint(this, p);

    qreal angle = m_points[index2]->angle();
    if (inverted)
        angle += M_PI;

    qreal dx = std::cos(angle) * width;
    qreal dy = std::sin(angle) * width;
    newPoint->setControlPoint1(QPointF(p.x() - dx / 2, p.y() - dy / 2));
    newPoint->setControlPoint2(QPointF(p.x() + dx / 2, p.y() + dy / 2));

    insertPoint(newPoint, KoPathPointIndex(0, pointIndex));
}
void KarbonCalligraphicShape::
appendPointToPath(const KarbonCalligraphicPoint &p)
{
    qreal dx = std::cos(p.angle()) * p.width();
    qreal dy = std::sin(p.angle()) * p.width();

    // find the outline points
    QPointF p1 = p.point() - QPointF(dx / 2, dy / 2);
    QPointF p2 = p.point() + QPointF(dx / 2, dy / 2);

    if (pointCount() == 0) {
        moveTo(p1);
        lineTo(p2);
        normalize();
        return;
    }
    // pointCount > 0

    bool flip = (pointCount() >= 2) ? flipDetected(p1, p2) : false;

    // if there was a flip add additional points
    if (flip) {
        appendPointsToPathAux(p2, p1);
        if (pointCount() > 4)
            smoothLastPoints();
    }

    appendPointsToPathAux(p1, p2);

    if (pointCount() > 4) {
        smoothLastPoints();

        if (flip) {
            int index = pointCount() / 2;
            // find the last two points
            KoPathPoint *last1 = pointByIndex(KoPathPointIndex(0, index - 1));
            KoPathPoint *last2 = pointByIndex(KoPathPointIndex(0, index));

            last1->removeControlPoint1();
            last1->removeControlPoint2();
            last2->removeControlPoint1();
            last2->removeControlPoint2();
            m_lastWasFlip = true;
        }

        if (m_lastWasFlip) {
            int index = pointCount() / 2;
            // find the previous two points
            KoPathPoint *prev1 = pointByIndex(KoPathPointIndex(0, index - 2));
            KoPathPoint *prev2 = pointByIndex(KoPathPointIndex(0, index + 1));

            prev1->removeControlPoint1();
            prev1->removeControlPoint2();
            prev2->removeControlPoint1();
            prev2->removeControlPoint2();

            if (! flip)
                m_lastWasFlip = false;
        }
    }
    normalize();

    // add initial cap if it's the fourth added point
    // this code is here because this function is called from different places
    // pointCount() == 8 may causes crashes because it doesn't take possible
    // flips into account
    if (m_points.count() >= 4 && &p == m_points[3]) {
        kDebug(38000) << "Adding caps!!!!!!!!!!!!!!!!" << m_points.count();
        addCap(3, 0, 0, true);
        // duplicate the last point to make the points remain "balanced"
        // needed to keep all indexes code (else I would need to change
        // everything in the code...)
        KoPathPoint *last = pointByIndex(KoPathPointIndex(0, pointCount() - 1));
        KoPathPoint *newPoint = new KoPathPoint(this, last->point());
        insertPoint(newPoint, KoPathPointIndex(0, pointCount()));
        close();
    }
}
void KoPathPointTypeCommand::redo()
{
    KUndo2Command::redo();
    repaint(false);
    m_additionalPointData.clear();

    QList<PointData>::iterator it(m_oldPointData.begin());
    for (; it != m_oldPointData.end(); ++it) {
        KoPathPoint *point = it->m_pointData.pathShape->pointByIndex(it->m_pointData.pointIndex);
        KoPathPoint::PointProperties properties = point->properties();

        switch (m_pointType) {
        case Line: {
            point->removeControlPoint1();
            point->removeControlPoint2();
            break;
        }
        case Curve: {
            KoPathPointIndex pointIndex = it->m_pointData.pointIndex;
            KoPathPointIndex prevIndex;
            KoPathPointIndex nextIndex;
            KoPathShape * path = it->m_pointData.pathShape;
            // get previous path node
            if (pointIndex.second > 0)
                prevIndex = KoPathPointIndex(pointIndex.first, pointIndex.second - 1);
            else if (pointIndex.second == 0 && path->isClosedSubpath(pointIndex.first))
                prevIndex = KoPathPointIndex(pointIndex.first, path->subpathPointCount(pointIndex.first) - 1);
            // get next node
            if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1)
                nextIndex = KoPathPointIndex(pointIndex.first, pointIndex.second + 1);
            else if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1
                     && path->isClosedSubpath(pointIndex.first))
                nextIndex = KoPathPointIndex(pointIndex.first, 0);

            KoPathPoint * prevPoint = path->pointByIndex(prevIndex);
            KoPathPoint * nextPoint = path->pointByIndex(nextIndex);

            if (prevPoint && ! point->activeControlPoint1() && appendPointData(KoPathPointData(path, prevIndex))) {
                KoPathSegment cubic = KoPathSegment(prevPoint, point).toCubic();
                if (prevPoint->activeControlPoint2()) {
                    prevPoint->setControlPoint2(cubic.first()->controlPoint2());
                    point->setControlPoint1(cubic.second()->controlPoint1());
                } else
                    point->setControlPoint1(cubic.second()->controlPoint1());
            }
            if (nextPoint && ! point->activeControlPoint2() && appendPointData(KoPathPointData(path, nextIndex))) {
                KoPathSegment cubic = KoPathSegment(point, nextPoint).toCubic();
                if (nextPoint->activeControlPoint1()) {
                    point->setControlPoint2(cubic.first()->controlPoint2());
                    nextPoint->setControlPoint1(cubic.second()->controlPoint1());
                } else
                    point->setControlPoint2(cubic.first()->controlPoint2());
            }
            break;
        }
        case Symmetric: {
            properties &= ~KoPathPoint::IsSmooth;
            properties |= KoPathPoint::IsSymmetric;

            // calculate vector from node point to first control point and normalize it
            QPointF directionC1 = point->controlPoint1() - point->point();
            qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y());
            directionC1 /= dirLengthC1;
            // calculate vector from node point to second control point and normalize it
            QPointF directionC2 = point->controlPoint2() - point->point();
            qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y());
            directionC2 /= dirLengthC2;
            // calculate the average distance of the control points to the node point
            qreal averageLength = 0.5 * (dirLengthC1 + dirLengthC2);
            // compute position of the control points so that they lie on a line going through the node point
            // the new distance of the control points is the average distance to the node point
            point->setControlPoint1(point->point() + 0.5 * averageLength * (directionC1 - directionC2));
            point->setControlPoint2(point->point() + 0.5 * averageLength * (directionC2 - directionC1));
        }
        break;
        case Smooth: {
            properties &= ~KoPathPoint::IsSymmetric;
            properties |= KoPathPoint::IsSmooth;

            // calculate vector from node point to first control point and normalize it
            QPointF directionC1 = point->controlPoint1() - point->point();
            qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y());
            directionC1 /= dirLengthC1;
            // calculate vector from node point to second control point and normalize it
            QPointF directionC2 = point->controlPoint2() - point->point();
            qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y());
            directionC2 /= dirLengthC2;
            // compute position of the control points so that they lie on a line going through the node point
            // the new distance of the control points is the average distance to the node point
            point->setControlPoint1(point->point() + 0.5 * dirLengthC1 * (directionC1 - directionC2));
            point->setControlPoint2(point->point() + 0.5 * dirLengthC2 * (directionC2 - directionC1));
        }
        break;
        case Corner:
        default:
            properties &= ~KoPathPoint::IsSymmetric;
            properties &= ~KoPathPoint::IsSmooth;
            break;
        }
        point->setProperties(properties);
    }
    repaint(true);
}
예제 #17
0
void RoundCornersCommand::roundPath()
{
    /*
    * This algorithm is worked out by <kudling AT kde DOT org> to produce similar results as
    * the "round corners" algorithms found in other applications. Neither code nor
    * algorithms from any 3rd party is used though.
    *
    * We want to replace all corners with round corners having "radius" m_radius.
    * The algorithm doesn't really produce circular arcs, but that's ok since
    * the algorithm achieves nice looking results and is generic enough to be applied
    * to all kind of paths.
    * Note also, that this algorithm doesn't touch smooth joins (in the sense of
    * KoPathPoint::isSmooth() ).
    *
    * We'll manipulate the input path for bookkeeping purposes and construct a new
    * temporary path in parallel. We finally replace the input path with the new path.
    *
    *
    * Without restricting generality, let's assume the input path is closed and
    * contains segments which build a rectangle.
    *
    *           2
    *    O------------O
    *    |            |        Numbers reflect the segments' order
    *   3|            |1       in the path. We neglect the "begin"
    *    |            |        segment here.
    *    O------------O
    *           0
    *
    * There are three unique steps to process. The second step is processed
    * many times in a loop.
    *
    * 1) Begin
    *    -----
    *    Split the first segment of the input path (called "path[0]" here)
    *    at parameter t
    *
    *        t = path[0]->param( m_radius )
    *
    *    and move newPath to this new knot. If current segment is too small
    *    (smaller than 2 * m_radius), we always set t = 0.5 here and in the further
    *    steps as well.
    *
    *    path:                 new path:
    *
    *           2
    *    O------------O
    *    |            |
    *  3 |            | 1                    The current segment is marked with "#"s.
    *    |            |
    *    O##O#########O        ...O
    *           0                     0
    *
    * 2) Loop
    *    ----
    *    The loop step is iterated over all segments. After each appliance the index n
    *    is incremented and the loop step is reapplied until no untouched segment is left.
    *
    *    Split the current segment path[n] of the input path at parameter t
    *
    *        t = path[n]->param( path[n]->length() - m_radius )
    *
    *    and add the first subsegment of the curent segment to newPath.
    *
    *    path:                 new path:
    *
    *           2
    *    O------------O
    *    |            |
    *  3 |            | 1
    *    |            |
    *    O--O######O##O           O------O...
    *           0                     0
    *
    *    Now make the second next segment (the original path[1] segment in our example)
    *    the current one. Split it at parameter t
    *
    *        t = path[n]->param( m_radius )
    *
    *    path:                 new path:
    *
    *           2
    *    O------------O
    *    |            #
    *  3 |            O 1
    *    |            #
    *    O--O------O--O           O------O...
    *           0                     0
    *
    *    Make the first subsegment of the current segment the current one.
    *
    *    path:                 new path:
    *
    *           2
    *    O------------O
    *    |            |
    *  3 |            O 1                   O
    *    |            #                    /.1
    *    O--O------O--O           O------O...
    *           0                     0
    *
    * 3) End
    *    ---
    *
    *    path:                 new path:
    *
    *           2                     4
    *    O--O------O--O        5 .O------O. 3
    *    |            |         /          \
    *  3 O            O 1    6 O            O 2
    *    |            |      7 .\          /
    *    O--O------O--O        ...O------O. 1
    *           0                     0
    */

    // TODO: not sure if we should only touch flat segment joins as the original algorithm

    m_path->clear();

    int subpathCount = m_copy->subpathCount();
    for( int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex )
    {
        int pointCount = m_copy->pointCountSubpath( subpathIndex );
        if( ! pointCount )
            continue;

        // check if we have sufficient number of points
        if( pointCount < 3 )
        {
            // copy the only segment
            KoPathSegment s = m_copy->segmentByIndex( KoPathPointIndex( subpathIndex, 0 ) );
            m_path->moveTo( m_copy->pointByIndex( KoPathPointIndex( subpathIndex, 0 ) )->point() );
            addSegment( m_path, s );

            continue;
        }

        KoPathSegment prevSeg = m_copy->segmentByIndex( KoPathPointIndex( subpathIndex, pointCount-1 ) );
        KoPathSegment nextSeg = m_copy->segmentByIndex( KoPathPointIndex( subpathIndex, 0 ) );
        KoPathSegment lastSeg;

        KoPathPoint * currPoint = nextSeg.first();
        KoPathPoint * firstPoint = 0;
        KoPathPoint * lastPoint = 0;

        // check if first path point is a smooth join with the closing segment
        bool firstPointIsCorner = m_copy->isClosedSubpath( subpathIndex ) 
                && ! currPoint->isSmooth( prevSeg.first(), nextSeg.second() );

        // Begin: take care of the first path point
        if( firstPointIsCorner )
        {
            // split the previous segment at length - radius
            qreal prevLength = prevSeg.length();
            qreal prevSplit = prevLength > m_radius ? prevSeg.paramAtLength( prevLength-m_radius ) : 0.5;
            QPair<KoPathSegment,KoPathSegment> prevParts = prevSeg.splitAt( prevSplit );

            // split the next segment at radius
            qreal nextLength = nextSeg.length();
            qreal nextSplit = nextLength > m_radius ? nextSeg.paramAtLength( m_radius ) : 0.5;
            QPair<KoPathSegment,KoPathSegment> nextParts = nextSeg.splitAt( nextSplit );

            // calculate smooth tangents
            QPointF P0 = prevParts.first.second()->point();
            QPointF P3 = nextParts.first.second()->point();
            qreal tangentLength1 = 0.5 * QLineF( P0, currPoint->point() ).length();
            qreal tangentLength2 = 0.5 * QLineF( P3, currPoint->point() ).length();
            QPointF P1 = P0 - tangentLength1 * tangentAtEnd( prevParts.first );
            QPointF P2 = P3 - tangentLength2 * tangentAtStart( nextParts.second );

            // start the subpath
            firstPoint = m_path->moveTo( prevParts.second.first()->point() );
            // connect the split points with curve
            // TODO: shall we create a correct arc?
            m_path->curveTo( P1, P2, P3 );

            prevSeg = nextParts.second;
            lastSeg = prevParts.first;
        }
        else
        {
            firstPoint = m_path->moveTo( currPoint->point() );
            prevSeg = nextSeg;
        }

        // Loop:
        for( int pointIndex = 1; pointIndex < pointCount; ++pointIndex )
        {
            nextSeg = m_copy->segmentByIndex( KoPathPointIndex( subpathIndex, pointIndex ) );
            if( ! nextSeg.isValid() )
                break;

            currPoint = nextSeg.first();
            if( ! currPoint )
                continue;

            if( currPoint->isSmooth( prevSeg.first(), nextSeg.second() ) )
            {
                // the current point has a smooth join, so we can add the previous segment
                // to our new path
                addSegment( m_path, prevSeg );
                prevSeg = nextSeg;
            }
            else
            {
                // split the previous segment at length - radius
                qreal prevLength = prevSeg.length();
                qreal prevSplit = prevLength > m_radius ? prevSeg.paramAtLength( prevLength-m_radius ) : 0.5;
                QPair<KoPathSegment,KoPathSegment> prevParts = prevSeg.splitAt( prevSplit );

                // add the remaining part up to the split point of the pervious segment
                lastPoint = addSegment( m_path, prevParts.first );

                // split the next segment at radius
                qreal nextLength = nextSeg.length();
                qreal nextSplit = nextLength > m_radius ? nextSeg.paramAtLength( m_radius ) : 0.5;
                QPair<KoPathSegment,KoPathSegment> nextParts = nextSeg.splitAt( nextSplit );

                // calculate smooth tangents
                QPointF P0 = prevParts.first.second()->point();
                QPointF P3 = nextParts.first.second()->point();
                qreal tangentLength1 = 0.5 * QLineF( P0, currPoint->point() ).length();
                qreal tangentLength2 = 0.5 * QLineF( P3, currPoint->point() ).length();
                QPointF P1 = P0 - tangentLength1 * tangentAtEnd( prevParts.first );
                QPointF P2 = P3 - tangentLength2 * tangentAtStart( nextParts.second );

                // connect the split points with curve
                // TODO: shall we create a correct arc?
                lastPoint = m_path->curveTo( P1, P2, P3 );

                prevSeg = nextParts.second;
            }

        }

        // End: take care of the last path point
        if( firstPointIsCorner )
        {
            // construct the closing segment
            lastPoint->setProperty( KoPathPoint::CloseSubpath );
            firstPoint->setProperty( KoPathPoint::CloseSubpath );
            switch( lastSeg.degree() )
            {
                case 1:
                    lastPoint->removeControlPoint2();
                    firstPoint->removeControlPoint1();
                    break;
                case 2:
                    if( lastSeg.first()->activeControlPoint2() )
                    {
                        lastPoint->setControlPoint2( lastSeg.first()->controlPoint2() );
                        firstPoint->removeControlPoint1();
                    }
                    else
                    {
                        lastPoint->removeControlPoint2();
                        firstPoint->setControlPoint1( lastSeg.second()->controlPoint1() );
                    }
                    break;
                case 3:
                    lastPoint->setControlPoint2( lastSeg.first()->controlPoint2() );
                    firstPoint->setControlPoint1( lastSeg.second()->controlPoint1() );
                    break;
            }
        }
        else
        {
            // add the last remaining segment
            addSegment( m_path, prevSeg );
        }
    }
}