diff --git a/stuff/config/current.txt b/stuff/config/current.txt
index 7aa6923..cb1f02a 100644
--- a/stuff/config/current.txt
+++ b/stuff/config/current.txt
@@ -1294,6 +1294,16 @@
- "STD_iwa_SpinGradientFx.endAngle" "End Angle"
- "STD_iwa_SpinGradientFx.endColor" "End Color"
+ - "STD_iwa_LinearGradientFx" "Linear Gradient"
+ - "STD_iwa_LinearGradientFx.curveType" "Type"
+ - "STD_iwa_LinearGradientFx.startPoint" "Start Point"
+ - "STD_iwa_LinearGradientFx.startColor" "Start Color"
+ - "STD_iwa_LinearGradientFx.endPoint" "End Point"
+ - "STD_iwa_LinearGradientFx.endColor" "End Color"
+ - "STD_iwa_LinearGradientFx.wave_amplitude" "Amplitude"
+ - "STD_iwa_LinearGradientFx.wave_frequency" "Frequency"
+ - "STD_iwa_LinearGradientFx.wave_phase" "Phase"
+
- STD_iwa_TiledParticlesFx "Tiled Particles Iwa"
diff --git a/stuff/profiles/layouts/fxs/STD_iwa_LinearGradientFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_LinearGradientFx.xml
new file mode 100644
index 0000000..8e0d915
--- /dev/null
+++ b/stuff/profiles/layouts/fxs/STD_iwa_LinearGradientFx.xml
@@ -0,0 +1,14 @@
+
+
+ startPoint
+ endPoint
+ startColor
+ endColor
+ startColor endColor
+ curveType
+
+ wave_amplitude
+ wave_frequency
+ wave_phase
+
+
diff --git a/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml b/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml
index de531e5..7c48ba7 100644
--- a/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml
+++ b/stuff/profiles/layouts/fxs/STD_linearGradientFx.xml
@@ -1,5 +1,5 @@
-
+
period
color1
color2
diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst
index f2d13c3..dca20c1 100644
--- a/stuff/profiles/layouts/fxs/fxs.lst
+++ b/stuff/profiles/layouts/fxs/fxs.lst
@@ -38,7 +38,7 @@
STD_diamondGradientFx
STD_fourPointsGradientFx
- STD_linearGradientFx
+ STD_iwa_LinearGradientFx
STD_multiLinearGradientFx
STD_multiRadialGradientFx
STD_radialGradientFx
diff --git a/toonz/sources/include/tparamuiconcept.h b/toonz/sources/include/tparamuiconcept.h
index 8a62c37..b72a278 100644
--- a/toonz/sources/include/tparamuiconcept.h
+++ b/toonz/sources/include/tparamuiconcept.h
@@ -61,8 +61,10 @@ public:
RECT, // A Rect, with width, height and center. { [2
// TDoubleParamP], TPointParamP }
- DIAMOND, // A diagonally laid square. {
- // [TDoubleParamP] }
+ DIAMOND, // A diagonally laid square. {
+ // [TDoubleParamP] }
+ LINEAR_RANGE, // A band-like range between two points.
+ // { [2 TPointParamP] }
TYPESCOUNT
};
diff --git a/toonz/sources/stdfx/CMakeLists.txt b/toonz/sources/stdfx/CMakeLists.txt
index e871986..52449bd 100644
--- a/toonz/sources/stdfx/CMakeLists.txt
+++ b/toonz/sources/stdfx/CMakeLists.txt
@@ -78,6 +78,7 @@ set(HEADERS
iwa_textfx.h
iwa_corridorgradientfx.h
iwa_spingradientfx.h
+ iwa_lineargradientfx.h
)
set(SOURCES
@@ -262,6 +263,7 @@ set(SOURCES
iwa_textfx.cpp
iwa_corridorgradientfx.cpp
iwa_spingradientfx.cpp
+ iwa_lineargradientfx.cpp
)
set(OBJCSOURCES
diff --git a/toonz/sources/stdfx/iwa_lineargradientfx.cpp b/toonz/sources/stdfx/iwa_lineargradientfx.cpp
new file mode 100644
index 0000000..173aa89
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_lineargradientfx.cpp
@@ -0,0 +1,178 @@
+#include "iwa_lineargradientfx.h"
+
+#include "tspectrumparam.h"
+#include "gradients.h"
+#include "tparamuiconcept.h"
+#include "traster.h"
+
+//------------------------------------------------------------
+
+Iwa_LinearGradientFx::Iwa_LinearGradientFx()
+ : m_startPoint(TPointD(-50.0, 0.0))
+ , m_endPoint(TPointD(50.0, 0.0))
+ , m_startColor(TPixel32::Black)
+ , m_endColor(TPixel32::White)
+ , m_curveType(new TIntEnumParam(EaseInOut, "Ease In-Out"))
+ , m_wave_amplitude(0.0)
+ , m_wave_freq(0.0)
+ , m_wave_phase(0.0) {
+ m_startPoint->getX()->setMeasureName("fxLength");
+ m_startPoint->getY()->setMeasureName("fxLength");
+ m_endPoint->getX()->setMeasureName("fxLength");
+ m_endPoint->getY()->setMeasureName("fxLength");
+ bindParam(this, "startPoint", m_startPoint);
+ bindParam(this, "endPoint", m_endPoint);
+
+ m_curveType->addItem(Linear, "Linear");
+ m_curveType->addItem(EaseIn, "Ease In");
+ m_curveType->addItem(EaseOut, "Ease Out");
+ bindParam(this, "curveType", m_curveType);
+
+ m_wave_amplitude->setValueRange(0, std::numeric_limits::max());
+ m_wave_amplitude->setMeasureName("fxLength");
+ bindParam(this, "wave_amplitude", m_wave_amplitude);
+ bindParam(this, "wave_frequency", m_wave_freq);
+ bindParam(this, "wave_phase", m_wave_phase);
+
+ bindParam(this, "startColor", m_startColor);
+ bindParam(this, "endColor", m_endColor);
+}
+
+//------------------------------------------------------------
+
+bool Iwa_LinearGradientFx::doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &ri) {
+ bBox = TConsts::infiniteRectD;
+ return true;
+}
+
+//------------------------------------------------------------
+
+namespace {
+template
+void doLinearGradientT(RASTER ras, TDimensionI dim, TPointD startPos,
+ TPointD endPos, const TSpectrumT &spectrum,
+ GradientCurveType type, double w_amplitude,
+ double w_freq, double w_phase, const TAffine &affInv) {
+ auto getFactor = [&](double t) {
+ if (t > 1.0)
+ t = 1.0;
+ else if (t < 0.0)
+ t = 0.0;
+
+ double factor;
+ switch (type) {
+ case Linear:
+ factor = t;
+ break;
+ case EaseIn:
+ factor = t * t;
+ break;
+ case EaseOut:
+ factor = 1.0 - (1.0 - t) * (1.0 - t);
+ break;
+ case EaseInOut:
+ default:
+ factor = (-2 * t + 3) * (t * t);
+ break;
+ }
+ return factor;
+ };
+ startPos = affInv * startPos;
+ endPos = affInv * endPos;
+ TPointD seVec = endPos - startPos;
+
+ if (seVec == TPointD()) {
+ ras->fill(spectrum.getPremultipliedValue(0.0));
+ return;
+ }
+
+ TPointD posTrasf = -startPos;
+ double seVecLen2 = seVec.x * seVec.x + seVec.y * seVec.y;
+ double seVecLen = sqrt(seVecLen2);
+ double amplitude = w_amplitude / seVecLen;
+ TPointD auxVec(-seVec.y / seVecLen, seVec.x / seVecLen);
+
+ ras->lock();
+ for (int j = 0; j < ras->getLy(); j++) {
+ TPointD posAux = posTrasf;
+
+ PIXEL *pix = ras->pixels(j);
+ PIXEL *endPix = pix + ras->getLx();
+
+ while (pix < endPix) {
+ double t = (seVec.x * posAux.x + seVec.y * posAux.y) / seVecLen2;
+ if (amplitude) {
+ double distance = posAux.x * auxVec.x + posAux.y * auxVec.y;
+ t += amplitude * sin(w_freq * distance + w_phase);
+ }
+ double factor = getFactor(t);
+ *pix++ = spectrum.getPremultipliedValue(factor);
+
+ posAux.x += affInv.a11;
+ posAux.y += affInv.a21;
+ }
+ posTrasf.x += affInv.a12;
+ posTrasf.y += affInv.a22;
+ }
+ ras->unlock();
+}
+} // namespace
+
+//------------------------------------------------------------
+
+void Iwa_LinearGradientFx::doCompute(TTile &tile, double frame,
+ const TRenderSettings &ri) {
+ if (!((TRaster32P)tile.getRaster()) && !((TRaster64P)tile.getRaster())) {
+ throw TRopException("unsupported input pixel type");
+ }
+
+ // convert shape position to render region coordinate
+ TAffine aff = ri.m_affine;
+ TDimensionI dimOut(tile.getRaster()->getLx(), tile.getRaster()->getLy());
+ TPointD dimOffset((float)dimOut.lx / 2.0f, (float)dimOut.ly / 2.0f);
+ TPointD startPos = aff * m_startPoint->getValue(frame) -
+ (tile.m_pos + tile.getRaster()->getCenterD()) + dimOffset;
+ TPointD endPos = aff * m_endPoint->getValue(frame) -
+ (tile.m_pos + tile.getRaster()->getCenterD()) + dimOffset;
+
+ double w_amplitude = m_wave_amplitude->getValue(frame) / ri.m_shrinkX;
+ double w_freq = m_wave_freq->getValue(frame) * ri.m_shrinkX;
+ double w_phase = m_wave_phase->getValue(frame);
+ w_freq *= 0.01 * M_PI_180;
+
+ std::vector colors = {
+ TSpectrum::ColorKey(0, m_startColor->getValue(frame)),
+ TSpectrum::ColorKey(1, m_endColor->getValue(frame))};
+ TSpectrumParamP m_colors = TSpectrumParamP(colors);
+
+ tile.getRaster()->clear();
+ TRaster32P outRas32 = (TRaster32P)tile.getRaster();
+ TRaster64P outRas64 = (TRaster64P)tile.getRaster();
+ if (outRas32)
+ doLinearGradientT(
+ outRas32, dimOut, startPos, endPos, m_colors->getValue(frame),
+ (GradientCurveType)m_curveType->getValue(), w_amplitude, w_freq,
+ w_phase, aff.inv());
+ else if (outRas64)
+ doLinearGradientT(
+ outRas64, dimOut, startPos, endPos, m_colors->getValue64(frame),
+ (GradientCurveType)m_curveType->getValue(), w_amplitude, w_freq,
+ w_phase, aff.inv());
+}
+
+//------------------------------------------------------------
+
+void Iwa_LinearGradientFx::getParamUIs(TParamUIConcept *&concepts,
+ int &length) {
+ concepts = new TParamUIConcept[length = 1];
+
+ concepts[0].m_type = TParamUIConcept::LINEAR_RANGE;
+ concepts[0].m_label = "";
+ concepts[0].m_params.push_back(m_startPoint);
+ concepts[0].m_params.push_back(m_endPoint);
+}
+
+//------------------------------------------------------------
+
+FX_PLUGIN_IDENTIFIER(Iwa_LinearGradientFx, "iwa_LinearGradientFx");
diff --git a/toonz/sources/stdfx/iwa_lineargradientfx.h b/toonz/sources/stdfx/iwa_lineargradientfx.h
new file mode 100644
index 0000000..88bfe33
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_lineargradientfx.h
@@ -0,0 +1,32 @@
+#pragma once
+#ifndef IWA_LINEARGRADIENTFX_H
+#define IWA_LINEARGRADIENTFX_H
+
+#include "tfxparam.h"
+#include "stdfx.h"
+#include "tparamset.h"
+
+class Iwa_LinearGradientFx final : public TStandardZeraryFx {
+ FX_PLUGIN_DECLARATION(Iwa_LinearGradientFx)
+
+ TIntEnumParamP m_curveType;
+ TPointParamP m_startPoint, m_endPoint;
+ TPixelParamP m_startColor, m_endColor;
+
+ TDoubleParamP m_wave_amplitude;
+ TDoubleParamP m_wave_freq;
+ TDoubleParamP m_wave_phase;
+
+public:
+ Iwa_LinearGradientFx();
+
+ bool canHandle(const TRenderSettings &info, double frame) override {
+ return true;
+ }
+ bool doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &ri) override;
+ void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override;
+ void getParamUIs(TParamUIConcept *&concepts, int &length) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/toonz/sources/stdfx/stdfx.cpp b/toonz/sources/stdfx/stdfx.cpp
index 2f11f6c..1183754 100644
--- a/toonz/sources/stdfx/stdfx.cpp
+++ b/toonz/sources/stdfx/stdfx.cpp
@@ -236,6 +236,11 @@ public:
}
};
+// From V1.4 LinearGradientFx becomes obsolete and was replaced by
+// Iwa_LinearGradientFx which has more flexibility. (iwa_lineargradientfx.cpp)
+// This code is kept in order to load the fx made with older OT versions.
+// Nov 14, 2019
+
class LinearGradientFx final : public TStandardZeraryFx {
FX_PLUGIN_DECLARATION(LinearGradientFx)
TDoubleParamP m_period;
diff --git a/toonz/sources/tnztools/edittoolgadgets.cpp b/toonz/sources/tnztools/edittoolgadgets.cpp
index 2cd9d93..ffad94e 100644
--- a/toonz/sources/tnztools/edittoolgadgets.cpp
+++ b/toonz/sources/tnztools/edittoolgadgets.cpp
@@ -1342,6 +1342,192 @@ void QuadFxGadget::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
m_handle = None;
}
+//=============================================================================
+
+class LinearRangeFxGadget final : public FxGadget {
+ TPointParamP m_start, m_end;
+
+ enum HANDLE { Body = 0, Start, End, None } m_handle = None;
+
+ TPointD m_clickedPos;
+ TPointD m_targetPos, m_anotherPos;
+
+public:
+ LinearRangeFxGadget(FxGadgetController *controller,
+ const TPointParamP &startPoint,
+ const TPointParamP &endPoint);
+
+ void draw(bool picking) override;
+
+ void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
+ void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override;
+ void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
+};
+
+//---------------------------------------------------------------------------
+
+LinearRangeFxGadget::LinearRangeFxGadget(FxGadgetController *controller,
+ const TPointParamP &startPoint,
+ const TPointParamP &endPoint)
+ : FxGadget(controller, 3), m_start(startPoint), m_end(endPoint) {
+ addParam(startPoint->getX());
+ addParam(startPoint->getY());
+ addParam(endPoint->getX());
+ addParam(endPoint->getY());
+}
+
+//---------------------------------------------------------------------------
+
+void LinearRangeFxGadget::draw(bool picking) {
+ auto setColorById = [&](int id) {
+ if (isSelected(id))
+ glColor3dv(m_selectedColor);
+ else
+ glColor3d(0, 0, 1);
+ };
+
+ auto drawPoint = [&]() {
+ double r = getPixelSize() * 3;
+ double d = getPixelSize() * 6;
+ glBegin(GL_LINES);
+ glVertex2d(-d, 0);
+ glVertex2d(-r, 0);
+ glVertex2d(d, 0);
+ glVertex2d(r, 0);
+ glVertex2d(0, -d);
+ glVertex2d(0, -r);
+ glVertex2d(0, d);
+ glVertex2d(0, r);
+ glEnd();
+ tglDrawRect(-r, -r, r, r);
+ };
+
+ setPixelSize();
+ double r = getPixelSize() * 200;
+ double a = getPixelSize() * 5;
+
+ TPointD start = getValue(m_start);
+ TPointD end = getValue(m_end);
+
+ glPushMatrix();
+
+ if (start != end) {
+ // draw lines perpendicular to the line between ends
+ double angle = std::atan2(start.x - end.x, end.y - start.y) * M_180_PI;
+ // start
+ setColorById(Start);
+ glPushMatrix();
+ glTranslated(start.x, start.y, 0);
+ glRotated(angle, 0, 0, 1);
+ if (m_handle == Start) glScaled(5.0, 1.0, 1.0);
+ glBegin(GL_LINES);
+ glVertex2d(-r, 0);
+ glVertex2d(r, 0);
+ glEnd();
+ glPopMatrix();
+ // end
+ setColorById(End);
+ glPushMatrix();
+ glTranslated(end.x, end.y, 0);
+ glRotated(angle, 0, 0, 1);
+ if (m_handle == End) glScaled(5.0, 1.0, 1.0);
+ glBegin(GL_LINE_STRIP);
+ glVertex2d(-r, 0);
+ glVertex2d(r, 0);
+ glEnd();
+ glPopMatrix();
+
+ // line body
+ setColorById(Body);
+ glPushName(getId() + Body);
+ glBegin(GL_LINES);
+ glVertex2d(start.x, start.y);
+ glVertex2d(end.x, end.y);
+ glEnd();
+ // small dash at the center
+ glPushMatrix();
+ glTranslated((start.x + end.x) / 2.0, (start.y + end.y) / 2.0, 0);
+ glRotated(angle, 0, 0, 1);
+ glBegin(GL_LINES);
+ glVertex2d(-a, 0);
+ glVertex2d(a, 0);
+ glEnd();
+ glPopMatrix();
+ glPopName();
+ }
+
+ // start point
+ setColorById(Start);
+ glPushName(getId() + Start);
+ glPushMatrix();
+ glTranslated(start.x, start.y, 0);
+ drawPoint();
+ glPopMatrix();
+ glPopName();
+ drawTooltip(start + TPointD(7, 3) * getPixelSize(), "Start");
+
+ // end point
+ setColorById(End);
+ glPushName(getId() + End);
+ glPushMatrix();
+ glTranslated(end.x, end.y, 0);
+ drawPoint();
+ glPopMatrix();
+ glPopName();
+ drawTooltip(end + TPointD(7, 3) * getPixelSize(), "End");
+
+ glPopMatrix();
+}
+
+//---------------------------------------------------------------------------
+
+void LinearRangeFxGadget::leftButtonDown(const TPointD &pos,
+ const TMouseEvent &) {
+ m_handle = (HANDLE)m_selected;
+ if (m_handle == None) return;
+ m_clickedPos = pos;
+ m_targetPos = (m_handle == Start || m_handle == Body) ? getValue(m_start)
+ : getValue(m_end);
+ m_anotherPos = (m_handle == Start || m_handle == Body) ? getValue(m_end)
+ : getValue(m_start);
+}
+
+//---------------------------------------------------------------------------
+
+void LinearRangeFxGadget::leftButtonDrag(const TPointD &pos,
+ const TMouseEvent &e) {
+ if (m_handle == None) return;
+ TPointD d = pos - m_clickedPos;
+
+ if (m_handle == Body) {
+ setValue(m_start, m_targetPos + d);
+ setValue(m_end, m_anotherPos + d);
+ return;
+ }
+
+ TPointParamP target = (m_handle == Start) ? m_start : m_end;
+
+ if (m_targetPos != m_anotherPos && e.isShiftPressed()) {
+ TPointD vecA = m_targetPos - m_anotherPos;
+ TPointD vecB = m_targetPos + d - m_anotherPos;
+ double ratio = std::min(vecB.x / vecA.x, vecB.y / vecA.y);
+ d = vecA * (ratio - 1.0);
+ }
+
+ setValue(target, m_targetPos + d);
+
+ if (e.isCtrlPressed()) {
+ TPointParamP another = (m_handle == Start) ? m_end : m_start;
+ setValue(another, m_anotherPos - d);
+ }
+}
+
+//---------------------------------------------------------------------------
+
+void LinearRangeFxGadget::leftButtonUp(const TPointD &pos,
+ const TMouseEvent &) {
+ m_handle = None;
+}
//*************************************************************************************
// FxGadgetController implementation
//*************************************************************************************
@@ -1519,6 +1705,13 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) {
gadget = new DiamondFxGadget(this, uiConcept.m_params[0]);
break;
}
+
+ case TParamUIConcept::LINEAR_RANGE: {
+ assert(uiConcept.m_params.size() == 2);
+ gadget = new LinearRangeFxGadget(this, uiConcept.m_params[0],
+ uiConcept.m_params[1]);
+ break;
+ }
}
if (gadget) gadget->setLabel(uiConcept.m_label);