Example #1
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();
}
/*
 * 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;
            }
        }
    }
}
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);
}