Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "stdfx.h"
Toshihiro Shimizu 890ddd
#include "tfxparam.h"
Toshihiro Shimizu 890ddd
#include "tparamset.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonz/tdistort.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************
Toshihiro Shimizu 890ddd
//    Local namespace stuff
Toshihiro Shimizu 890ddd
//****************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
namespace
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
class KaleidoDistorter : public TDistorter
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	double m_angle;
Toshihiro Shimizu 890ddd
	TAffine m_aff;
Toshihiro Shimizu 890ddd
	TPointD m_shift;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	KaleidoDistorter(double angle, const TAffine &aff, const TPointD shift)
Toshihiro Shimizu 890ddd
		: m_angle(angle), m_aff(aff), m_shift(shift) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TPointD map(const TPointD &p) const { return TPointD(); }
Toshihiro Shimizu 890ddd
	int maxInvCount() const { return 1; }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int invMap(const TPointD &p, TPointD *results) const;
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
int KaleidoDistorter::invMap(const TPointD &p, TPointD *results) const
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TPointD q(m_aff * p);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build p's angular position
Toshihiro Shimizu 890ddd
	double qAngle = atan2(q.y, q.x);
Toshihiro Shimizu 890ddd
	if (qAngle < 0.0)
Toshihiro Shimizu 890ddd
		qAngle += 2.0 * TConsts::pi;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	assert(qAngle >= 0.0);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int section = tfloor(qAngle / m_angle);
Toshihiro Shimizu 890ddd
	bool reflect = (bool)(section % 2);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double normQ = norm(q);
Toshihiro Shimizu 890ddd
	if (reflect) {
Toshihiro Shimizu 890ddd
		double newAngle = qAngle - (section + 1) * m_angle;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		results[0].x = normQ * cos(newAngle) + m_shift.x;
Toshihiro Shimizu 890ddd
		results[0].y = -normQ * sin(newAngle) + m_shift.y;
Toshihiro Shimizu 890ddd
	} else {
Toshihiro Shimizu 890ddd
		double newAngle = qAngle - section * m_angle;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		results[0].x = normQ * cos(newAngle) + m_shift.x;
Toshihiro Shimizu 890ddd
		results[0].y = normQ * sin(newAngle) + m_shift.y;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return 1;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************
Toshihiro Shimizu 890ddd
//    Kaleido Fx
Toshihiro Shimizu 890ddd
//****************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
class KaleidoFx : public TStandardRasterFx
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	FX_PLUGIN_DECLARATION(KaleidoFx)
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRasterFxPort m_input;
Toshihiro Shimizu 890ddd
	TPointParamP m_center;
Toshihiro Shimizu 890ddd
	TDoubleParamP m_angle;
Toshihiro Shimizu 890ddd
	TIntParamP m_count;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Toshihiro Shimizu 890ddd
	KaleidoFx()
Toshihiro Shimizu 890ddd
		: m_center(TPointD()), m_angle(0.0), m_count(3)
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		m_center->getX()->setMeasureName("fxLength");
Toshihiro Shimizu 890ddd
		m_center->getY()->setMeasureName("fxLength");
Toshihiro Shimizu 890ddd
		m_angle->setMeasureName("angle");
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		bindParam(this, "center", m_center);
Toshihiro Shimizu 890ddd
		bindParam(this, "angle", m_angle);
Toshihiro Shimizu 890ddd
		bindParam(this, "count", m_count);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		addInputPort("Source", m_input);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		m_count->setValueRange(1, 100);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	~KaleidoFx(){};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	void doDryCompute(TRectD &rect, double frame, const TRenderSettings &ri);
Toshihiro Shimizu 890ddd
	void doCompute(TTile &tile, double frame, const TRenderSettings &ri);
Toshihiro Shimizu 890ddd
	int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	bool canHandle(const TRenderSettings &info, double frame)
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		return isAlmostIsotropic(info.m_affine);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
private:
Toshihiro Shimizu 890ddd
	void buildSectionRect(TRectD &inRect, double angle);
Toshihiro Shimizu 890ddd
	void rotate(TRectD &rect);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TAffine buildInputReference(double frame,
Toshihiro Shimizu 890ddd
								TRectD &inRect, TRenderSettings &inInfo,
Toshihiro Shimizu 890ddd
								const TRectD &outRect, const TRenderSettings &outInfo);
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void KaleidoFx::buildSectionRect(TRectD &inRect, double angle)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	inRect.y0 = tmax(inRect.y0, 0.0);
Toshihiro Shimizu 890ddd
	if (angle <= TConsts::pi_2) {
Toshihiro Shimizu 890ddd
		inRect.x0 = tmax(inRect.x0, 0.0);
Toshihiro Shimizu 890ddd
		inRect.y1 = tmin(inRect.y1, inRect.x1 * tan(angle));
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void KaleidoFx::rotate(TRectD &rect)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	TPointD pMax(tmax(-rect.x0, rect.x1), tmax(-rect.y0, rect.y1));
Toshihiro Shimizu 890ddd
	double normPMax = norm(pMax);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	rect = TRectD(-normPMax, -normPMax, normPMax, normPMax);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
TAffine KaleidoFx::buildInputReference(
Toshihiro Shimizu 890ddd
	double frame,
Toshihiro Shimizu 890ddd
	TRectD &inRect, TRenderSettings &inInfo,
Toshihiro Shimizu 890ddd
	const TRectD &outRect, const TRenderSettings &outInfo)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	double scale = fabs(sqrt(outInfo.m_affine.det()));
Toshihiro Shimizu 890ddd
	double angle = TConsts::pi / m_count->getValue();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	inInfo.m_affine = TRotation(-m_angle->getValue(frame) - angle) *
Toshihiro Shimizu 890ddd
					  TScale(scale).place(m_center->getValue(frame), TPointD());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TAffine outRefToInRef(inInfo.m_affine * outInfo.m_affine.inv());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build the input bounding box
Toshihiro Shimizu 890ddd
	TRectD inBBox;
Toshihiro Shimizu 890ddd
	m_input->getBBox(frame, inBBox, inInfo);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Rotate the output rect in the input reference. This is required since the rotational
Toshihiro Shimizu 890ddd
	// deformation may rotate points outside the rect, inside it.
Toshihiro Shimizu 890ddd
	TRectD outRect_inputRef(outRefToInRef * outRect);
Toshihiro Shimizu 890ddd
	rotate(outRect_inputRef);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Intersect with the useful kaleido region
Toshihiro Shimizu 890ddd
	inRect = inBBox * outRect_inputRef;
Toshihiro Shimizu 890ddd
	buildSectionRect(inRect, angle);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return outRefToInRef;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
bool KaleidoFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	//Remember: info.m_affine MUST NOT BE CONSIDERED in doGetBBox's implementation
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	//Retrieve the input bbox without applied affines.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!m_input.getFx())
Toshihiro Shimizu 890ddd
		return false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	double angle = TConsts::pi / m_count->getValue();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRenderSettings inInfo(info);
Toshihiro Shimizu 890ddd
	inInfo.m_affine = TRotation(-m_angle->getValue(frame) - angle) *
Toshihiro Shimizu 890ddd
					  TTranslation(-m_center->getValue(frame));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (!m_input->getBBox(frame, bBox, inInfo))
Toshihiro Shimizu 890ddd
		return false;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRectD infiniteRect(TConsts::infiniteRectD);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRectD kaleidoRect(
Toshihiro Shimizu 890ddd
		(m_count->getValue() > 1) ? 0.0 : infiniteRect.x0, 0.0,
Toshihiro Shimizu 890ddd
		infiniteRect.x1, infiniteRect.y1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	bBox *= kaleidoRect;
Toshihiro Shimizu 890ddd
	if (bBox.x0 == infiniteRect.x0 || bBox.x1 == infiniteRect.x1 || bBox.y1 == infiniteRect.y1) {
Toshihiro Shimizu 890ddd
		bBox = infiniteRect;
Toshihiro Shimizu 890ddd
		return true;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	buildSectionRect(bBox, angle);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Now, we must rotate the bBox in order to obtain the kaleidoscoped box
Toshihiro Shimizu 890ddd
	rotate(bBox);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Finally, bring it back to standard reference
Toshihiro Shimizu 890ddd
	bBox = inInfo.m_affine.inv() * bBox;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void KaleidoFx::doDryCompute(TRectD &rect, double frame, const TRenderSettings &info)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (!m_input.isConnected())
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (fabs(info.m_affine.det()) < TConsts::epsilon)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build the input reference
Toshihiro Shimizu 890ddd
	TRectD inRect;
Toshihiro Shimizu 890ddd
	TRenderSettings inInfo(info);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TAffine outRefToInRef(buildInputReference(frame, inRect, inInfo, rect, info));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (inRect.getLx() <= 0.0 || inRect.getLy() <= 0.0)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	inRect = inRect.enlarge(1.0); // tdistort() seems to need it
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Allocate a corresponding input tile and calculate it
Toshihiro Shimizu 890ddd
	m_input->dryCompute(inRect, frame, inInfo);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void KaleidoFx::doCompute(TTile &tile, double frame, const TRenderSettings &info)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (!m_input.isConnected())
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (fabs(info.m_affine.det()) < TConsts::epsilon)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build the output rect
Toshihiro Shimizu 890ddd
	TDimension tileSize(tile.getRaster()->getSize());
Toshihiro Shimizu 890ddd
	TRectD tileRect(tile.m_pos, TDimensionD(tileSize.lx, tileSize.ly));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build the input reference
Toshihiro Shimizu 890ddd
	TRectD inRect;
Toshihiro Shimizu 890ddd
	TRenderSettings inInfo(info);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TAffine outRefToInRef(buildInputReference(frame, inRect, inInfo, tileRect, info));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (inRect.getLx() <= 0.0 || inRect.getLy() <= 0.0)
Toshihiro Shimizu 890ddd
		return;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	inRect = inRect.enlarge(1.0); // tdistort() seems to need it
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Allocate a corresponding input tile and calculate it
Toshihiro Shimizu 890ddd
	TTile inTile;
Toshihiro Shimizu 890ddd
	TDimension inDim(tceil(inRect.getLx()), tceil(inRect.getLy()));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	m_input->allocateAndCompute(inTile, inRect.getP00(), inDim, tile.getRaster(), frame, inInfo);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Now, perform kaleido
Toshihiro Shimizu 890ddd
	double angle = TConsts::pi / m_count->getValue();
Toshihiro Shimizu 890ddd
	KaleidoDistorter distorter(angle, outRefToInRef, -inRect.getP00());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TRasterP inRas(inTile.getRaster());
Toshihiro Shimizu 890ddd
	TRasterP tileRas(tile.getRaster());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	distort(tileRas, inRas, distorter, convert(tile.m_pos));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
int KaleidoFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	if (!m_input.isConnected())
Toshihiro Shimizu 890ddd
		return 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (fabs(info.m_affine.det()) < TConsts::epsilon)
Toshihiro Shimizu 890ddd
		return 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Build the input reference
Toshihiro Shimizu 890ddd
	TRectD inRect;
Toshihiro Shimizu 890ddd
	TRenderSettings inInfo(info);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	TAffine outRefToInRef(buildInputReference(frame, inRect, inInfo, rect, info));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	if (inRect.getLx() <= 0.0 || inRect.getLy() <= 0.0)
Toshihiro Shimizu 890ddd
		return 0;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	inRect = inRect.enlarge(1.0); // tdistort() seems to need it
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	return TRasterFx::memorySize(inRect, info.m_bpp);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
FX_PLUGIN_IDENTIFIER(KaleidoFx, "kaleidoFx");