diff --git a/stuff/config/current.txt b/stuff/config/current.txt
index a6cd128..e8ac97b 100644
--- a/stuff/config/current.txt
+++ b/stuff/config/current.txt
@@ -1272,6 +1272,20 @@
- "STD_iwa_TextFx.boxColor" "Box Color"
- "STD_iwa_TextFx.showBorder" "Show Border"
+ - "STD_iwa_CorridorGradientFx" "Corridor Gradient Iwa"
+ - "STD_iwa_CorridorGradientFx.shape" "Shape"
+ - "STD_iwa_CorridorGradientFx.curveType" "Type"
+ - "STD_iwa_CorridorGradientFx.bottom_left_in" "Bottom Left In"
+ - "STD_iwa_CorridorGradientFx.bottom_left_out" "Bottom Left Out"
+ - "STD_iwa_CorridorGradientFx.bottom_right_in" "Bottom Right In"
+ - "STD_iwa_CorridorGradientFx.bottom_right_out" "Bottom Right Out"
+ - "STD_iwa_CorridorGradientFx.top_right_in" "Top Right In"
+ - "STD_iwa_CorridorGradientFx.top_right_out" "Top Right Out"
+ - "STD_iwa_CorridorGradientFx.top_left_in" "Top Left In"
+ - "STD_iwa_CorridorGradientFx.top_left_out" "Top Left Out"
+ - "STD_iwa_CorridorGradientFx.inner_color" "Inner Color"
+ - "STD_iwa_CorridorGradientFx.outer_color" "Outer Color"
+
- STD_iwa_TiledParticlesFx "Tiled Particles Iwa"
diff --git a/stuff/profiles/layouts/fxs/STD_iwa_CorridorGradientFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_CorridorGradientFx.xml
new file mode 100644
index 0000000..5258f74
--- /dev/null
+++ b/stuff/profiles/layouts/fxs/STD_iwa_CorridorGradientFx.xml
@@ -0,0 +1,18 @@
+
+
+ shape
+ curveType
+
+ bottom_left_in
+ bottom_right_in
+ top_left_in
+ top_right_in
+ inner_color
+
+ bottom_left_out
+ bottom_right_out
+ top_left_out
+ top_right_out
+ outer_color
+
+
diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst
index b7c1e33..ca26b7c 100644
--- a/stuff/profiles/layouts/fxs/fxs.lst
+++ b/stuff/profiles/layouts/fxs/fxs.lst
@@ -44,6 +44,7 @@
STD_radialGradientFx
STD_spiralFx
STD_squareGradientFx
+ STD_iwa_CorridorGradientFx
STD_iwa_AdjustExposureFx
diff --git a/toonz/sources/stdfx/CMakeLists.txt b/toonz/sources/stdfx/CMakeLists.txt
index f0a0017..4aa2aaf 100644
--- a/toonz/sources/stdfx/CMakeLists.txt
+++ b/toonz/sources/stdfx/CMakeLists.txt
@@ -76,6 +76,7 @@ set(HEADERS
iwa_timecodefx.h
iwa_bokehreffx.h
iwa_textfx.h
+ iwa_corridorgradientfx.h
)
set(SOURCES
@@ -258,6 +259,7 @@ set(SOURCES
iwa_bokehreffx.cpp
iwa_barreldistortfx.cpp
iwa_textfx.cpp
+ iwa_corridorgradientfx.cpp
)
set(OBJCSOURCES
diff --git a/toonz/sources/stdfx/freedistortfx.cpp b/toonz/sources/stdfx/freedistortfx.cpp
index 8e3dfab..45db345 100644
--- a/toonz/sources/stdfx/freedistortfx.cpp
+++ b/toonz/sources/stdfx/freedistortfx.cpp
@@ -19,7 +19,7 @@
namespace {
inline bool myIsEmpty(const TRectD &r) { return r.x0 >= r.x1 || r.y0 >= r.y1; }
-}
+} // namespace
//==============================================================================
@@ -415,19 +415,21 @@ void FreeDistortBaseFx::safeTransform(double frame, int port,
// ------------------------------------------------------------------------
void FreeDistortBaseFx::getParamUIs(TParamUIConcept *&concepts, int &length) {
- concepts = new TParamUIConcept[length = 14];
+ concepts = new TParamUIConcept[length = 6];
concepts[0].m_type = TParamUIConcept::QUAD;
- concepts[0].m_params.push_back(m_p00_b);
- concepts[0].m_params.push_back(m_p10_b);
- concepts[0].m_params.push_back(m_p11_b);
concepts[0].m_params.push_back(m_p01_b);
+ concepts[0].m_params.push_back(m_p11_b);
+ concepts[0].m_params.push_back(m_p10_b);
+ concepts[0].m_params.push_back(m_p00_b);
+ concepts[0].m_label = " Src";
concepts[1].m_type = TParamUIConcept::QUAD;
- concepts[1].m_params.push_back(m_p00_a);
- concepts[1].m_params.push_back(m_p10_a);
- concepts[1].m_params.push_back(m_p11_a);
concepts[1].m_params.push_back(m_p01_a);
+ concepts[1].m_params.push_back(m_p11_a);
+ concepts[1].m_params.push_back(m_p10_a);
+ concepts[1].m_params.push_back(m_p00_a);
+ concepts[1].m_label = " Dst";
concepts[2].m_type = TParamUIConcept::VECTOR;
concepts[2].m_params.push_back(m_p00_b);
@@ -444,38 +446,6 @@ void FreeDistortBaseFx::getParamUIs(TParamUIConcept *&concepts, int &length) {
concepts[5].m_type = TParamUIConcept::VECTOR;
concepts[5].m_params.push_back(m_p11_b);
concepts[5].m_params.push_back(m_p11_a);
-
- concepts[6].m_type = TParamUIConcept::POINT;
- concepts[6].m_label = "Bottom Left Src";
- concepts[6].m_params.push_back(m_p00_b);
-
- concepts[7].m_type = TParamUIConcept::POINT;
- concepts[7].m_label = "Bottom Right Src";
- concepts[7].m_params.push_back(m_p10_b);
-
- concepts[8].m_type = TParamUIConcept::POINT;
- concepts[8].m_label = "Top Left Src";
- concepts[8].m_params.push_back(m_p01_b);
-
- concepts[9].m_type = TParamUIConcept::POINT;
- concepts[9].m_label = "Top Right Src";
- concepts[9].m_params.push_back(m_p11_b);
-
- concepts[10].m_type = TParamUIConcept::POINT;
- concepts[10].m_label = "Bottom Left Dst";
- concepts[10].m_params.push_back(m_p00_a);
-
- concepts[11].m_type = TParamUIConcept::POINT;
- concepts[11].m_label = "Bottom Right Dst";
- concepts[11].m_params.push_back(m_p10_a);
-
- concepts[12].m_type = TParamUIConcept::POINT;
- concepts[12].m_label = "Top Left Dst";
- concepts[12].m_params.push_back(m_p01_a);
-
- concepts[13].m_type = TParamUIConcept::POINT;
- concepts[13].m_label = "Top Right Dst";
- concepts[13].m_params.push_back(m_p11_a);
}
// ------------------------------------------------------------------------
@@ -672,7 +642,7 @@ void doBlur(TRasterPT &r, double blur0, double blur1, double transp0,
r->unlock();
delete[] row;
}
-}
+} // namespace
// ------------------------------------------------------------------------
diff --git a/toonz/sources/stdfx/iwa_corridorgradientfx.cpp b/toonz/sources/stdfx/iwa_corridorgradientfx.cpp
new file mode 100644
index 0000000..96e826c
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_corridorgradientfx.cpp
@@ -0,0 +1,378 @@
+#include "iwa_corridorgradientfx.h"
+
+#include "trop.h"
+#include "tparamuiconcept.h"
+#include "tspectrumparam.h"
+#include "gradients.h"
+
+#include
+
+#include
+#include
+
+//------------------------------------------------------------
+
+Iwa_CorridorGradientFx::Iwa_CorridorGradientFx()
+ : m_shape(new TIntEnumParam(0, "Quadrangle"))
+ , m_innerColor(TPixel32::White)
+ , m_outerColor(TPixel32::Black)
+ , m_curveType(new TIntEnumParam()) {
+ for (int inout = 0; inout < 2; inout++) {
+ double size = (inout == 0) ? 50. : 400.;
+ std::string inout_str = (inout == 0) ? "_in" : "_out";
+
+ for (int c = 0; c < 4; c++) {
+ Qt::Corner corner = (Qt::Corner)c;
+ TPointD basePos(1, 1);
+ if (corner == Qt::TopLeftCorner || corner == Qt::BottomLeftCorner)
+ basePos.x *= -1;
+ if (corner == Qt::BottomLeftCorner || corner == Qt::BottomRightCorner)
+ basePos.y *= -1;
+
+ m_points[inout][corner] = basePos * size;
+
+ m_points[inout][corner]->getX()->setMeasureName("fxLength");
+ m_points[inout][corner]->getY()->setMeasureName("fxLength");
+
+ std::string TB_str =
+ (corner == Qt::TopLeftCorner || corner == Qt::TopRightCorner)
+ ? "top"
+ : "bottom";
+ std::string LR_str =
+ (corner == Qt::TopLeftCorner || corner == Qt::BottomLeftCorner)
+ ? "_left"
+ : "_right";
+
+ bindParam(this, TB_str + LR_str + inout_str, m_points[inout][corner]);
+ }
+ }
+
+ m_shape->addItem(1, "Circle");
+ bindParam(this, "shape", m_shape);
+
+ m_curveType->addItem(EaseInOut, "Ease In-Out");
+ m_curveType->addItem(Linear, "Linear");
+ m_curveType->addItem(EaseIn, "Ease In");
+ m_curveType->addItem(EaseOut, "Ease Out");
+ m_curveType->setDefaultValue(Linear);
+ m_curveType->setValue(Linear);
+ bindParam(this, "curveType", m_curveType);
+
+ bindParam(this, "inner_color", m_innerColor);
+ bindParam(this, "outer_color", m_outerColor);
+}
+
+//------------------------------------------------------------
+
+bool Iwa_CorridorGradientFx::doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &ri) {
+ bBox = TConsts::infiniteRectD;
+ return true;
+}
+
+//------------------------------------------------------------
+
+namespace {
+
+QPointF toQPointF(const TPointD &p) { return QPointF(p.x, p.y); }
+
+double WedgeProduct(const TPointD v, const TPointD w) {
+ return v.x * w.y - v.y * w.x;
+}
+
+struct BilinearParam {
+ TPointD p0, b1, b2, b3;
+};
+
+//------------------------------------------------------------
+
+double getFactor(const TPointD &p, const BilinearParam ¶m,
+ const GradientCurveType type) {
+ double t;
+ TPointD q = p - param.p0;
+ // Set up quadratic formula
+ float A = WedgeProduct(param.b2, param.b3);
+ float B = WedgeProduct(param.b3, q) - WedgeProduct(param.b1, param.b2);
+ float C = WedgeProduct(param.b1, q);
+
+ // Solve for v
+ if (std::abs(A) < 0.001) {
+ // Linear form
+ t = -C / B;
+ } else {
+ // Quadratic form
+ float discrim = B * B - 4 * A * C;
+ t = 0.5 * (-B - std::sqrt(discrim)) / A;
+ }
+ 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;
+}
+
+//------------------------------------------------------------
+
+template
+void doQuadrangleT(RASTER ras, TDimensionI dim, TPointD pos[2][4],
+ const TSpectrumT &spectrum, GradientCurveType type) {
+ auto buildPolygon = [&](QPolygonF &pol, Qt::Corner c1, Qt::Corner c2) {
+ pol << toQPointF(pos[0][(int)c1]) << toQPointF(pos[1][(int)c1])
+ << toQPointF(pos[1][(int)c2]) << toQPointF(pos[0][(int)c2]);
+ };
+
+ auto buildBilinearParam = [&](BilinearParam &bp, Qt::Corner c1,
+ Qt::Corner c2) {
+ bp.p0 = pos[0][(int)c1];
+ bp.b1 = pos[0][(int)c2] - pos[0][(int)c1];
+ bp.b2 = pos[1][(int)c1] - pos[0][(int)c1];
+ bp.b3 =
+ pos[0][(int)c1] - pos[0][(int)c2] - pos[1][(int)c1] + pos[1][(int)c2];
+ };
+
+ std::array polygons;
+ std::array params;
+
+ // Top
+ buildPolygon(polygons[0], Qt::TopLeftCorner, Qt::TopRightCorner);
+ buildBilinearParam(params[0], Qt::TopLeftCorner, Qt::TopRightCorner);
+ // Left
+ buildPolygon(polygons[1], Qt::BottomLeftCorner, Qt::TopLeftCorner);
+ buildBilinearParam(params[1], Qt::BottomLeftCorner, Qt::TopLeftCorner);
+ // Bottom
+ buildPolygon(polygons[2], Qt::BottomRightCorner, Qt::BottomLeftCorner);
+ buildBilinearParam(params[2], Qt::BottomRightCorner, Qt::BottomLeftCorner);
+ // Right
+ buildPolygon(polygons[3], Qt::TopRightCorner, Qt::BottomRightCorner);
+ buildBilinearParam(params[3], Qt::TopRightCorner, Qt::BottomRightCorner);
+
+ QPolygonF innerPolygon;
+ innerPolygon << toQPointF(pos[0][Qt::TopLeftCorner])
+ << toQPointF(pos[0][Qt::TopRightCorner])
+ << toQPointF(pos[0][Qt::BottomRightCorner])
+ << toQPointF(pos[0][Qt::BottomLeftCorner]);
+
+ ras->lock();
+ for (int j = 0; j < ras->getLy(); j++) {
+ PIXEL *pix = ras->pixels(j);
+ PIXEL *endPix = pix + ras->getLx();
+ int i = 0;
+ while (pix < endPix) {
+ QPointF p(i, j);
+
+ double factor;
+ bool found = false;
+ for (int edge = 0; edge < 4; edge++) {
+ if (polygons[edge].containsPoint(p, Qt::WindingFill)) {
+ factor = getFactor(TPointD(i, j), params.at(edge), type);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ if (innerPolygon.containsPoint(p, Qt::WindingFill))
+ factor = 0.0;
+ else
+ factor = 1.0;
+ }
+
+ *pix++ = spectrum.getPremultipliedValue(factor);
+ i++;
+ }
+ }
+ ras->unlock();
+}
+
+//------------------------------------------------------------
+
+template
+void doCircleT(RASTER ras, TDimensionI dim, TPointD pos[2][4],
+ const TSpectrumT &spectrum, GradientCurveType type) {
+ auto lerp = [](TPointD p1, TPointD p2, double f) {
+ return p1 * (1 - f) + p2 * f;
+ };
+ auto bilinearPos = [&](TPointD uv, int inout) {
+ return lerp(lerp(pos[inout][Qt::BottomLeftCorner],
+ pos[inout][Qt::BottomRightCorner], uv.x),
+ lerp(pos[inout][Qt::TopLeftCorner],
+ pos[inout][Qt::TopRightCorner], uv.x),
+ uv.y);
+ };
+
+ const int DIVNUM = 36;
+
+ std::array innerPos;
+ std::array outerPos;
+ double tmpRadius = std::sqrt(2.0) / 2.0;
+ for (int div = 0; div < DIVNUM; div++) {
+ double angle = 2.0 * M_PI * (double)div / (double)DIVNUM;
+ // circle position in uv coordinate
+ TPointD uv(tmpRadius * std::cos(angle) + 0.5,
+ tmpRadius * std::sin(angle) + 0.5);
+ // compute inner and outer circle positions by bilinear interpolation
+ // using uv coordinate values.
+ innerPos[div] = bilinearPos(uv, 0);
+ outerPos[div] = bilinearPos(uv, 1);
+ }
+
+ // - - - - - - - -
+
+ auto buildPolygon = [&](QPolygonF &pol, int id1, int id2) {
+ pol << toQPointF(innerPos[id2]) << toQPointF(outerPos[id2])
+ << toQPointF(outerPos[id1]) << toQPointF(innerPos[id1]);
+ };
+
+ auto buildBilinearParam = [&](BilinearParam &bp, int id1, int id2) {
+ bp.p0 = innerPos[id2];
+ bp.b1 = innerPos[id1] - innerPos[id2];
+ bp.b2 = outerPos[id2] - innerPos[id2];
+ bp.b3 = innerPos[id2] - innerPos[id1] - outerPos[id2] + outerPos[id1];
+ };
+ std::array polygons;
+ std::array params;
+ QPolygonF innerPolygon;
+ for (int div = 0; div < DIVNUM; div++) {
+ int next_div = (div == DIVNUM - 1) ? 0 : div + 1;
+ // create polygon and bilinear parameters for each piece surrounding the
+ // circle
+ buildPolygon(polygons[div], div, next_div);
+ buildBilinearParam(params[div], div, next_div);
+ // create inner circle polygon
+ innerPolygon << toQPointF(innerPos[div]);
+ }
+
+ // - - - ok, ready to render
+
+ ras->lock();
+
+ for (int j = 0; j < ras->getLy(); j++) {
+ PIXEL *pix = ras->pixels(j);
+ PIXEL *endPix = pix + ras->getLx();
+ int i = 0;
+ while (pix < endPix) {
+ QPointF p(i, j);
+ double factor;
+ bool found = false;
+ for (int div = 0; div < DIVNUM; div++) {
+ // check if the point is inside of the surrounding pieces
+ if (polygons[div].containsPoint(p, Qt::WindingFill)) {
+ // compute factor by invert bilinear interpolation
+ factor = getFactor(TPointD(i, j), params.at(div), type);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ if (innerPolygon.containsPoint(p, Qt::WindingFill))
+ factor = 0.0;
+ else
+ factor = 1.0;
+ }
+
+ *pix++ = spectrum.getPremultipliedValue(factor);
+
+ i++;
+ }
+ }
+ ras->unlock();
+}
+
+}; // namespace
+
+//------------------------------------------------------------
+
+void Iwa_CorridorGradientFx::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
+ TPointD pos[2][4];
+ 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);
+ for (int inout = 0; inout < 2; inout++) {
+ for (int c = 0; c < 4; c++) {
+ TPointD _point = m_points[inout][c]->getValue(frame);
+ pos[inout][c] = aff * _point -
+ (tile.m_pos + tile.getRaster()->getCenterD()) + dimOffset;
+ }
+ }
+
+ std::vector colors = {
+ TSpectrum::ColorKey(0, m_innerColor->getValue(frame)),
+ TSpectrum::ColorKey(1, m_outerColor->getValue(frame))};
+ TSpectrumParamP m_colors = TSpectrumParamP(colors);
+
+ tile.getRaster()->clear();
+ TRaster32P outRas32 = (TRaster32P)tile.getRaster();
+ TRaster64P outRas64 = (TRaster64P)tile.getRaster();
+ if (m_shape->getValue() == 0) { // Quadrangle
+ if (outRas32)
+ doQuadrangleT(
+ outRas32, dimOut, pos, m_colors->getValue(frame),
+ (GradientCurveType)m_curveType->getValue());
+ else if (outRas64)
+ doQuadrangleT(
+ outRas64, dimOut, pos, m_colors->getValue64(frame),
+ (GradientCurveType)m_curveType->getValue());
+ } else { // m_shape == 1 : Circle
+ if (outRas32)
+ doCircleT(
+ outRas32, dimOut, pos, m_colors->getValue(frame),
+ (GradientCurveType)m_curveType->getValue());
+ else if (outRas64)
+ doCircleT(
+ outRas64, dimOut, pos, m_colors->getValue64(frame),
+ (GradientCurveType)m_curveType->getValue());
+ }
+}
+
+//------------------------------------------------------------
+
+void Iwa_CorridorGradientFx::getParamUIs(TParamUIConcept *&concepts,
+ int &length) {
+ concepts = new TParamUIConcept[length = 6];
+
+ int vectorUiIdOffset = 2;
+
+ std::array loopIds{Qt::TopLeftCorner, Qt::TopRightCorner,
+ Qt::BottomRightCorner,
+ Qt::BottomLeftCorner};
+
+ for (int inout = 0; inout < 2; inout++) {
+ concepts[inout].m_type = TParamUIConcept::QUAD;
+
+ for (int c = 0; c < 4; c++) {
+ Qt::Corner corner = loopIds[c];
+
+ // quad ui
+ concepts[inout].m_params.push_back(m_points[inout][(int)corner]);
+ concepts[inout].m_label = (inout == 0) ? " In" : " Out";
+
+ // vector ui
+ if (inout == 0)
+ concepts[vectorUiIdOffset + (int)corner].m_type =
+ TParamUIConcept::VECTOR;
+ concepts[vectorUiIdOffset + (int)corner].m_params.push_back(
+ m_points[inout][(int)corner]);
+ }
+ }
+}
+
+//------------------------------------------------------------
+
+FX_PLUGIN_IDENTIFIER(Iwa_CorridorGradientFx, "iwa_CorridorGradientFx");
diff --git a/toonz/sources/stdfx/iwa_corridorgradientfx.h b/toonz/sources/stdfx/iwa_corridorgradientfx.h
new file mode 100644
index 0000000..c16ecc3
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_corridorgradientfx.h
@@ -0,0 +1,31 @@
+#pragma once
+#ifndef IWA_CORRIDORGRADIENTFX_H
+#define IWA_CORRIDORGRADIENTFX_H
+
+#include "tfxparam.h"
+#include "stdfx.h"
+#include "tparamset.h"
+
+class Iwa_CorridorGradientFx final : public TStandardZeraryFx {
+ FX_PLUGIN_DECLARATION(Iwa_CorridorGradientFx)
+
+ TIntEnumParamP m_shape;
+ TIntEnumParamP m_curveType;
+ TPointParamP m_points[2][4]; // [in/out][Qt::Corner]
+
+ TPixelParamP m_innerColor;
+ TPixelParamP m_outerColor;
+
+public:
+ Iwa_CorridorGradientFx();
+
+ 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/tnztools/edittoolgadgets.cpp b/toonz/sources/tnztools/edittoolgadgets.cpp
index 3fb6f5d..c3c30ee 100644
--- a/toonz/sources/tnztools/edittoolgadgets.cpp
+++ b/toonz/sources/tnztools/edittoolgadgets.cpp
@@ -30,8 +30,13 @@ int getDevPixRatio() {
static int devPixRatio = QApplication::desktop()->devicePixelRatio();
return devPixRatio;
}
+
+TPointD hadamard(const TPointD &v1, const TPointD &v2) {
+ return TPointD(v1.x * v2.x, v1.y * v2.y);
}
+} // namespace
+
//*************************************************************************************
// FxGadgetUndo definition
//*************************************************************************************
@@ -131,13 +136,14 @@ public:
// FxGadget implementation
//*************************************************************************************
-FxGadget::FxGadget(FxGadgetController *controller)
+FxGadget::FxGadget(FxGadgetController *controller, int handleCount)
: m_id(-1)
- , m_selected(false)
+ , m_selected(-1)
, m_controller(controller)
, m_pixelSize(1)
, m_undo(0)
- , m_scaleFactor(1) {
+ , m_scaleFactor(1)
+ , m_handleCount(handleCount) {
controller->assignId(this);
}
@@ -592,7 +598,7 @@ void DiamondFxGadget::draw(bool picking) {
//---------------------------------------------------------------------------
void DiamondFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &) {
- double sz = fabs(pos.x) + fabs(pos.y);
+ double sz = fabs(pos.x) + fabs(pos.y);
if (sz < 0.1) sz = 0.1;
setValue(m_param, sz);
}
@@ -841,12 +847,11 @@ public:
class VectorFxGadget final : public FxGadget {
TPointParamP m_pa, m_pb;
- int m_selected;
public:
VectorFxGadget(FxGadgetController *controller, const TPointParamP &pa,
const TPointParamP &pb)
- : FxGadget(controller), m_pa(pa), m_pb(pb), m_selected(0) {
+ : FxGadget(controller), m_pa(pa), m_pb(pb) {
addParam(pa->getX());
addParam(pa->getY());
addParam(pb->getX());
@@ -859,7 +864,7 @@ public:
glColor3dv(m_selectedColor);
else
glColor3d(0, 0, 1);
- glPushName(getId());
+ // glPushName(getId());
double pixelSize = getPixelSize();
TPointD pa = getValue(m_pa);
TPointD pb = getValue(m_pb);
@@ -882,11 +887,11 @@ public:
}
tglDrawSegment(pbb, pbb - u * a + v * b);
tglDrawSegment(pbb, pbb - u * a - v * b);
- drawDot(pa);
- drawDot(pb);
- } else
- drawDot(pa);
- glPopName();
+ // drawDot(pa);
+ // drawDot(pb);
+ } // else
+ // drawDot(pa);
+ // glPopName();
}
void leftButtonDown(const TPointD &pos, const TMouseEvent &) override {}
@@ -897,53 +902,269 @@ public:
//=============================================================================
class QuadFxGadget final : public FxGadget {
- TPointParamP m_pa, m_pb, m_pc, m_pd;
+ TPointParamP m_TL, m_TR, m_BR, m_BL;
+
+ enum HANDLE {
+ Body = 0,
+ TopLeft,
+ TopRight,
+ BottomRight,
+ BottomLeft,
+ TopEdge,
+ RightEdge,
+ BottomEdge,
+ LeftEdge,
+ None
+ } m_handle = None;
+
+ TPointD m_pivot;
+ TPointD m_dragStartPos;
+ TPointD m_startTL, m_startTR, m_startBR, m_startBL;
public:
- QuadFxGadget(FxGadgetController *controller, const TPointParamP &pa,
- const TPointParamP &pb, const TPointParamP &pc,
- const TPointParamP &pd)
- : FxGadget(controller), m_pa(pa), m_pb(pb), m_pc(pc), m_pd(pd) {
- addParam(pa->getX());
- addParam(pa->getY());
- addParam(pb->getX());
- addParam(pb->getY());
- addParam(pc->getX());
- addParam(pc->getY());
- addParam(pd->getX());
- addParam(pd->getY());
+ QuadFxGadget(FxGadgetController *controller, const TPointParamP &topLeft,
+ const TPointParamP &topRight, const TPointParamP &bottomRight,
+ const TPointParamP &bottomLeft)
+ : FxGadget(controller, 9)
+ , m_TL(topLeft)
+ , m_TR(topRight)
+ , m_BR(bottomRight)
+ , m_BL(bottomLeft) {
+ addParam(topLeft->getX());
+ addParam(topLeft->getY());
+ addParam(topRight->getX());
+ addParam(topRight->getY());
+ addParam(bottomRight->getX());
+ addParam(bottomRight->getY());
+ addParam(bottomLeft->getX());
+ addParam(bottomLeft->getY());
}
void draw(bool picking) override {
+ int idBase = getId();
+
+ auto setColorById = [&](int id) {
+ if (isSelected(id))
+ glColor3dv(m_selectedColor);
+ else
+ glColor3d(0, 0, 1);
+ };
+
+ auto id2Str = [](const HANDLE handleId) -> std::string {
+ switch (handleId) {
+ case TopLeft:
+ return "Top Left";
+ case TopRight:
+ return "Top Right";
+ case BottomRight:
+ return "Bottom Right";
+ case BottomLeft:
+ return "Bottom Left";
+ default:
+ return "";
+ }
+ };
+
+ auto drawPoint = [&](const TPointD &pos, int id) {
+ setColorById(id);
+ glPushName(idBase + id);
+ double unit = getPixelSize();
+ glPushMatrix();
+ glTranslated(pos.x, pos.y, 0);
+ double r = unit * 3;
+ tglDrawRect(-r, -r, r, r);
+ glPopMatrix();
+ glPopName();
+
+ if (isSelected(id) && id >= TopLeft && id <= BottomLeft) {
+ drawTooltip(pos + TPointD(7, 3) * unit,
+ id2Str((HANDLE)id) + getLabel());
+ }
+ };
+
setPixelSize();
- if (isSelected())
- glColor3dv(m_selectedColor);
- else
- glColor3d(0, 0, 1);
- // glPushName(getId());
- double pixelSize = getPixelSize();
- TPointD pa = getValue(m_pa);
- TPointD pb = getValue(m_pb);
- TPointD pc = getValue(m_pc);
- TPointD pd = getValue(m_pd);
+
+ // lines for moving all vertices
+ glPushName(idBase + Body);
+ setColorById(Body);
+ double pixelSize = getPixelSize();
+ TPointD topLeft = getValue(m_TL);
+ TPointD topRight = getValue(m_TR);
+ TPointD bottomRight = getValue(m_BR);
+ TPointD bottomLeft = getValue(m_BL);
glLineStipple(1, 0xCCCC);
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINE_STRIP);
- tglVertex(pa);
- tglVertex(pb);
- tglVertex(pc);
- tglVertex(pd);
- tglVertex(pa);
+ tglVertex(topLeft);
+ tglVertex(topRight);
+ tglVertex(bottomRight);
+ tglVertex(bottomLeft);
+ tglVertex(topLeft);
glEnd();
glDisable(GL_LINE_STIPPLE);
- // glPopName();
+ glPopName();
+
+ // corners
+ drawPoint(topLeft, TopLeft);
+ drawPoint(topRight, TopRight);
+ drawPoint(bottomRight, BottomRight);
+ drawPoint(bottomLeft, BottomLeft);
+
+ // center of the edges
+ drawPoint((topLeft + topRight) * 0.5, TopEdge);
+ drawPoint((topRight + bottomRight) * 0.5, RightEdge);
+ drawPoint((bottomRight + bottomLeft) * 0.5, BottomEdge);
+ drawPoint((bottomLeft + topLeft) * 0.5, LeftEdge);
}
- void leftButtonDown(const TPointD &pos, const TMouseEvent &) override {}
- void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override {}
- void leftButtonUp(const TPointD &pos, const TMouseEvent &) 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;
};
+//---------------------------------------------------------------------------
+
+void QuadFxGadget::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
+ m_handle = (HANDLE)m_selected;
+ m_dragStartPos = pos;
+ m_startTL = getValue(m_TL);
+ m_startTR = getValue(m_TR);
+ m_startBR = getValue(m_BR);
+ m_startBL = getValue(m_BL);
+ m_pivot = (m_startTL + m_startTR + m_startBR + m_startBL) * 0.25;
+}
+
+//---------------------------------------------------------------------------
+
+void QuadFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
+ TPointD offset = pos - m_dragStartPos;
+
+ auto scaleShape = [&](const TPointD &start, const TPointD &pivot) {
+ TPointD startVec = start - pivot;
+ TPointD endVec = start + offset - pivot;
+ TPointD scaleFac((startVec.x == 0.0) ? 1.0 : endVec.x / startVec.x,
+ (startVec.y == 0.0) ? 1.0 : endVec.y / startVec.y);
+ if (e.isShiftPressed()) {
+ if (std::abs(scaleFac.x) > std::abs(scaleFac.y))
+ scaleFac.y = scaleFac.x;
+ else
+ scaleFac.x = scaleFac.y;
+ }
+ if (m_startTL != pivot)
+ setValue(m_TL, pivot + hadamard((m_startTL - pivot), scaleFac));
+ if (m_startTR != pivot)
+ setValue(m_TR, pivot + hadamard((m_startTR - pivot), scaleFac));
+ if (m_startBR != pivot)
+ setValue(m_BR, pivot + hadamard((m_startBR - pivot), scaleFac));
+ if (m_startBL != pivot)
+ setValue(m_BL, pivot + hadamard((m_startBL - pivot), scaleFac));
+ };
+
+ auto doCorner = [&](const TPointParamP point, const TPointD &start,
+ const TPointD &opposite) {
+ if (e.isCtrlPressed())
+ setValue(point, start + offset);
+ else if (e.isAltPressed())
+ scaleShape(start, m_pivot);
+ else
+ scaleShape(start, opposite);
+ };
+
+ auto doEdge = [&](const TPointParamP p1, const TPointParamP p2) {
+ if (e.isShiftPressed()) {
+ if (std::abs(offset.x) > std::abs(offset.y))
+ offset.y = 0;
+ else
+ offset.x = 0;
+ }
+ if (m_TL == p1 || m_TL == p2)
+ setValue(m_TL, m_startTL + offset);
+ else if (e.isAltPressed())
+ setValue(m_TL, m_startTL - offset);
+ if (m_TR == p1 || m_TR == p2)
+ setValue(m_TR, m_startTR + offset);
+ else if (e.isAltPressed())
+ setValue(m_TR, m_startTR - offset);
+ if (m_BR == p1 || m_BR == p2)
+ setValue(m_BR, m_startBR + offset);
+ else if (e.isAltPressed())
+ setValue(m_BR, m_startBR - offset);
+ if (m_BL == p1 || m_BL == p2)
+ setValue(m_BL, m_startBL + offset);
+ else if (e.isAltPressed())
+ setValue(m_BL, m_startBL - offset);
+ };
+
+ auto pointRotate = [&](const TPointD pos, const double angle) {
+ TPointD p = pos - m_pivot;
+ return m_pivot + TPointD(p.x * std::cos(angle) - p.y * std::sin(angle),
+ p.x * std::sin(angle) + p.y * std::cos(angle));
+ };
+
+ switch (m_handle) {
+ case Body:
+ if (e.isCtrlPressed()) { // rotate
+ TPointD startVec = m_dragStartPos - m_pivot;
+ TPointD currentVec = pos - m_pivot;
+ if (currentVec == TPointD()) return;
+ double angle = std::atan2(currentVec.y, currentVec.x) -
+ std::atan2(startVec.y, startVec.x);
+ if (e.isShiftPressed()) {
+ angle = std::round(angle / (M_PI / 2.0)) * (M_PI / 2.0);
+ }
+ setValue(m_TL, pointRotate(m_startTL, angle));
+ setValue(m_TR, pointRotate(m_startTR, angle));
+ setValue(m_BR, pointRotate(m_startBR, angle));
+ setValue(m_BL, pointRotate(m_startBL, angle));
+ } else { // translate
+ // move all shapes
+ if (e.isShiftPressed()) {
+ if (std::abs(offset.x) > std::abs(offset.y))
+ offset.y = 0;
+ else
+ offset.x = 0;
+ }
+ setValue(m_TL, m_startTL + offset);
+ setValue(m_TR, m_startTR + offset);
+ setValue(m_BR, m_startBR + offset);
+ setValue(m_BL, m_startBL + offset);
+ }
+ break;
+ case TopLeft:
+ doCorner(m_TL, m_startTL, m_startBR);
+ break;
+ case TopRight:
+ doCorner(m_TR, m_startTR, m_startBL);
+ break;
+ case BottomRight:
+ doCorner(m_BR, m_startBR, m_startTL);
+ break;
+ case BottomLeft:
+ doCorner(m_BL, m_startBL, m_startTR);
+ break;
+ case TopEdge:
+ doEdge(m_TL, m_TR);
+ break;
+ case RightEdge:
+ doEdge(m_TR, m_BR);
+ break;
+ case BottomEdge:
+ doEdge(m_BR, m_BL);
+ break;
+ case LeftEdge:
+ doEdge(m_BL, m_TL);
+ break;
+ default:
+ break;
+ }
+}
+
+//---------------------------------------------------------------------------
+
+void QuadFxGadget::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
+ m_handle = None;
+}
+
//*************************************************************************************
// FxGadgetController implementation
//*************************************************************************************
@@ -981,8 +1202,10 @@ void FxGadgetController::clearGadgets() {
void FxGadgetController::assignId(FxGadget *gadget) {
gadget->setId(m_nextId);
- m_idTable[m_nextId] = gadget;
- ++m_nextId;
+ for (int g = 0; g < gadget->getHandleCount(); g++) {
+ m_idTable[m_nextId] = gadget;
+ ++m_nextId;
+ }
}
//---------------------------------------------------------------------------
@@ -1009,9 +1232,10 @@ void FxGadgetController::selectById(unsigned int id) {
it = m_idTable.find(id);
FxGadget *selectedGadget = it != m_idTable.end() ? it->second : 0;
if (selectedGadget != m_selectedGadget) {
- if (m_selectedGadget) m_selectedGadget->select(false);
+ if (m_selectedGadget) m_selectedGadget->select(-1);
m_selectedGadget = selectedGadget;
- if (m_selectedGadget) m_selectedGadget->select(true);
+ if (m_selectedGadget)
+ m_selectedGadget->select(id - m_selectedGadget->getId());
}
}
@@ -1144,7 +1368,7 @@ void FxGadgetController::onFxSwitched() {
// before, the levels were considered as nonZeraryFx and the edit tool
// gadget was not displayed! Vinz
{
- if (zfx) fx = zfx->getZeraryFx();
+ if (zfx) fx = zfx->getZeraryFx();
m_editingNonZeraryFx = false;
}
diff --git a/toonz/sources/tnztools/edittoolgadgets.h b/toonz/sources/tnztools/edittoolgadgets.h
index 27e6e12..a26141f 100644
--- a/toonz/sources/tnztools/edittoolgadgets.h
+++ b/toonz/sources/tnztools/edittoolgadgets.h
@@ -32,7 +32,6 @@ class TParamUIConcept;
class FxGadget : public TParamObserver {
GLuint m_id;
- bool m_selected;
std::vector m_params;
double m_pixelSize;
@@ -42,11 +41,14 @@ class FxGadget : public TParamObserver {
protected:
FxGadgetController *m_controller;
+ int m_handleCount = 1;
+ // -1 : not selected, 0~ : handle id
+ int m_selected;
public:
static GLdouble m_selectedColor[3]; // rgb
- FxGadget(FxGadgetController *controller);
+ FxGadget(FxGadgetController *controller, int handleCount = 1);
virtual ~FxGadget();
void setLabel(std::string label) { m_label = label; }
@@ -64,8 +66,11 @@ public:
void setId(GLuint id) { m_id = id; }
GLuint getId() const { return m_id; }
- void select(bool selected) { m_selected = selected; }
- bool isSelected() const { return m_selected; }
+ int getHandleCount() { return m_handleCount; }
+
+ void select(int selected) { m_selected = selected; }
+ bool isSelected() const { return m_selected >= 0; }
+ bool isSelected(int id) const { return m_selected == id; }
void setPixelSize(); // uses tglGetPixelSize2()
void setPixelSize(double pixelSize) { m_pixelSize = pixelSize; }
@@ -108,7 +113,7 @@ public:
//-----------------------------------------------------------------------------
-} // EditToolGadgets namespace
+} // namespace EditToolGadgets
class FxGadgetController final : public QObject {
Q_OBJECT