File indexing completed on 2024-06-09 04:20:44
0001 /* This file is part of the KDE project 0002 * SPDX-FileCopyrightText: 2006, 2008 Jan Hambrecht <jaham@gmx.net> 0003 * SPDX-FileCopyrightText: 2006, 2007 Thorsten Zachmann <zachmann@kde.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "KoPathPointTypeCommand.h" 0009 0010 #include <klocalizedstring.h> 0011 #include <math.h> 0012 0013 #include "KoPathSegment.h" 0014 0015 KoPathPointTypeCommand::KoPathPointTypeCommand( 0016 const QList<KoPathPointData> & pointDataList, 0017 PointType pointType, 0018 KUndo2Command *parent) 0019 : KoPathBaseCommand(parent) 0020 , m_pointType(pointType) 0021 { 0022 QList<KoPathPointData>::const_iterator it(pointDataList.begin()); 0023 for (; it != pointDataList.end(); ++it) { 0024 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex); 0025 if (point) { 0026 PointData pointData(*it); 0027 pointData.m_oldControlPoint1 = it->pathShape->shapeToDocument(point->controlPoint1()); 0028 pointData.m_oldControlPoint2 = it->pathShape->shapeToDocument(point->controlPoint2()); 0029 pointData.m_oldProperties = point->properties(); 0030 pointData.m_hadControlPoint1 = point->activeControlPoint1(); 0031 pointData.m_hadControlPoint2 = point->activeControlPoint2(); 0032 m_oldPointData.append(pointData); 0033 m_shapes.insert(it->pathShape); 0034 } 0035 } 0036 setText(kundo2_i18n("Set point type")); 0037 } 0038 0039 KoPathPointTypeCommand::~KoPathPointTypeCommand() 0040 { 0041 } 0042 0043 void KoPathPointTypeCommand::redo() 0044 { 0045 KUndo2Command::redo(); 0046 repaint(false); 0047 m_additionalPointData.clear(); 0048 0049 QList<PointData>::iterator it(m_oldPointData.begin()); 0050 for (; it != m_oldPointData.end(); ++it) { 0051 KoPathPoint *point = it->m_pointData.pathShape->pointByIndex(it->m_pointData.pointIndex); 0052 KoPathPoint::PointProperties properties = point->properties(); 0053 0054 switch (m_pointType) { 0055 case Line: { 0056 point->removeControlPoint1(); 0057 point->removeControlPoint2(); 0058 break; 0059 } 0060 case Curve: { 0061 KoPathPointIndex pointIndex = it->m_pointData.pointIndex; 0062 KoPathPointIndex prevIndex; 0063 KoPathPointIndex nextIndex; 0064 KoPathShape * path = it->m_pointData.pathShape; 0065 // get previous path node 0066 if (pointIndex.second > 0) 0067 prevIndex = KoPathPointIndex(pointIndex.first, pointIndex.second - 1); 0068 else if (pointIndex.second == 0 && path->isClosedSubpath(pointIndex.first)) 0069 prevIndex = KoPathPointIndex(pointIndex.first, path->subpathPointCount(pointIndex.first) - 1); 0070 // get next node 0071 if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1) 0072 nextIndex = KoPathPointIndex(pointIndex.first, pointIndex.second + 1); 0073 else if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1 0074 && path->isClosedSubpath(pointIndex.first)) 0075 nextIndex = KoPathPointIndex(pointIndex.first, 0); 0076 0077 KoPathPoint * prevPoint = path->pointByIndex(prevIndex); 0078 KoPathPoint * nextPoint = path->pointByIndex(nextIndex); 0079 0080 if (prevPoint && ! point->activeControlPoint1() && appendPointData(KoPathPointData(path, prevIndex))) { 0081 KoPathSegment cubic = KoPathSegment(prevPoint, point).toCubic(); 0082 if (prevPoint->activeControlPoint2()) { 0083 prevPoint->setControlPoint2(cubic.first()->controlPoint2()); 0084 point->setControlPoint1(cubic.second()->controlPoint1()); 0085 } else 0086 point->setControlPoint1(cubic.second()->controlPoint1()); 0087 } 0088 if (nextPoint && ! point->activeControlPoint2() && appendPointData(KoPathPointData(path, nextIndex))) { 0089 KoPathSegment cubic = KoPathSegment(point, nextPoint).toCubic(); 0090 if (nextPoint->activeControlPoint1()) { 0091 point->setControlPoint2(cubic.first()->controlPoint2()); 0092 nextPoint->setControlPoint1(cubic.second()->controlPoint1()); 0093 } else 0094 point->setControlPoint2(cubic.first()->controlPoint2()); 0095 } 0096 point->setProperties(properties); 0097 break; 0098 } 0099 case Symmetric: { 0100 properties &= ~KoPathPoint::IsSmooth; 0101 properties |= KoPathPoint::IsSymmetric; 0102 0103 // calculate vector from node point to first control point and normalize it 0104 QPointF directionC1 = point->controlPoint1() - point->point(); 0105 qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y()); 0106 directionC1 /= dirLengthC1; 0107 // calculate vector from node point to second control point and normalize it 0108 QPointF directionC2 = point->controlPoint2() - point->point(); 0109 qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y()); 0110 directionC2 /= dirLengthC2; 0111 // calculate the average distance of the control points to the node point 0112 qreal averageLength = 0.5 * (dirLengthC1 + dirLengthC2); 0113 // compute position of the control points so that they lie on a line going through the node point 0114 // the new distance of the control points is the average distance to the node point 0115 point->setControlPoint1(point->point() + 0.5 * averageLength * (directionC1 - directionC2)); 0116 point->setControlPoint2(point->point() + 0.5 * averageLength * (directionC2 - directionC1)); 0117 point->setProperties(properties); 0118 } 0119 break; 0120 case Smooth: { 0121 makeCubicPointSmooth(point); 0122 } 0123 break; 0124 case Corner: 0125 default: 0126 properties &= ~KoPathPoint::IsSymmetric; 0127 properties &= ~KoPathPoint::IsSmooth; 0128 point->setProperties(properties); 0129 break; 0130 } 0131 } 0132 repaint(true); 0133 } 0134 0135 void KoPathPointTypeCommand::undo() 0136 { 0137 KUndo2Command::undo(); 0138 repaint(false); 0139 0140 /* 0141 QList<PointData>::iterator it(m_oldPointData.begin()); 0142 for (; it != m_oldPointData.end(); ++it) 0143 { 0144 KoPathShape *pathShape = it->m_pointData.pathShape; 0145 KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex); 0146 0147 point->setProperties(it->m_oldProperties); 0148 if (it->m_hadControlPoint1) 0149 point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1)); 0150 else 0151 point->removeControlPoint1(); 0152 if (it->m_hadControlPoint2) 0153 point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2)); 0154 else 0155 point->removeControlPoint2(); 0156 } 0157 */ 0158 undoChanges(m_oldPointData); 0159 undoChanges(m_additionalPointData); 0160 0161 repaint(true); 0162 } 0163 0164 void KoPathPointTypeCommand::makeCubicPointSmooth(KoPathPoint *point) 0165 { 0166 KoPathPoint::PointProperties properties = point->properties(); 0167 0168 properties &= ~KoPathPoint::IsSymmetric; 0169 properties |= KoPathPoint::IsSmooth; 0170 0171 // calculate vector from node point to first control point and normalize it 0172 QPointF directionC1 = point->controlPoint1() - point->point(); 0173 qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y()); 0174 directionC1 /= dirLengthC1; 0175 // calculate vector from node point to second control point and normalize it 0176 QPointF directionC2 = point->controlPoint2() - point->point(); 0177 qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y()); 0178 directionC2 /= dirLengthC2; 0179 // compute position of the control points so that they lie on a line going through the node point 0180 // the new distance of the control points is the average distance to the node point 0181 point->setControlPoint1(point->point() + 0.5 * dirLengthC1 * (directionC1 - directionC2)); 0182 point->setControlPoint2(point->point() + 0.5 * dirLengthC2 * (directionC2 - directionC1)); 0183 0184 point->setProperties(properties); 0185 } 0186 0187 void KoPathPointTypeCommand::undoChanges(const QList<PointData> &data) 0188 { 0189 QList<PointData>::const_iterator it(data.begin()); 0190 for (; it != data.end(); ++it) { 0191 KoPathShape *pathShape = it->m_pointData.pathShape; 0192 KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex); 0193 0194 point->setProperties(it->m_oldProperties); 0195 if (it->m_hadControlPoint1) 0196 point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1)); 0197 else 0198 point->removeControlPoint1(); 0199 if (it->m_hadControlPoint2) 0200 point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2)); 0201 else 0202 point->removeControlPoint2(); 0203 } 0204 } 0205 0206 bool KoPathPointTypeCommand::appendPointData(KoPathPointData data) 0207 { 0208 KoPathPoint *point = data.pathShape->pointByIndex(data.pointIndex); 0209 if (! point) 0210 return false; 0211 0212 PointData pointData(data); 0213 pointData.m_oldControlPoint1 = data.pathShape->shapeToDocument(point->controlPoint1()); 0214 pointData.m_oldControlPoint2 = data.pathShape->shapeToDocument(point->controlPoint2()); 0215 pointData.m_oldProperties = point->properties(); 0216 pointData.m_hadControlPoint1 = point->activeControlPoint1(); 0217 pointData.m_hadControlPoint2 = point->activeControlPoint2(); 0218 0219 m_additionalPointData.append(pointData); 0220 0221 return true; 0222 }