tahoma2d/toonz/sources/stdfx/kaleido.cpp

291 lines
8.6 KiB
C++
Raw Normal View History

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