File indexing completed on 2025-03-09 04:03:39

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006 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 "KoPathControlPointMoveCommand.h"
0009 #include <klocalizedstring.h>
0010 #include <math.h>
0011 #include "kis_command_ids.h"
0012 
0013 KoPathControlPointMoveCommand::KoPathControlPointMoveCommand(
0014     const KoPathPointData &pointData,
0015     const QPointF &offset,
0016     KoPathPoint::PointType pointType,
0017     KUndo2Command *parent)
0018         : KUndo2Command(parent)
0019         , m_pointData(pointData)
0020         , m_pointType(pointType)
0021 {
0022     Q_ASSERT(offset.x() < 1e14 && offset.y() < 1e14);
0023     KoPathShape * pathShape = m_pointData.pathShape;
0024     KoPathPoint * point = pathShape->pointByIndex(m_pointData.pointIndex);
0025     if (point) {
0026         m_offset = offset;
0027     }
0028 
0029     setText(kundo2_i18n("Move control point"));
0030 }
0031 
0032 void KoPathControlPointMoveCommand::redo()
0033 {
0034     KUndo2Command::redo();
0035     KoPathShape * pathShape = m_pointData.pathShape;
0036     KoPathPoint * point = pathShape->pointByIndex(m_pointData.pointIndex);
0037     if (point) {
0038         const QRectF oldDirtyRect = pathShape->boundingRect();
0039 
0040         if (m_pointType == KoPathPoint::ControlPoint1) {
0041             point->setControlPoint1(point->controlPoint1() + m_offset);
0042             if (point->properties() & KoPathPoint::IsSymmetric) {
0043                 // set the other control point so that it lies on the line between the moved
0044                 // control point and the point, with the same distance to the point as the moved point
0045                 point->setControlPoint2(2.0 * point->point() - point->controlPoint1());
0046             } else if (point->properties() & KoPathPoint::IsSmooth) {
0047                 // move the other control point so that it lies on the line through point and control point
0048                 // keeping its distance to the point
0049                 QPointF direction = point->point() - point->controlPoint1();
0050                 direction /= sqrt(direction.x() * direction.x() + direction.y() * direction.y());
0051                 QPointF distance = point->point() - point->controlPoint2();
0052                 qreal length = sqrt(distance.x() * distance.x() + distance.y() * distance.y());
0053                 point->setControlPoint2(point->point() + length * direction);
0054             }
0055         } else if (m_pointType == KoPathPoint::ControlPoint2) {
0056             point->setControlPoint2(point->controlPoint2() + m_offset);
0057             if (point->properties() & KoPathPoint::IsSymmetric) {
0058                 // set the other control point so that it lies on the line between the moved
0059                 // control point and the point, with the same distance to the point as the moved point
0060                 point->setControlPoint1(2.0 * point->point() - point->controlPoint2());
0061             } else if (point->properties() & KoPathPoint::IsSmooth) {
0062                 // move the other control point so that it lies on the line through point and control point
0063                 // keeping its distance to the point
0064                 QPointF direction = point->point() - point->controlPoint2();
0065                 direction /= sqrt(direction.x() * direction.x() + direction.y() * direction.y());
0066                 QPointF distance = point->point() - point->controlPoint1();
0067                 qreal length = sqrt(distance.x() * distance.x() + distance.y() * distance.y());
0068                 point->setControlPoint1(point->point() + length * direction);
0069             }
0070         }
0071 
0072         pathShape->normalize();
0073         pathShape->updateAbsolute(oldDirtyRect | pathShape->boundingRect());
0074     }
0075 }
0076 
0077 void KoPathControlPointMoveCommand::undo()
0078 {
0079     KUndo2Command::undo();
0080     m_offset *= -1.0;
0081     redo();
0082     m_offset *= -1.0;
0083 }
0084 
0085 int KoPathControlPointMoveCommand::id() const
0086 {
0087     return KisCommandUtils::ChangePathShapeControlPointId;
0088 }
0089 
0090 bool KoPathControlPointMoveCommand::mergeWith(const KUndo2Command *command)
0091 {
0092     const KoPathControlPointMoveCommand *other = dynamic_cast<const KoPathControlPointMoveCommand*>(command);
0093 
0094     if (!other ||
0095         other->m_pointData != m_pointData ||
0096         other->m_pointType != m_pointType) {
0097 
0098         return false;
0099     }
0100 
0101     m_offset += other->m_offset;
0102 
0103     return true;
0104 }