void KoPathPointInsertCommand::undo() { QUndoCommand::undo(); for (int i = 0; i < m_pointDataList.size(); ++i) { const KoPathPointData &pdBefore = m_pointDataList.at(i); KoPathShape * pathShape = pdBefore.pathShape; KoPathPointIndex piAfter = pdBefore.pointIndex; ++piAfter.second; KoPathPoint * before = pathShape->pointByIndex(pdBefore.pointIndex); m_points[i] = pathShape->removePoint(piAfter); if (m_points[i]->properties() & KoPathPoint::CloseSubpath) { piAfter.second = 0; } KoPathPoint * after = pathShape->pointByIndex(piAfter); if (before->activeControlPoint2()) { QPointF controlPoint2 = before->controlPoint2(); qSwap(controlPoint2, m_controlPoints[i].first); before->setControlPoint2(controlPoint2); } if (after->activeControlPoint1()) { QPointF controlPoint1 = after->controlPoint1(); qSwap(controlPoint1, m_controlPoints[i].second); after->setControlPoint1(controlPoint1); } pathShape->update(); } m_deletePoints = true; }
QList<KoSubpath *> KarbonSimplifyPath::split( const KoPathShape &path ) { QList<KoSubpath *> res; KoSubpath *subpath = new KoSubpath; res.append( subpath ); for ( int i = 0; i < path.pointCount(); ++i ) { KoPathPoint *p = path.pointByIndex( KoPathPointIndex(0, i) ); // if the path separates two subpaths // (if it isn't smooth nor the first or last point) if ( i != 0 && i != path.pointCount()-1 ) { KoPathPoint *prev = path.pointByIndex( KoPathPointIndex(0, i-1) ); KoPathPoint *next = path.pointByIndex( KoPathPointIndex(0, i+1) ); if ( ! p->isSmooth(prev, next) ) { // create a new subpath subpath->append( new KoPathPoint(*p) ); subpath = new KoSubpath; res.append( subpath ); } } subpath->append( new KoPathPoint(*p) ); } return res; }
void KoPathPointTypeCommand::undoChanges(const QList<PointData> &data) { QList<PointData>::const_iterator it(data.begin()); for (; it != data.end(); ++it) { KoPathShape *pathShape = it->m_pointData.pathShape; KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex); point->setProperties(it->m_oldProperties); if (it->m_hadControlPoint1) point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1)); else point->removeControlPoint1(); if (it->m_hadControlPoint2) point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2)); else point->removeControlPoint2(); } }
KoPathControlPointMoveCommand::KoPathControlPointMoveCommand( const KoPathPointData &pointData, const QPointF &offset, KoPathPoint::PointType pointType, KUndo2Command *parent) : KUndo2Command(parent) , m_pointData(pointData) , m_pointType(pointType) { Q_ASSERT(offset.x() < 1e14 && offset.y() < 1e14); KoPathShape * pathShape = m_pointData.pathShape; KoPathPoint * point = pathShape->pointByIndex(m_pointData.pointIndex); if (point) { m_offset = point->parent()->documentToShape(offset) - point->parent()->documentToShape(QPointF(0, 0)); } setText(i18nc("(qtundo-format)", "Move control point")); }
void KoPathControlPointMoveCommand::redo() { KUndo2Command::redo(); KoPathShape * pathShape = m_pointData.pathShape; KoPathPoint * point = pathShape->pointByIndex(m_pointData.pointIndex); if (point) { pathShape->update(); if (m_pointType == KoPathPoint::ControlPoint1) { point->setControlPoint1(point->controlPoint1() + m_offset); if (point->properties() & KoPathPoint::IsSymmetric) { // set the other control point so that it lies on the line between the moved // control point and the point, with the same distance to the point as the moved point point->setControlPoint2(2.0 * point->point() - point->controlPoint1()); } else if (point->properties() & KoPathPoint::IsSmooth) { // move the other control point so that it lies on the line through point and control point // keeping its distance to the point QPointF direction = point->point() - point->controlPoint1(); direction /= sqrt(direction.x() * direction.x() + direction.y() * direction.y()); QPointF distance = point->point() - point->controlPoint2(); qreal length = sqrt(distance.x() * distance.x() + distance.y() * distance.y()); point->setControlPoint2(point->point() + length * direction); } } else if (m_pointType == KoPathPoint::ControlPoint2) { point->setControlPoint2(point->controlPoint2() + m_offset); if (point->properties() & KoPathPoint::IsSymmetric) { // set the other control point so that it lies on the line between the moved // control point and the point, with the same distance to the point as the moved point point->setControlPoint1(2.0 * point->point() - point->controlPoint2()); } else if (point->properties() & KoPathPoint::IsSmooth) { // move the other control point so that it lies on the line through point and control point // keeping its distance to the point QPointF direction = point->point() - point->controlPoint2(); direction /= sqrt(direction.x() * direction.x() + direction.y() * direction.y()); QPointF distance = point->point() - point->controlPoint1(); qreal length = sqrt(distance.x() * distance.x() + distance.y() * distance.y()); point->setControlPoint1(point->point() + length * direction); } } pathShape->normalize(); pathShape->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 ¤t = 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 KoSubpathJoinCommand::redo() { KUndo2Command::redo(); KoPathShape * pathShape = m_pointData1.pathShape; bool closeSubpath = m_pointData1.pointIndex.first == m_pointData2.pointIndex.first; KoPathPoint * point1 = pathShape->pointByIndex(m_pointData1.pointIndex); KoPathPoint * point2 = pathShape->pointByIndex(m_pointData2.pointIndex); // if the endpoint is has a control point create a contol point for the new segment to be // at the symetric position to the exiting one if (m_reverse & ReverseFirst || closeSubpath) { if (point1->activeControlPoint2()) point1->setControlPoint1(2.0 * point1->point() - point1->controlPoint2()); } else if (point1->activeControlPoint1()) point1->setControlPoint2(2.0 * point1->point() - point1->controlPoint1()); if (m_reverse & ReverseSecond || closeSubpath) { if (point2->activeControlPoint1()) point2->setControlPoint2(2.0 * point2->point() - point2->controlPoint1()); } else if (point2->activeControlPoint2()) point2->setControlPoint1(2.0 * point2->point() - point2->controlPoint2()); if (closeSubpath) { pathShape->closeSubpath(m_pointData1.pointIndex); } else { if (m_reverse & ReverseFirst) { pathShape->reverseSubpath(m_pointData1.pointIndex.first); } if (m_reverse & ReverseSecond) { pathShape->reverseSubpath(m_pointData2.pointIndex.first); } pathShape->moveSubpath(m_pointData2.pointIndex.first, m_pointData1.pointIndex.first + 1); m_splitIndex = m_pointData1.pointIndex; m_splitIndex.second = pathShape->subpathPointCount(m_pointData1.pointIndex.first) - 1; pathShape->join(m_pointData1.pointIndex.first); } pathShape->normalize(); pathShape->update(); }
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(); }
void KoSubpathJoinCommand::undo() { KUndo2Command::undo(); KoPathShape * pathShape = m_pointData1.pathShape; pathShape->update(); if (m_pointData1.pointIndex.first == m_pointData2.pointIndex.first) { pathShape->openSubpath(m_pointData1.pointIndex); } else { pathShape->breakAfter(m_splitIndex); pathShape->moveSubpath(m_pointData1.pointIndex.first + 1, m_pointData2.pointIndex.first); if (m_reverse & ReverseSecond) { pathShape->reverseSubpath(m_pointData2.pointIndex.first); } if (m_reverse & ReverseFirst) { pathShape->reverseSubpath(m_pointData1.pointIndex.first); } } KoPathPoint * point1 = pathShape->pointByIndex(m_pointData1.pointIndex); KoPathPoint * point2 = pathShape->pointByIndex(m_pointData2.pointIndex); // restore the old end points if (m_reverse & ReverseFirst) point1->setControlPoint1(pathShape->documentToShape(m_oldControlPoint1)); else point1->setControlPoint2(pathShape->documentToShape(m_oldControlPoint1)); point1->setProperties(m_oldProperties1); if (m_reverse & ReverseSecond) point2->setControlPoint1(pathShape->documentToShape(m_oldControlPoint2)); else point2->setControlPoint2(pathShape->documentToShape(m_oldControlPoint2)); point2->setProperties(m_oldProperties2); pathShape->normalize(); pathShape->update(); }
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); }