libs/flake

KoPathSegmentChangeStrategy.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  * Copyright (C) 2009 Jan Hambrecht <jaham@gmx.net>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this library; see the file COPYING.LIB.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  */
00019 
00020 #include "KoPathSegmentChangeStrategy.h"
00021 #include "KoPathShape.h"
00022 #include "KoPathPoint.h"
00023 #include "KoPathTool.h"
00024 #include "KoSnapGuide.h"
00025 #include "commands/KoPathControlPointMoveCommand.h"
00026 #include "commands/KoPathSegmentTypeCommand.h"
00027 #include <KoCanvasBase.h>
00028 #include <KLocale>
00029 
00030 KoPathSegmentChangeStrategy::KoPathSegmentChangeStrategy( KoPathTool *tool, KoCanvasBase *canvas, const QPointF &pos, const KoPathPointData &segment, qreal segmentParam)
00031 : KoInteractionStrategy(tool, canvas)
00032 , m_originalPosition(pos)
00033 , m_lastPosition(pos)
00034 , m_tool(tool)
00035 , m_segmentParam(segmentParam)
00036 , m_pointData1(segment)
00037 , m_pointData2(segment)
00038 {
00039     m_path = segment.pathShape;
00040     m_segment = m_path->segmentByIndex(segment.pointIndex);
00041     m_pointData2.pointIndex = m_path->pathPointIndex(m_segment.second());
00042     m_originalSegmentDegree = m_segment.degree();
00043 }
00044 
00045 KoPathSegmentChangeStrategy::~KoPathSegmentChangeStrategy()
00046 {
00047 }
00048 
00049 void KoPathSegmentChangeStrategy::handleMouseMove( const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
00050 {
00051     m_tool->canvas()->updateCanvas(m_tool->canvas()->snapGuide()->boundingRect());
00052     QPointF snappedPosition = m_tool->canvas()->snapGuide()->snap(mouseLocation, modifiers);
00053     m_tool->canvas()->updateCanvas(m_tool->canvas()->snapGuide()->boundingRect());
00054 
00055     QPointF localPos = m_path->documentToShape(snappedPosition);
00056 
00057     if (m_segment.degree() == 1) {
00058         // line segment is converted to a curve
00059         KoPathSegmentTypeCommand cmd(m_pointData1, KoPathSegmentTypeCommand::Curve);
00060         cmd.redo();
00061     }
00062 
00063     QPointF move1, move2;
00064 
00065     if (m_segment.degree() == 2) {
00066         // interpolate quadratic segment between segment start, mouse position and segment end
00067         KoPathSegment ipol = KoPathSegment::interpolate( m_segment.first()->point(),
00068                                                          localPos,
00069                                                          m_segment.second()->point(),
00070                                                          m_segmentParam );
00071         move1 = move2 = ipol.controlPoints()[1] - m_segment.controlPoints()[1];
00072     }
00073     else if (m_segment.degree() == 3) {
00074         /*
00075         * method from inkscape, original method and idea borrowed from Simon Budig
00076         * <simon@gimp.org> and the GIMP
00077         * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
00078         *
00079         * feel good is an arbitrary parameter that distributes the delta between handles
00080         * if t of the drag point is less than 1/6 distance form the endpoint only
00081         * the corresponding handle is adjusted. This matches the behavior in GIMP
00082         */
00083         const qreal t = m_segmentParam;
00084         qreal feel_good;
00085         if (t <= 1.0 / 6.0)
00086             feel_good = 0;
00087         else if (t <= 0.5)
00088             feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
00089         else if (t <= 5.0 / 6.0)
00090             feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
00091         else
00092             feel_good = 1;
00093 
00094         QPointF lastLocalPos = m_path->documentToShape(m_lastPosition);
00095         QPointF delta = localPos - lastLocalPos;
00096         move2 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
00097         move1 = (feel_good/(3*t*t*(1-t))) * delta;
00098     }
00099 
00100     m_path->update();
00101     if( m_segment.first()->activeControlPoint2() ) {
00102         KoPathControlPointMoveCommand cmd(m_pointData1, move2, KoPathPoint::ControlPoint2);
00103         cmd.redo();
00104     }
00105     if( m_segment.second()->activeControlPoint1() ) {
00106         KoPathControlPointMoveCommand cmd(m_pointData2, move1, KoPathPoint::ControlPoint1);
00107         cmd.redo();
00108     }
00109     m_path->normalize();
00110     m_path->update();
00111 
00112     m_ctrlPoint1Move += move1;
00113     m_ctrlPoint2Move += move2;
00114 
00115     // save last mouse position
00116     m_lastPosition = mouseLocation;
00117 }
00118 
00119 void KoPathSegmentChangeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
00120 {
00121     Q_UNUSED(modifiers);
00122 }
00123 
00124 QUndoCommand* KoPathSegmentChangeStrategy::createCommand()
00125 {
00126     m_tool->canvas()->updateCanvas(m_tool->canvas()->snapGuide()->boundingRect());
00127 
00128     bool hasControlPoint1 = m_segment.second()->activeControlPoint1();
00129     bool hasControlPoint2 = m_segment.first()->activeControlPoint2();
00130 
00131     QUndoCommand * cmd = new QUndoCommand(i18n("Change Segment"));
00132     if (m_originalSegmentDegree == 1) {
00133         m_segment.first()->removeControlPoint2();
00134         m_segment.second()->removeControlPoint1();
00135         new KoPathSegmentTypeCommand(m_pointData1, KoPathSegmentTypeCommand::Curve, cmd);
00136     }
00137 
00138     if (hasControlPoint2) {
00139         QPointF oldCtrlPointPos = m_segment.first()->controlPoint2()-m_ctrlPoint2Move;
00140         m_segment.first()->setControlPoint2(oldCtrlPointPos);
00141         new KoPathControlPointMoveCommand(m_pointData1, m_ctrlPoint2Move, KoPathPoint::ControlPoint2, cmd);
00142     }
00143     if (hasControlPoint1) {
00144         QPointF oldCtrlPointPos = m_segment.second()->controlPoint1()-m_ctrlPoint1Move;
00145         m_segment.second()->setControlPoint1(oldCtrlPointPos);
00146         new KoPathControlPointMoveCommand(m_pointData2, m_ctrlPoint1Move, KoPathPoint::ControlPoint1, cmd);
00147     }
00148 
00149     return cmd;
00150 }