Blob Blame Raw


#include "tools/tool.h"
#include "tools/toolutils.h"
#include "toonz/txsheethandle.h"
#include "tools/toolhandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/txshlevelhandle.h"
#include "tools/strokeselection.h"
#include "tools/tool.h"

#include "tmathutil.h"
#include "tstroke.h"
#include "tools/cursors.h"
#include "tundo.h"
#include "tvectorimage.h"
#include "tthreadmessage.h"

#include "toonzqt/imageutils.h"
#include "toonzqt/tselectionhandle.h"

#include "tgl.h"

using namespace ToolUtils;

//=============================================================================
namespace
{

//=============================================================================
// UndoCutter
//-----------------------------------------------------------------------------

class UndoCutter : public ToolUtils::TToolUndo
{
	int m_newStrokeId1;
	int m_newStrokeId2;
	int m_pos;

	VIStroke *m_oldStroke;

	vector<TFilledRegionInf> *m_fillInformation;
	vector<DoublePair> *m_sortedWRanges;

	int m_row;
	int m_column;

public:
	UndoCutter(TXshSimpleLevel *level,
			   const TFrameId &frameId,
			   VIStroke *oldStroke,
			   int pos,
			   int newStrokeId1,
			   int newStrokeId2,
			   vector<TFilledRegionInf> *fillInformation,
			   vector<DoublePair> *sortedWRanges)
		: TToolUndo(level, frameId), m_oldStroke(oldStroke), m_newStrokeId1(newStrokeId1), m_newStrokeId2(newStrokeId2), m_pos(pos), m_fillInformation(fillInformation), m_sortedWRanges(sortedWRanges)
	{
		TTool::Application *app = TTool::getApplication();
		if (app) {
			m_row = app->getCurrentFrame()->getFrame();
			m_column = app->getCurrentColumn()->getColumnIndex();
		}
	}

	~UndoCutter()
	{
		deleteVIStroke(m_oldStroke);
		delete m_sortedWRanges;
		delete m_fillInformation;
	}

	void undo() const
	{
		TTool::Application *app = TTool::getApplication();
		if (!app)
			return;
		if (dynamic_cast<StrokeSelection *>(TTool::getApplication()->getCurrentSelection()->getSelection()))
			TTool::getApplication()->getCurrentSelection()->setSelection(0);

		if (app->getCurrentFrame()->isEditingScene()) {
			app->getCurrentColumn()->setColumnIndex(m_column);
			app->getCurrentFrame()->setFrame(m_row);
		} else
			app->getCurrentFrame()->setFid(m_frameId);
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		assert(!!image);
		if (!image)
			return;
		QMutexLocker lock(image->getMutex());
		VIStroke *stroke;

		stroke = image->getStrokeById(m_newStrokeId1);
		if (stroke)
			image->deleteStroke(stroke);

		stroke = image->getStrokeById(m_newStrokeId2);
		if (stroke)
			image->deleteStroke(stroke);

		stroke = cloneVIStroke(m_oldStroke);

		image->insertStrokeAt(stroke, m_pos);

		UINT size = m_fillInformation->size();
		if (!size) {
			app->getCurrentXsheet()->notifyXsheetChanged();
			notifyImageChanged();
			return;
		}

		image->findRegions();
		TRegion *reg;
		for (UINT i = 0; i < size; i++) {
			reg = image->getRegion((*m_fillInformation)[i].m_regionId);
			assert(reg);
			if (reg)
				reg->setStyle((*m_fillInformation)[i].m_styleId);
		}
		app->getCurrentXsheet()->notifyXsheetChanged();
		notifyImageChanged();
	}

	void redo() const
	{
		TTool::Application *app = TTool::getApplication();
		if (!app)
			return;
		if (app->getCurrentFrame()->isEditingScene()) {
			app->getCurrentColumn()->setColumnIndex(m_column);
			app->getCurrentFrame()->setFrame(m_row);
		} else
			app->getCurrentFrame()->setFid(m_frameId);
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		assert(!!image);
		if (!image)
			return;
		QMutexLocker lock(image->getMutex());

		bool isSelfLoop = image->getStroke(m_pos)->isSelfLoop();
		image->splitStroke(m_pos, *m_sortedWRanges);

		image->getStroke(m_pos)->setId(m_newStrokeId1);
		if (!isSelfLoop && m_sortedWRanges->size() == 2)
			image->getStroke(m_pos + 1)->setId(m_newStrokeId2);

		app->getCurrentXsheet()->notifyXsheetChanged();
		notifyImageChanged();
	}

	int getSize() const
	{
		return sizeof(*this) + m_fillInformation->capacity() * sizeof(TFilledRegionInf) + 500;
	}

	QString getToolName()
	{
		return QString("Cutter Tool");
	}
};

//=============================================================================
// CutterTool
//-----------------------------------------------------------------------------

class CutterTool : public TTool
{

public:
	bool m_mouseDown;

	TPointD m_vTan;

	TThickPoint m_cursor;
	TPointD m_speed;
	int m_cursorId;
	double m_pW;

	CutterTool()
		: TTool("T_Cutter"), m_mouseDown(false), m_cursorId(ToolCursor::CutterCursor)
	{
		bind(TTool::VectorImage);
	}

	ToolType getToolType() const { return TTool::LevelWriteTool; }

	void draw()
	{
		//TAffine viewMatrix = getViewer()->getViewMatrix();
		//glPushMatrix();
		//tglMultMatrix(viewMatrix);

		const double pixelSize = getPixelSize();

		double len = m_cursor.thick + 15 * pixelSize;

		if (m_speed != TPointD(0, 0)) {

			TPointD v = m_speed;
			TPointD p = (TPointD)m_cursor;

			v = rotate90(v);
			v = normalize(v);

			v = v * (len);

			tglColor(TPixelD(0.1, 0.9, 0.1));
			tglDrawSegment(p - v, p + v);
		}
		//glPopMatrix();
	}

	void leftButtonDown(const TPointD &pos, const TMouseEvent &)
	{
		TVectorImageP vi = TImageP(getImage(true));
		if (!vi)
			return;
		QMutexLocker sl(vi->getMutex());

		double dist, pW;
		UINT strokeIndex;

		TStroke *strokeRef;

		if (vi->getNearestStroke(pos, pW, strokeIndex, dist) && pW >= 0 && pW <= 1) {
			double w;

			strokeRef = vi->getStroke(strokeIndex);

			double hitPointLen = strokeRef->getLength(pW);
			double totalLen = strokeRef->getLength();

			double len = hitPointLen;

			if (!strokeRef->isSelfLoop()) {
				if (len < TConsts::epsilon)
					w = 0;
				else
					w = strokeRef->getParameterAtLength(len);

				if (len > totalLen - TConsts::epsilon)
					w = 1;
				else
					w = strokeRef->getParameterAtLength(len);
			} else {
				if (len < 0)
					len += totalLen;

				if (len > totalLen)
					len -= totalLen;

				w = strokeRef->getParameterAtLength(len);
			}

			vector<DoublePair> *sortedWRanges = new vector<DoublePair>;

			if (strokeRef->isSelfLoop()) {
				sortedWRanges->push_back(make_pair(0, w));
				sortedWRanges->push_back(make_pair(w, 1));
			} else {
				if (w == 0 || w == 1)
					sortedWRanges->push_back(make_pair(0, 1));
				else {
					sortedWRanges->push_back(make_pair(0, w));
					sortedWRanges->push_back(make_pair(w, 1));
				}
			}

			vector<TFilledRegionInf> *fillInformation = new vector<TFilledRegionInf>;
			ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation, strokeRef->getBBox());

			VIStroke *oldStroke = cloneVIStroke(vi->getVIStroke(strokeIndex));
			bool isSelfLoop = vi->getStroke(strokeIndex)->isSelfLoop();
			vi->splitStroke(strokeIndex, *sortedWRanges);

			TUndo *nundo;

			TXshSimpleLevel *sl = TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
			assert(sl);
			TFrameId id = getCurrentFid();
			if (isSelfLoop || sortedWRanges->size() == 1) {
				nundo = new UndoCutter(sl, id, oldStroke, strokeIndex, vi->getStroke(strokeIndex)->getId(), -1, fillInformation, sortedWRanges);
			} else {
				assert(strokeIndex + 1 < vi->getStrokeCount());
				nundo = new UndoCutter(sl, id, oldStroke, strokeIndex, vi->getStroke(strokeIndex)->getId(), vi->getStroke(strokeIndex + 1)->getId(), fillInformation, sortedWRanges);
			}

			TUndoManager::manager()->add(nundo);

			invalidate();
			notifyImageChanged();
		}
		invalidate();
	}

	void mouseMove(const TPointD &pos, const TMouseEvent &e)
	{
		TVectorImageP vi = TImageP(getImage(true));
		if (!vi) {
			m_speed = TPointD(0, 0);
			return;
		}

		// select nearest stroke and finds its parameter
		double dist, pW;
		UINT stroke;

		if (vi->getNearestStroke(pos, pW, stroke, dist)) {
			TStroke *strokeRef = vi->getStroke(stroke);
			m_speed = strokeRef->getSpeed(pW);
			m_cursor = strokeRef->getThickPoint(pW);
			m_pW = pW;
		} else {
			m_speed = TPointD(0, 0);
		}
		invalidate();
	}

	void onLeave()
	{
		m_speed = TPointD(0, 0);
	}

	void onActivate()
	{
	}
	void onEnter()
	{
		if ((TVectorImageP)getImage(false))
			m_cursorId = ToolCursor::CutterCursor;
		else
			m_cursorId = ToolCursor::CURSOR_NO;
	}

	int getCursorId() const { return m_cursorId; }

} cutterTool;

//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------

//TTool *getCutterTool() {return &cutterTool;}