Blob Blame Raw
#pragma once

#ifndef CONTROLPOINT_SELECTION_INCLUDED
#define CONTROLPOINT_SELECTION_INCLUDED

#include "toonzqt/selection.h"
#include "tools/tool.h"
#include "tstroke.h"
#include "tvectorimage.h"
#include "tcurves.h"

//=============================================================================
// ControlPointEditorStroke
//-----------------------------------------------------------------------------

class ControlPointEditorStroke
{
private:
	class ControlPoint
	{
	public:
		int m_pointIndex;
		TThickPoint m_speedIn;
		TThickPoint m_speedOut;
		bool m_isCusp;

		ControlPoint(int i, TThickPoint speedIn, TThickPoint speedOut, bool isCusp = true)
			: m_pointIndex(i), m_speedIn(speedIn), m_speedOut(speedOut), m_isCusp(isCusp)
		{
		}
		ControlPoint()
		{
		}
	};

	QList<ControlPoint> m_controlPoints;
	TVectorImageP m_vi;
	int m_strokeIndex;

	void adjustChunkParity();

	/*! Reset m_controlPoints. */
	void resetControlPoints();

	TThickPoint getPureDependentPoint(int index) const;
	void getDependentPoints(int index, std::vector<std::pair<int, TThickPoint>> &points) const;
	void updatePoints();
	void updateDependentPoint(int index);

	inline int nextIndex(int index) const;
	inline int prevIndex(int index) const;

	void moveSpeedIn(int index, const TPointD &delta, double minDistance);
	void moveSpeedOut(int index, const TPointD &delta, double minDistance);

	void moveSingleControlPoint(int index, const TPointD &delta);

public:
	enum PointType {
		CONTROL_POINT,
		SPEED_IN,
		SPEED_OUT,
		SEGMENT,
		NONE
	};

	ControlPointEditorStroke()
		: m_vi() {}

	~ControlPointEditorStroke()
	{
		m_controlPoints.clear();
	}

	ControlPointEditorStroke *clone() const;

	/*! Modify stroke: between two linear or cusp point must be a pair chunk number.
      PAY ATTENTION: Can add control point in the stroke. */
	void setStroke(const TVectorImageP &vi, int strokeIndex);
	TStroke *getStroke() const { return m_vi ? m_vi->getStroke(m_strokeIndex) : 0; }

	void setStrokeIndex(int strokeIndex) { m_strokeIndex = strokeIndex; }
	int getStrokeIndex() const { return m_strokeIndex; }

	int getControlPointCount() const { return m_controlPoints.size(); }

	TThickPoint getControlPoint(int index) const;
	/*! From index point in \b ControlPointEditorStroke to index point in \b TStroke. */
	int getIndexPointInStroke(int index) const;
	TThickPoint getSpeedInPoint(int index) const;
	TThickPoint getSpeedOutPoint(int index) const;

	//! Return true if index point is a cusp.
	bool isCusp(int index) const;
	//! Set \b m_isCusp of \b index point to \b isCusp.
	/*! If isCusp == false check \b setSpeedIn and modify speed. */
	void setCusp(int index, bool isCusp, bool setSpeedIn);

	bool isSpeedInLinear(int index) const;
	bool isSpeedOutLinear(int index) const;
	void setLinearSpeedIn(int index, bool linear = true, bool updatePoints = true);
	void setLinearSpeedOut(int index, bool linear = true, bool updatePoints = true);

	/*! If isLinear==true set to "0" the speedIn and speedOut value of index point; otherwise set to default value.
      Return true if speedIn or speedOut is modified.
      If updatePoints is true update dependent point after.*/
	bool setLinear(int index, bool isLinear, bool updatePoints = true);
	bool setControlPointsLinear(std::set<int> points, bool isLinear);

	/*! Move \b index ControlPoint of \b delta. */
	void moveControlPoint(int index, const TPointD &delta);

	/*! Add a ControlPoint in \b point. Return added ControlPoint index. */
	int addControlPoint(const TPointD &pos);

	void deleteControlPoint(int index);

	/*! Move speed in \b index of \b delta. */
	void moveSpeed(int index, const TPointD &delta, bool isIn, double minDistance);

	/*! Move segment between \b beforeIndex and \b nextIndex of \b delta. */
	void moveSegment(int beforeIndex, int nextIndex, const TPointD &delta, const TPointD &pos);

	/*!Return NONE if \b pos is far from stroke.*/
	PointType getPointTypeAt(const TPointD &pos, double &distance2, int &index) const;

	bool isSelfLoop() const
	{
		TStroke *stroke = getStroke();
		return stroke && stroke->isSelfLoop();
	}
};

//=============================================================================
// ControlPointSelection
//-----------------------------------------------------------------------------

class ControlPointSelection : public QObject, public TSelection
{
	Q_OBJECT

private:
	std::set<int> m_selectedPoints;
	int m_strokeIndex;
	ControlPointEditorStroke *m_controlPointEditorStroke;

public:
	ControlPointSelection() : m_controlPointEditorStroke(0), m_strokeIndex(-1)
	{
	}

	void setControlPointEditorStroke(ControlPointEditorStroke *controlPointEditorStroke)
	{
		m_controlPointEditorStroke = controlPointEditorStroke;
	}

	bool isEmpty() const { return m_selectedPoints.empty(); }

	void selectNone() { m_selectedPoints.clear(); }
	bool isSelected(int index) const;
	void select(int index);
	void unselect(int index);

	void deleteControlPoints();

	void addMenuItems(QMenu *menu);

	void enableCommands();

protected slots:
	void setLinear();
	void setUnlinear();
};

#endif //CONTROLPOINT_SELECTION_INCLUDED