Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tvectorgl.h"
Toshihiro Shimizu 890ddd
#include "tgl.h"
Toshihiro Shimizu 890ddd
#include "tpalette.h"
Toshihiro Shimizu 890ddd
#include "tproperty.h"
Toshihiro Shimizu 890ddd
#include "tthreadmessage.h"
Toshihiro Shimizu 890ddd
#include "tvectorimage.h"
Toshihiro Shimizu 890ddd
#include "drawutil.h"
Toshihiro Shimizu 890ddd
#include "tcurveutil.h"
Toshihiro Shimizu 890ddd
#include "tstroke.h"
Toshihiro Shimizu 890ddd
#include "tstrokeutil.h"
Toshihiro Shimizu 890ddd
#include "tvectorrenderdata.h"
Toshihiro Shimizu 890ddd
#include "tstrokedeformations.h"
Toshihiro Shimizu 890ddd
#include "tmathutil.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//Toonz includes
Toshihiro Shimizu 890ddd
#include "toonz/tobjecthandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshlevelhandle.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//TnzTools includes
Toshihiro Shimizu 890ddd
#include "tools/tool.h"
Toshihiro Shimizu 890ddd
#include "tools/toolutils.h"
Toshihiro Shimizu 890ddd
#include "tools/cursors.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//Qt includes
Toshihiro Shimizu 890ddd
#include <qcoreapplication> // For Qt translation support</qcoreapplication>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace ToolUtils;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
//    PumpTool declaration
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
class PumpTool : public TTool
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	Q_DECLARE_TR_FUNCTIONS(PumpTool)
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int m_strokeStyleId, m_strokeIndex; //!< Edited stroke indices
Toshihiro Shimizu 890ddd
	TStroke *m_inStroke, *m_outStroke;  //!< Input/Output strokes
Shinya Kitaoka 3bfa54
	std::vector<tstroke *=""> m_splitStrokes;   //!< Merging these, m_inStroke is reformed</tstroke>
Toshihiro Shimizu 890ddd
	int m_stroke1Idx, m_stroke2Idx;		//!< Indices of deformed strokes among split ones
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TUndo *m_undo; //!< Undo to be added upon non-trivial button up
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double m_actionW;			   //!< The action center stroke parameter
Toshihiro Shimizu 890ddd
	double m_actionS1, m_actionS2; //!< Action center length in m_stroke
Toshihiro Shimizu 890ddd
	double m_actionRadius;		   //!< Tool action radius in curve length
Toshihiro Shimizu 890ddd
Shinya Kitaoka 3bfa54
	std::vector<double> m_splitPars;				   //!< Split parameters for action localization</double>
Shinya Kitaoka 3bfa54
	std::vector<double> m_cpLenDiff1, m_cpLenDiff2; //!< Distorted CPs' length distances from action center</double>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	bool m_active;		  //!< Whether a stroke is currently being edited
Toshihiro Shimizu 890ddd
	bool m_enabled;		  //!< Tells whether the image allows editing
Toshihiro Shimizu 890ddd
	bool m_cursorEnabled; //!< Whether the 'pump preview cursor' can be seen
Toshihiro Shimizu 890ddd
	bool m_draw;		  //!< Should be removed...?
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TPointD m_oldPoint, m_downPoint; //!< Mouse positions upon editing
Toshihiro Shimizu 890ddd
	TThickPoint m_cursor;			 //!< Pump preview cursor data
Toshihiro Shimizu 890ddd
	int m_cursorId;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double m_errorTol; //!< Allowed approximation error during edit
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TDoubleProperty m_toolSize;
Toshihiro Shimizu 890ddd
	TIntProperty m_accuracy;
Toshihiro Shimizu 890ddd
	TPropertyGroup m_prop;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	PumpTool()
Toshihiro Shimizu 890ddd
		: TTool("T_Pump"), m_active(false), m_actionW(0), m_strokeIndex((std::numeric_limits<uint>::max)()), m_inStroke(0), m_outStroke(0), m_stroke1Idx(-1), m_stroke2Idx(-1), m_cursorEnabled(false), m_cursorId(ToolCursor::PumpCursor), m_actionRadius(1), m_draw(false), m_undo(0), m_toolSize("Size:", 1, 100, 20), m_accuracy("Accuracy:", 0, 100, 40), m_enabled(false)</uint>
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		bind(TTool::VectorImage);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_splitPars.resize(2);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_prop.bind(m_toolSize);
Toshihiro Shimizu 890ddd
		m_prop.bind(m_accuracy);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	ToolType getToolType() const { return TTool::LevelWriteTool; }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TPropertyGroup *getProperties(int targetType) { return &m_prop; }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void updateTranslation()
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		m_toolSize.setQStringName(tr("Size:"));
Toshihiro Shimizu 890ddd
		m_accuracy.setQStringName(tr("Accuracy:"));
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void onEnter();
Toshihiro Shimizu 890ddd
	void onLeave();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void draw();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void leftButtonDown(const TPointD &pos, const TMouseEvent &e);
Toshihiro Shimizu 890ddd
	void leftButtonDrag(const TPointD &pos, const TMouseEvent &e);
Toshihiro Shimizu 890ddd
	void leftButtonUp(const TPointD &pos, const TMouseEvent &e);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void mouseMove(const TPointD &pos, const TMouseEvent &e);
Toshihiro Shimizu 890ddd
	bool moveCursor(const TPointD &pos);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int getCursorId() const { return m_cursorId; }
Toshihiro Shimizu 890ddd
	void invalidateCursorArea();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void onDeactivate();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
private:
Toshihiro Shimizu 890ddd
	double actionRadius(double strokeLength);
Toshihiro Shimizu 890ddd
	void splitStroke(TStroke *s);
Toshihiro Shimizu 890ddd
	TStroke *mergeStrokes(const std::vector<tstroke *=""> &strokes);</tstroke>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} PumpToolInstance;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
//    PumpTool implementation
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::onEnter()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_draw = true;
Toshihiro Shimizu 890ddd
	if (TTool::getApplication()->getCurrentObject()->isSpline() ||
Toshihiro Shimizu 890ddd
		!(TVectorImageP)getImage(false)) {
Toshihiro Shimizu 890ddd
		m_enabled = false;
Toshihiro Shimizu 890ddd
		m_cursorId = ToolCursor::CURSOR_NO;
Toshihiro Shimizu 890ddd
	} else {
Toshihiro Shimizu 890ddd
		m_enabled = true;
Toshihiro Shimizu 890ddd
		m_cursorId = ToolCursor::PumpCursor;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::draw()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (!m_draw || !m_enabled)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TVectorImageP vi = TImageP(getImage(false));
Toshihiro Shimizu 890ddd
	if (!vi)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TPalette *palette = vi->getPalette();
Toshihiro Shimizu 890ddd
	assert(palette);
Toshihiro Shimizu 890ddd
	if (m_active) {
Toshihiro Shimizu 890ddd
		//Editing with the tool
Toshihiro Shimizu 890ddd
		assert(m_outStroke);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TRectD bboxD(m_outStroke->getBBox());
Toshihiro Shimizu 890ddd
		TRect bbox(tfloor(bboxD.x0), tfloor(bboxD.y0), tceil(bboxD.x1) - 1, tceil(bboxD.y1) - 1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		tglDraw(TVectorRenderData(TAffine(), bbox, palette, 0, true), m_outStroke);
Toshihiro Shimizu 890ddd
	} else {
Toshihiro Shimizu 890ddd
		//Hovering
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		double w, dist;
Toshihiro Shimizu 890ddd
		UINT index;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (m_cursorEnabled) {
Toshihiro Shimizu 890ddd
			//Draw cursor
Toshihiro Shimizu 890ddd
			glColor3d(1.0, 0.0, 1.0);
Toshihiro Shimizu 890ddd
			if (m_cursor.thick > 0)
Toshihiro Shimizu 890ddd
				tglDrawCircle(m_cursor, m_cursor.thick);
Toshihiro Shimizu 890ddd
			tglDrawCircle(m_cursor, m_cursor.thick + 4 * getPixelSize());
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (vi->getNearestStroke(m_cursor, w, index, dist, true)) {
Toshihiro Shimizu 890ddd
			TStroke *stroke = vi->getStroke(index);
Toshihiro Shimizu 890ddd
			double totalLen = stroke->getLength();
Toshihiro Shimizu 890ddd
			double actionLen = actionRadius(totalLen);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			tglColor(TPixel32::Red);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			if (totalLen < actionLen || (stroke->isSelfLoop() && totalLen < actionLen + actionLen))
Toshihiro Shimizu 890ddd
				drawStrokeCenterline(*stroke, getPixelSize());
Toshihiro Shimizu 890ddd
			else {
Toshihiro Shimizu 890ddd
				int i, chunckIndex1, chunckIndex2;
Toshihiro Shimizu 890ddd
				double t, t1, t2, w1, w2;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				double len = stroke->getLength(w);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				double len1 = len - actionLen;
Toshihiro Shimizu 890ddd
				if (len1 < 0)
Toshihiro Shimizu 890ddd
					if (stroke->isSelfLoop())
Toshihiro Shimizu 890ddd
						len1 += totalLen;
Toshihiro Shimizu 890ddd
					else
Toshihiro Shimizu 890ddd
						len1 = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				double len2 = len + actionLen;
Toshihiro Shimizu 890ddd
				if (len2 > totalLen)
Toshihiro Shimizu 890ddd
					if (stroke->isSelfLoop())
Toshihiro Shimizu 890ddd
						len2 -= totalLen;
Toshihiro Shimizu 890ddd
					else
Toshihiro Shimizu 890ddd
						len2 = totalLen;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				w1 = stroke->getParameterAtLength(len1);
Toshihiro Shimizu 890ddd
				w2 = stroke->getParameterAtLength(len2);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				int chunkCount = stroke->getChunkCount();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				stroke->getChunkAndT(w1, chunckIndex1, t1);
Toshihiro Shimizu 890ddd
				stroke->getChunkAndT(w2, chunckIndex2, t2);
Toshihiro Shimizu 890ddd
				double step;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				const TThickQuadratic *q = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				glBegin(GL_LINE_STRIP);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				q = stroke->getChunk(chunckIndex1);
Toshihiro Shimizu 890ddd
				step = computeStep(*q, getPixelSize());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				if (chunckIndex1 == chunckIndex2 && t1 < t2) {
Toshihiro Shimizu 890ddd
					for (t = t1; t < t2; t += step)
Toshihiro Shimizu 890ddd
						tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
					tglVertex(stroke->getPoint(w2));
Toshihiro Shimizu 890ddd
					glEnd();
Toshihiro Shimizu 890ddd
					return;
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				for (t = t1; t < 1; t += step)
Toshihiro Shimizu 890ddd
					tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				for (i = chunckIndex1 + 1; i != chunckIndex2; i++) {
Toshihiro Shimizu 890ddd
					if (i == chunkCount)
Toshihiro Shimizu 890ddd
						i = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
					if (i == chunckIndex2)
Toshihiro Shimizu 890ddd
						break;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
					q = stroke->getChunk(i);
Toshihiro Shimizu 890ddd
					step = computeStep(*q, getPixelSize());
Toshihiro Shimizu 890ddd
					for (t = 0; t < 1; t += step)
Toshihiro Shimizu 890ddd
						tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				q = stroke->getChunk(chunckIndex2);
Toshihiro Shimizu 890ddd
				step = computeStep(*q, getPixelSize());
Toshihiro Shimizu 890ddd
				for (t = 0; t < t2; t += step)
Toshihiro Shimizu 890ddd
					tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				tglVertex(stroke->getPoint(w2));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				glEnd();
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::leftButtonDown(const TPointD &pos, const TMouseEvent &)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (m_active || !m_enabled)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	assert(m_undo == 0);
Toshihiro Shimizu 890ddd
	m_active = false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TVectorImageP vi(getImage(true));
Toshihiro Shimizu 890ddd
	if (!vi)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// set current point and init parameters
Toshihiro Shimizu 890ddd
	m_oldPoint = pos;
Toshihiro Shimizu 890ddd
	m_downPoint = pos;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_inStroke = m_outStroke = 0;
Toshihiro Shimizu 890ddd
	m_stroke1Idx = m_stroke2Idx = -1;
Toshihiro Shimizu 890ddd
	m_splitPars[0] = m_splitPars[1] = -2;
Toshihiro Shimizu 890ddd
	m_actionW = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_errorTol = (1.0 - 0.01 * m_accuracy.getValue()) * getPixelSize();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double dist2 = 0.0;
Toshihiro Shimizu 890ddd
	int cpCount;
Toshihiro Shimizu 890ddd
	int i;
Toshihiro Shimizu 890ddd
	UINT index;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (vi->getNearestStroke(pos, m_actionW, index, dist2)) {
Toshihiro Shimizu 890ddd
		//A stroke near the pressed point was found - modify it
Toshihiro Shimizu 890ddd
		m_active = true;
Toshihiro Shimizu 890ddd
		m_strokeIndex = index;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_inStroke = vi->getStroke(m_strokeIndex);
Toshihiro Shimizu 890ddd
		m_outStroke = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		double totalLength = m_inStroke->getLength();
Toshihiro Shimizu 890ddd
		TXshSimpleLevel *sl = TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
Toshihiro Shimizu 890ddd
		assert(sl);
Toshihiro Shimizu 890ddd
		TFrameId id = getCurrentFid();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		//Allocate the modification undo - will be assigned to the undo manager on mouse release
Toshihiro Shimizu 890ddd
		m_undo = new UndoModifyStrokeAndPaint(sl, id, m_strokeIndex);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		//Set the stroke's style to 'none'. This is needed to make the original stroke transparent,
Toshihiro Shimizu 890ddd
		//while the deformed one is shown at its place.
Toshihiro Shimizu 890ddd
		m_strokeStyleId = m_inStroke->getStyle();
Toshihiro Shimizu 890ddd
		m_inStroke->setStyle(0);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (totalLength <= 0.0) {
Toshihiro Shimizu 890ddd
			//Single point case
Toshihiro Shimizu 890ddd
			cpCount = m_inStroke->getControlPointCount();
Toshihiro Shimizu 890ddd
			m_cpLenDiff1.resize(cpCount);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			for (i = 0; i < cpCount; i++)
Toshihiro Shimizu 890ddd
				m_cpLenDiff1[i] = 0.0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_splitStrokes.resize(1);
Toshihiro Shimizu 890ddd
			m_splitStrokes[0] = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_stroke1Idx = 0;
Toshihiro Shimizu 890ddd
		} else
Toshihiro Shimizu 890ddd
			//Common strokes - split the stroke according to deformation requirements
Toshihiro Shimizu 890ddd
			splitStroke(m_inStroke);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (!m_active || !m_enabled)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TVectorImageP vi(getImage(true));
Toshihiro Shimizu 890ddd
	if (!vi || !m_outStroke)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Revert current deformation, recovering the one from button press
Toshihiro Shimizu 890ddd
	delete m_outStroke;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Retrieve cursor's vertical displacement
Toshihiro Shimizu 890ddd
	TPointD delta = TPointD(0, (pos - m_downPoint).y);
Toshihiro Shimizu 890ddd
	int deltaSign = tsign(delta.y);
Toshihiro Shimizu 890ddd
	if (deltaSign == 0) {
Toshihiro Shimizu 890ddd
		//Use a copy of the original stroke
Toshihiro Shimizu 890ddd
		m_outStroke = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
		m_outStroke->setStyle(m_strokeStyleId);
Toshihiro Shimizu 890ddd
		invalidate();
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Build deformation upon the original stroke pieces
Toshihiro Shimizu 890ddd
	TStroke *stroke1 = 0, *stroke2 = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	stroke1 = new TStroke(*m_splitStrokes[m_stroke1Idx]);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Deform stroke1
Toshihiro Shimizu 890ddd
	TStrokeThicknessDeformation deformer(stroke1, delta, m_actionS1, m_actionRadius, deltaSign);
Toshihiro Shimizu 890ddd
	modifyThickness(*stroke1, deformer, m_cpLenDiff1, deltaSign < 0);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (m_stroke2Idx >= 0) {
Toshihiro Shimizu 890ddd
		//Deform stroke2
Toshihiro Shimizu 890ddd
		stroke2 = new TStroke(*m_splitStrokes[m_stroke2Idx]);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TStrokeThicknessDeformation deformer2(stroke2, delta, m_actionS2, m_actionRadius, deltaSign);
Toshihiro Shimizu 890ddd
		modifyThickness(*stroke2, deformer2, m_cpLenDiff2, deltaSign < 0);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Apply deformation
Toshihiro Shimizu 890ddd
	std::vector<tstroke *=""> splitStrokesCopy(m_splitStrokes);</tstroke>
Toshihiro Shimizu 890ddd
	splitStrokesCopy[m_stroke1Idx] = stroke1;
Toshihiro Shimizu 890ddd
	if (stroke2)
Toshihiro Shimizu 890ddd
		splitStrokesCopy[m_stroke2Idx] = stroke2;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_outStroke = mergeStrokes(splitStrokesCopy);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	delete stroke1;
Toshihiro Shimizu 890ddd
	delete stroke2;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::leftButtonUp(const TPointD &pos, const TMouseEvent &)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TVectorImageP vi;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!m_active || !m_enabled)
Toshihiro Shimizu 890ddd
		goto cleanup;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	vi = TVectorImageP(getImage(true));
Toshihiro Shimizu 890ddd
	if (!vi)
Toshihiro Shimizu 890ddd
		goto cleanup;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		m_active = false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		//Reset cursor data
Toshihiro Shimizu 890ddd
		double t;
Toshihiro Shimizu 890ddd
		UINT index;
Toshihiro Shimizu 890ddd
		double dist2;
Toshihiro Shimizu 890ddd
		if (vi->getNearestStroke(pos, t, index, dist2)) {
Toshihiro Shimizu 890ddd
			TStroke *nearestStroke = vi->getStroke(index);
Toshihiro Shimizu 890ddd
			if (nearestStroke)
Toshihiro Shimizu 890ddd
				m_cursor = nearestStroke->getThickPoint(t);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (m_outStroke &&
Toshihiro Shimizu 890ddd
			!areAlmostEqual(m_downPoint, pos, PickRadius * getPixelSize())) {
Toshihiro Shimizu 890ddd
			//Accept action
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			//Clone input stroke - it is someway needed by the stroke change notifier... I wonder why...
Toshihiro Shimizu 890ddd
			TStroke *oldStroke = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_outStroke->swap(*m_inStroke);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_inStroke->invalidate();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			delete m_outStroke;
Toshihiro Shimizu 890ddd
			m_outStroke = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			assert(m_undo);
Toshihiro Shimizu 890ddd
			TUndoManager::manager()->add(m_undo);
Toshihiro Shimizu 890ddd
			m_undo = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			vi->notifyChangedStrokes(m_strokeIndex, oldStroke);
Toshihiro Shimizu 890ddd
			notifyImageChanged();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			delete oldStroke;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
cleanup:
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (m_inStroke)
Toshihiro Shimizu 890ddd
		m_inStroke->setStyle(m_strokeStyleId); //Make the image stroke visible again
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_strokeIndex = m_strokeStyleId = -1;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	clearPointerContainer(m_splitStrokes);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	delete m_outStroke;
Toshihiro Shimizu 890ddd
	m_inStroke = m_outStroke = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	delete m_undo;
Toshihiro Shimizu 890ddd
	m_undo = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::invalidateCursorArea()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	double r = m_cursor.thick + 6;
Toshihiro Shimizu 890ddd
	TPointD d(r, r);
Toshihiro Shimizu 890ddd
	invalidate(TRectD(m_cursor - d, m_cursor + d));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::mouseMove(const TPointD &pos, const TMouseEvent &e)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (m_active || !m_enabled)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Cursor preview updates on 3-pixel steps
Toshihiro Shimizu 890ddd
	if (tdistance2(pos, m_oldPoint) < 9.0 * sq(getPixelSize()))
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!m_draw)
Toshihiro Shimizu 890ddd
		m_draw = true;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_oldPoint = pos;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (moveCursor(pos)) {
Toshihiro Shimizu 890ddd
		m_cursorEnabled = true;
Toshihiro Shimizu 890ddd
		invalidate();
Toshihiro Shimizu 890ddd
	} else
Toshihiro Shimizu 890ddd
		m_cursorEnabled = false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
bool PumpTool::moveCursor(const TPointD &pos)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TVectorImageP vi(getImage(false));
Toshihiro Shimizu 890ddd
	if (vi) {
Toshihiro Shimizu 890ddd
		double t;
Toshihiro Shimizu 890ddd
		UINT index;
Toshihiro Shimizu 890ddd
		double dist2;
Toshihiro Shimizu 890ddd
		if (vi->getNearestStroke(pos, t, index, dist2)) {
Toshihiro Shimizu 890ddd
			TStroke *stroke = vi->getStroke(index);
Toshihiro Shimizu 890ddd
			if (stroke) {
Toshihiro Shimizu 890ddd
				m_cursor = stroke->getThickPoint(t);
Toshihiro Shimizu 890ddd
				return true;
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::onDeactivate()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	m_draw = false;
Toshihiro Shimizu 890ddd
	if (m_active) {
Toshihiro Shimizu 890ddd
		m_active = false;
Toshihiro Shimizu 890ddd
		TVectorImageP vi(getImage(true));
Toshihiro Shimizu 890ddd
		assert(!!vi && m_outStroke);
Toshihiro Shimizu 890ddd
		if (!vi || !m_outStroke)
Toshihiro Shimizu 890ddd
			return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		clearPointerContainer(m_splitStrokes);
Toshihiro Shimizu 890ddd
		if (m_splitPars[0] == -1) {
Toshihiro Shimizu 890ddd
			delete m_outStroke;
Toshihiro Shimizu 890ddd
			m_outStroke = 0;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// restore previous style
Toshihiro Shimizu 890ddd
		assert(m_strokeIndex >= 0);
Toshihiro Shimizu 890ddd
		if (m_strokeIndex >= 0) {
Toshihiro Shimizu 890ddd
			TStroke *stroke = vi->getStroke(m_strokeIndex);
Toshihiro Shimizu 890ddd
			stroke->setStyle(m_strokeStyleId);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		assert(m_undo);
Toshihiro Shimizu 890ddd
		delete m_undo;
Toshihiro Shimizu 890ddd
		m_undo = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		invalidate();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_strokeIndex = -1;
Toshihiro Shimizu 890ddd
		m_outStroke = 0;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void PumpTool::onLeave()
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (!m_active)
Toshihiro Shimizu 890ddd
		m_draw = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
//    PumpTool privates
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
double PumpTool::actionRadius(double strokeLength)
Toshihiro Shimizu 890ddd
{
Shinya Kitaoka 12c444
	double toolSize = std::max(m_toolSize.getValue(), 5.0);
Toshihiro Shimizu 890ddd
	double toolPercent = toolSize * 0.01;
Toshihiro Shimizu 890ddd
	double interpolationVal = pow(toolPercent, 5);
Toshihiro Shimizu 890ddd
	double indipendentValue = 7.0 * toolSize;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double actionRadius = (indipendentValue) * (1.0 - interpolationVal) +
Toshihiro Shimizu 890ddd
						  (strokeLength * toolPercent) * interpolationVal;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 12c444
	return std::max(actionRadius, indipendentValue);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  Edited strokes are split near the corresponding editing position, in order
Toshihiro Shimizu 890ddd
  to localize stroke manipulation.
Toshihiro Shimizu 890ddd
  Only the localized part of the stroke will receive CP increase and thickness
Toshihiro Shimizu 890ddd
  tuning needed for the tool action.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
void PumpTool::splitStroke(TStroke *s)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	assert(m_splitStrokes.empty());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TStroke *stroke1 = 0, *stroke2 = 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Build the action radius
Toshihiro Shimizu 890ddd
	double totalLength = s->getLength();
Toshihiro Shimizu 890ddd
	m_actionRadius = actionRadius(totalLength);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Get the length at selected point and build the split (length) positions
Toshihiro Shimizu 890ddd
	m_actionS1 = s->getLength(m_actionW);
Toshihiro Shimizu 890ddd
	double startLen = m_actionS1 - m_actionRadius;
Toshihiro Shimizu 890ddd
	double endLen = m_actionS1 + m_actionRadius;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Now, perform splitting
Toshihiro Shimizu 890ddd
	int i, cpCount;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if ((startLen <= 0 && endLen >= totalLength) ||
Toshihiro Shimizu 890ddd
		(s->isSelfLoop() && totalLength < (m_actionRadius + m_actionRadius))) {
Toshihiro Shimizu 890ddd
		//The whole stroke is included in the action - no split
Toshihiro Shimizu 890ddd
		m_splitStrokes.resize(1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_splitPars[0] = -1;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_splitStrokes[0] = new TStroke(*s);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_stroke1Idx = 0;
Toshihiro Shimizu 890ddd
		stroke1 = m_splitStrokes[m_stroke1Idx];
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TStrokeThicknessDeformation deformer(s, m_actionS1, m_actionRadius);
Toshihiro Shimizu 890ddd
		increaseControlPoints(*stroke1, deformer, getPixelSize());
Toshihiro Shimizu 890ddd
	} else {
Toshihiro Shimizu 890ddd
		if (!s->isSelfLoop() || (startLen >= 0.0 && endLen <= totalLength)) {
Toshihiro Shimizu 890ddd
			//Regular split positions, in the [0.0, totalLength] range.
Toshihiro Shimizu 890ddd
			//Split points at extremities are dealt.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 12c444
			m_splitPars[0] = s->getParameterAtLength(std::max(startLen, 0.0)); //Crop in the open case
Shinya Kitaoka 12c444
			m_splitPars[1] = s->getParameterAtLength(std::min(endLen, totalLength));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			if (m_splitPars[0] == 0.0) // the "&& m_splitPars[0] == totalLength" was dealt outside
Toshihiro Shimizu 890ddd
			{
Toshihiro Shimizu 890ddd
				m_splitStrokes.resize(2);
Toshihiro Shimizu 890ddd
				m_splitStrokes[0] = new TStroke;
Toshihiro Shimizu 890ddd
				m_splitStrokes[1] = new TStroke;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				s->split(m_splitPars[1], *(m_splitStrokes[0]), *(m_splitStrokes[1]));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				m_stroke1Idx = 0;
Toshihiro Shimizu 890ddd
			} else {
Toshihiro Shimizu 890ddd
				if (m_splitPars[1] == 1.0) {
Toshihiro Shimizu 890ddd
					m_splitStrokes.resize(2);
Toshihiro Shimizu 890ddd
					m_splitStrokes[0] = new TStroke;
Toshihiro Shimizu 890ddd
					m_splitStrokes[1] = new TStroke;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
					s->split(m_splitPars[0], *(m_splitStrokes[0]), *(m_splitStrokes[1]));
Toshihiro Shimizu 890ddd
				} else
Toshihiro Shimizu 890ddd
					::splitStroke(*s, m_splitPars, m_splitStrokes);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				m_stroke1Idx = 1;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
				//Update the edit point to refer to the central stroke piece
Toshihiro Shimizu 890ddd
				m_actionS1 -= m_splitStrokes[0]->getLength();
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			stroke1 = m_splitStrokes[m_stroke1Idx];
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			//Apply deformation to the middle piece
Toshihiro Shimizu 890ddd
			TStrokeThicknessDeformation deformer(stroke1, m_actionS1, m_actionRadius);
Toshihiro Shimizu 890ddd
			increaseControlPoints(*stroke1, deformer, getPixelSize());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_actionS2 = 0;
Toshihiro Shimizu 890ddd
		} else {
Toshihiro Shimizu 890ddd
			//Circular 'overflow' case - (exactly) one split point is outside the regular scope.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			//Since the action diameter is < totalLength, these cases are mutually exclusive.
Toshihiro Shimizu 890ddd
			if (startLen < 0)
Toshihiro Shimizu 890ddd
				startLen += totalLength;
Toshihiro Shimizu 890ddd
			else {
Toshihiro Shimizu 890ddd
				endLen -= totalLength;
Toshihiro Shimizu 890ddd
				m_actionS1 -= totalLength;
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			//The deformation must be applied in two distinct strokes, since its
Toshihiro Shimizu 890ddd
			//action interval crosses the junction point
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_splitPars[0] = s->getParameterAtLength(endLen);
Toshihiro Shimizu 890ddd
			m_splitPars[1] = s->getParameterAtLength(startLen);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			::splitStroke(*s, m_splitPars, m_splitStrokes);
Toshihiro Shimizu 890ddd
			assert(m_splitStrokes.size() >= 3);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_stroke1Idx = 0;
Toshihiro Shimizu 890ddd
			m_stroke2Idx = 2;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			stroke1 = m_splitStrokes[m_stroke1Idx];
Toshihiro Shimizu 890ddd
			stroke2 = m_splitStrokes[m_stroke2Idx];
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			m_actionS2 = m_actionS1 + stroke2->getLength();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			TStrokeThicknessDeformation deformer(stroke1, m_actionS1, m_actionRadius);
Toshihiro Shimizu 890ddd
			increaseControlPoints(*stroke1, deformer, getPixelSize());
Toshihiro Shimizu 890ddd
			TStrokeThicknessDeformation deformer2(stroke2, m_actionS2, m_actionRadius);
Toshihiro Shimizu 890ddd
			increaseControlPoints(*stroke2, deformer2, getPixelSize());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			cpCount = stroke2->getControlPointCount();
Toshihiro Shimizu 890ddd
			m_cpLenDiff2.resize(cpCount);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			for (i = 0; i < cpCount; ++i)
Toshihiro Shimizu 890ddd
				m_cpLenDiff2[i] = stroke2->getLengthAtControlPoint(i) - m_actionS2;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	cpCount = stroke1->getControlPointCount();
Toshihiro Shimizu 890ddd
	m_cpLenDiff1.resize(cpCount);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double diff;
Toshihiro Shimizu 890ddd
	for (i = 0; i < cpCount; i++) {
Toshihiro Shimizu 890ddd
		diff = stroke1->getLengthAtControlPoint(i) - m_actionS1;
Toshihiro Shimizu 890ddd
		m_cpLenDiff1[i] = (s->isSelfLoop() && stroke2 && totalLength - diff < diff)
Toshihiro Shimizu 890ddd
							  ? totalLength - diff
Toshihiro Shimizu 890ddd
							  : diff;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  A split stroke must be reassembled before it is output.
Toshihiro Shimizu 890ddd
  In particular, it must be ensured that the merge does not add additional CPS
Toshihiro Shimizu 890ddd
  at split points, leaving the output seamless.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
TStroke *PumpTool::mergeStrokes(const std::vector<tstroke *=""> &strokes)</tstroke>
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	assert(strokes.size() > 0);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TStroke *mergedStroke;
Toshihiro Shimizu 890ddd
	if (strokes.size() > 1) {
Toshihiro Shimizu 890ddd
		if (m_errorTol > 0.0) {
Toshihiro Shimizu 890ddd
			strokes[m_stroke1Idx]->reduceControlPoints(m_errorTol);
Toshihiro Shimizu 890ddd
			if (m_stroke2Idx >= 0)
Toshihiro Shimizu 890ddd
				strokes[m_stroke2Idx]->reduceControlPoints(m_errorTol);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		//Merge split strokes
Toshihiro Shimizu 890ddd
		mergedStroke = merge(strokes);
Toshihiro Shimizu 890ddd
		//mergedStroke->reduceControlPoints(0.4*getPixelSize());    //Originally on the whole result...
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		if (m_inStroke->isSelfLoop()) {
Toshihiro Shimizu 890ddd
			int cpCount = mergedStroke->getControlPointCount();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			TThickPoint p1 = mergedStroke->getControlPoint(0);
Toshihiro Shimizu 890ddd
			TThickPoint p2 = mergedStroke->getControlPoint(cpCount - 1);
Toshihiro Shimizu 890ddd
			TThickPoint midP = 0.5 * (p1 + p2);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			mergedStroke->setControlPoint(0, midP);
Toshihiro Shimizu 890ddd
			mergedStroke->setControlPoint(cpCount - 1, midP);
Toshihiro Shimizu 890ddd
			mergedStroke->setSelfLoop(true);
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		mergedStroke->outlineOptions() = strokes[0]->outlineOptions();
Toshihiro Shimizu 890ddd
	} else {
Toshihiro Shimizu 890ddd
		mergedStroke = new TStroke(*strokes[0]);
Toshihiro Shimizu 890ddd
		if (m_errorTol > 0.0)
Toshihiro Shimizu 890ddd
			mergedStroke->reduceControlPoints(m_errorTol);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	mergedStroke->setStyle(m_strokeStyleId);
Toshihiro Shimizu 890ddd
	mergedStroke->invalidate();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return mergedStroke;
Toshihiro Shimizu 890ddd
}