| |
| |
| #include "stdfx.h" |
| #include "tfxparam.h" |
| #include "tparamset.h" |
| |
| #include "toonz/tdistort.h" |
| |
| |
| |
| |
| |
| namespace { |
| |
| class KaleidoDistorter final : public TDistorter { |
| double m_angle; |
| TAffine m_aff; |
| TPointD m_shift; |
| |
| public: |
| KaleidoDistorter(double angle, const TAffine &aff, const TPointD shift) |
| : m_angle(angle), m_aff(aff), m_shift(shift) {} |
| |
| TPointD map(const TPointD &p) const override { return TPointD(); } |
| int maxInvCount() const override { return 1; } |
| |
| int invMap(const TPointD &p, TPointD *results) const override; |
| }; |
| |
| |
| |
| int KaleidoDistorter::invMap(const TPointD &p, TPointD *results) const { |
| TPointD q(m_aff * p); |
| |
| |
| double qAngle = atan2(q.y, q.x); |
| if (qAngle < 0.0) qAngle += 2.0 * M_PI; |
| |
| assert(qAngle >= 0.0); |
| |
| int section = tfloor(qAngle / m_angle); |
| bool reflect = (bool)(section % 2); |
| |
| double normQ = norm(q); |
| if (reflect) { |
| double newAngle = qAngle - (section + 1) * m_angle; |
| |
| 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; |
| |
| results[0].x = normQ * cos(newAngle) + m_shift.x; |
| results[0].y = normQ * sin(newAngle) + m_shift.y; |
| } |
| |
| return 1; |
| } |
| |
| } |
| |
| |
| |
| |
| |
| class KaleidoFx final : public TStandardRasterFx { |
| FX_PLUGIN_DECLARATION(KaleidoFx) |
| |
| TRasterFxPort m_input; |
| TPointParamP m_center; |
| TDoubleParamP m_angle; |
| TIntParamP m_count; |
| |
| public: |
| 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"); |
| |
| bindParam(this, "center", m_center); |
| bindParam(this, "angle", m_angle); |
| bindParam(this, "count", m_count); |
| |
| addInputPort("Source", m_input); |
| |
| m_count->setValueRange(1, 100); |
| } |
| |
| ~KaleidoFx(){}; |
| |
| bool doGetBBox(double frame, TRectD &bBox, |
| const TRenderSettings &info) override; |
| |
| void doDryCompute(TRectD &rect, double frame, |
| const TRenderSettings &ri) override; |
| void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override; |
| int getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) override; |
| |
| bool canHandle(const TRenderSettings &info, double frame) override { |
| return isAlmostIsotropic(info.m_affine); |
| } |
| |
| private: |
| void buildSectionRect(TRectD &inRect, double angle); |
| void rotate(TRectD &rect); |
| |
| TAffine buildInputReference(double frame, TRectD &inRect, |
| TRenderSettings &inInfo, const TRectD &outRect, |
| const TRenderSettings &outInfo); |
| }; |
| |
| |
| |
| 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)); |
| } |
| } |
| |
| |
| |
| void KaleidoFx::rotate(TRectD &rect) { |
| TPointD pMax(std::max(-rect.x0, rect.x1), std::max(-rect.y0, rect.y1)); |
| double normPMax = norm(pMax); |
| |
| rect = TRectD(-normPMax, -normPMax, normPMax, normPMax); |
| } |
| |
| |
| |
| 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(); |
| |
| inInfo.m_affine = TRotation(-m_angle->getValue(frame) - angle) * |
| TScale(scale).place(m_center->getValue(frame), TPointD()); |
| |
| TAffine outRefToInRef(inInfo.m_affine * outInfo.m_affine.inv()); |
| |
| |
| TRectD inBBox; |
| m_input->getBBox(frame, inBBox, inInfo); |
| |
| |
| |
| |
| TRectD outRect_inputRef(outRefToInRef * outRect); |
| rotate(outRect_inputRef); |
| |
| |
| inRect = inBBox * outRect_inputRef; |
| buildSectionRect(inRect, angle); |
| |
| return outRefToInRef; |
| } |
| |
| |
| |
| bool KaleidoFx::doGetBBox(double frame, TRectD &bBox, |
| const TRenderSettings &info) { |
| |
| |
| |
| |
| |
| if (!m_input.getFx()) return false; |
| |
| double angle = M_PI / m_count->getValue(); |
| |
| TRenderSettings inInfo(info); |
| inInfo.m_affine = TRotation(-m_angle->getValue(frame) - angle) * |
| TTranslation(-m_center->getValue(frame)); |
| |
| if (!m_input->getBBox(frame, bBox, inInfo)) return false; |
| |
| TRectD infiniteRect(TConsts::infiniteRectD); |
| |
| TRectD kaleidoRect((m_count->getValue() > 1) ? 0.0 : infiniteRect.x0, 0.0, |
| infiniteRect.x1, infiniteRect.y1); |
| |
| bBox *= kaleidoRect; |
| if (bBox.x0 == infiniteRect.x0 || bBox.x1 == infiniteRect.x1 || |
| bBox.y1 == infiniteRect.y1) { |
| bBox = infiniteRect; |
| return true; |
| } |
| |
| buildSectionRect(bBox, angle); |
| |
| |
| rotate(bBox); |
| |
| |
| bBox = inInfo.m_affine.inv() * bBox; |
| |
| return true; |
| } |
| |
| |
| |
| void KaleidoFx::doDryCompute(TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| if (!m_input.isConnected()) return; |
| |
| if (fabs(info.m_affine.det()) < TConsts::epsilon) return; |
| |
| |
| TRectD inRect; |
| TRenderSettings inInfo(info); |
| |
| TAffine outRefToInRef(buildInputReference(frame, inRect, inInfo, rect, info)); |
| |
| if (inRect.getLx() <= 0.0 || inRect.getLy() <= 0.0) return; |
| |
| inRect = inRect.enlarge(1.0); |
| |
| |
| m_input->dryCompute(inRect, frame, inInfo); |
| } |
| |
| |
| |
| void KaleidoFx::doCompute(TTile &tile, double frame, |
| const TRenderSettings &info) { |
| if (!m_input.isConnected()) return; |
| |
| if (fabs(info.m_affine.det()) < TConsts::epsilon) return; |
| |
| |
| TDimension tileSize(tile.getRaster()->getSize()); |
| TRectD tileRect(tile.m_pos, TDimensionD(tileSize.lx, tileSize.ly)); |
| |
| |
| TRectD inRect; |
| TRenderSettings inInfo(info); |
| |
| TAffine outRefToInRef( |
| buildInputReference(frame, inRect, inInfo, tileRect, info)); |
| |
| if (inRect.getLx() <= 0.0 || inRect.getLy() <= 0.0) return; |
| |
| inRect = inRect.enlarge(1.0); |
| |
| |
| TTile inTile; |
| TDimension inDim(tceil(inRect.getLx()), tceil(inRect.getLy())); |
| |
| m_input->allocateAndCompute(inTile, inRect.getP00(), inDim, tile.getRaster(), |
| frame, inInfo); |
| |
| |
| double angle = M_PI / m_count->getValue(); |
| KaleidoDistorter distorter(angle, outRefToInRef, -inRect.getP00()); |
| |
| TRasterP inRas(inTile.getRaster()); |
| TRasterP tileRas(tile.getRaster()); |
| |
| distort(tileRas, inRas, distorter, convert(tile.m_pos)); |
| } |
| |
| |
| |
| int KaleidoFx::getMemoryRequirement(const TRectD &rect, double frame, |
| const TRenderSettings &info) { |
| if (!m_input.isConnected()) return 0; |
| |
| if (fabs(info.m_affine.det()) < TConsts::epsilon) return 0; |
| |
| |
| TRectD inRect; |
| TRenderSettings inInfo(info); |
| |
| TAffine outRefToInRef(buildInputReference(frame, inRect, inInfo, rect, info)); |
| |
| if (inRect.getLx() <= 0.0 || inRect.getLy() <= 0.0) return 0; |
| |
| inRect = inRect.enlarge(1.0); |
| |
| return TRasterFx::memorySize(inRect, info.m_bpp); |
| } |
| |
| |
| |
| FX_PLUGIN_IDENTIFIER(KaleidoFx, "kaleidoFx"); |
| |