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