Merge pull request #3216 from pojienie/add-interpolation-in-eraser-tool

Add interpolation in vector eraser tool
This commit is contained in:
Rodney 2020-04-10 13:37:19 -05:00 committed by GitHub
commit ae6c2db1e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 28 deletions

View file

@ -229,8 +229,8 @@ static void detectCorners(const TStroke *stroke, double minDegree,
} }
} }
const double ratioLen = 2.5; const double ratioLen = 2.5;
const double ratioAngle = 0.2; const double ratioAngle = 0.2;
std::vector<std::pair<int, double>>::iterator it = corners.begin(); std::vector<std::pair<int, double>>::iterator it = corners.begin();
for (j = 1; j < (int)quadCount1; for (j = 1; j < (int)quadCount1;
@ -241,8 +241,9 @@ static void detectCorners(const TStroke *stroke, double minDegree,
continue; continue;
} }
if (j - 2 >= 0 && (corners.empty() || it == corners.begin() || if (j - 2 >= 0 &&
j - 1 != (*(it - 1)).first) && (corners.empty() || it == corners.begin() ||
j - 1 != (*(it - 1)).first) &&
j + 1 < (int)quadCount1 && j + 1 < (int)quadCount1 &&
(corners.empty() || it == corners.end() || j + 1 != (*it).first)) { (corners.empty() || it == corners.end() || j + 1 != (*it).first)) {
speed1 = stroke->getChunk(j - 2)->getSpeed(1); speed1 = stroke->getChunk(j - 2)->getSpeed(1);
@ -255,7 +256,7 @@ static void detectCorners(const TStroke *stroke, double minDegree,
if (tan1 * tan2 < 0) { if (tan1 * tan2 < 0) {
angle = 180 - asin(tcrop(vectorialProduct, -1.0, 1.0)) * M_180_PI; angle = 180 - asin(tcrop(vectorialProduct, -1.0, 1.0)) * M_180_PI;
metaCornerLen = ratioLen * (stroke->getChunk(j - 1)->getLength() + metaCornerLen = ratioLen * (stroke->getChunk(j - 1)->getLength() +
stroke->getChunk(j)->getLength()); stroke->getChunk(j)->getLength());
partialLen = 0; partialLen = 0;
bool goodAngle = false; bool goodAngle = false;
@ -1480,8 +1481,8 @@ TVectorImageP TInbetween::Imp::tween(double t) const {
len1 += step1; len1 += step1;
len2 += step2; len2 += step2;
} }
point2 = subStroke2->getThickPointAtLength(totalLen2); point2 = subStroke2->getThickPointAtLength(totalLen2);
point2 = TThickPoint(m_transformation[i].m_inverse * point2 = TThickPoint(m_transformation[i].m_inverse *
subStroke2->getThickPointAtLength(totalLen2), subStroke2->getThickPointAtLength(totalLen2),
point2.thick); point2.thick);
finalPoint = subStroke1->getThickPointAtLength(totalLen1) * (1 - t) + finalPoint = subStroke1->getThickPointAtLength(totalLen1) * (1 - t) +
@ -1554,3 +1555,20 @@ void TInbetween::Imp::transferColor(const TVectorImageP &destination) const {
TVectorImageP TInbetween::tween(double t) const { return m_imp->tween(t); } TVectorImageP TInbetween::tween(double t) const { return m_imp->tween(t); }
//------------------------------------------------------------------- //-------------------------------------------------------------------
double TInbetween::interpolation(double t, enum TweenAlgorithm algorithm) {
// in tutte le interpolazioni : s(0) = 0, s(1) = 1
switch (algorithm) {
case EaseInInterpolation: // s'(1) = 0
return t * (2 - t);
case EaseOutInterpolation: // s'(0) = 0
return t * t;
case EaseInOutInterpolation: // s'(0) = s'(1) = 0
return t * t * (3 - 2 * t);
case LinearInterpolation:
default:
return t;
}
}
//-------------------------------------------------------------------

View file

@ -24,6 +24,15 @@ class DVAPI TInbetween {
std::unique_ptr<Imp> m_imp; std::unique_ptr<Imp> m_imp;
public: public:
enum TweenAlgorithm {
LinearInterpolation,
EaseInInterpolation,
EaseOutInterpolation,
EaseInOutInterpolation
};
static double interpolation(double t, enum TweenAlgorithm);
TInbetween(const TVectorImageP firstImage, const TVectorImageP lastImage); TInbetween(const TVectorImageP firstImage, const TVectorImageP lastImage);
virtual ~TInbetween(); virtual ~TInbetween();

View file

@ -40,6 +40,8 @@ using namespace ToolUtils;
TEnv::DoubleVar EraseVectorSize("InknpaintEraseVectorSize", 10); TEnv::DoubleVar EraseVectorSize("InknpaintEraseVectorSize", 10);
TEnv::StringVar EraseVectorType("InknpaintEraseVectorType", "Normal"); TEnv::StringVar EraseVectorType("InknpaintEraseVectorType", "Normal");
TEnv::StringVar EraseVectorInterpolation("InknpaintEraseVectorInterpolation",
"Linear");
TEnv::IntVar EraseVectorSelective("InknpaintEraseVectorSelective", 0); TEnv::IntVar EraseVectorSelective("InknpaintEraseVectorSelective", 0);
TEnv::IntVar EraseVectorInvert("InknpaintEraseVectorInvert", 0); TEnv::IntVar EraseVectorInvert("InknpaintEraseVectorInvert", 0);
TEnv::IntVar EraseVectorRange("InknpaintEraseVectorRange", 0); TEnv::IntVar EraseVectorRange("InknpaintEraseVectorRange", 0);
@ -56,6 +58,11 @@ namespace {
#define POLYLINE_ERASE L"Polyline" #define POLYLINE_ERASE L"Polyline"
#define SEGMENT_ERASE L"Segment" #define SEGMENT_ERASE L"Segment"
#define LINEAR_INTERPOLATION L"Linear"
#define EASE_IN_INTERPOLATION L"Ease In"
#define EASE_OUT_INTERPOLATION L"Ease Out"
#define EASE_IN_OUT_INTERPOLATION L"Ease In/Out"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const double minDistance2 = 16.0; // 4 pixel const double minDistance2 = 16.0; // 4 pixel
@ -294,6 +301,7 @@ private:
TPropertyGroup m_prop; TPropertyGroup m_prop;
TEnumProperty m_eraseType; TEnumProperty m_eraseType;
TEnumProperty m_interpolation;
TDoubleProperty m_toolSize; TDoubleProperty m_toolSize;
TBoolProperty m_selective; TBoolProperty m_selective;
TBoolProperty m_invertOption; TBoolProperty m_invertOption;
@ -366,7 +374,8 @@ private:
EraserTool::EraserTool() EraserTool::EraserTool()
: TTool("T_Eraser") : TTool("T_Eraser")
, m_eraseType("Type:") // "W_ToolOptions_Erasetype" , m_eraseType("Type:") // "W_ToolOptions_Erasetype"
, m_interpolation("interpolation:")
, m_toolSize("Size:", 1, 1000, 10) // "W_ToolOptions_EraserToolSize" , m_toolSize("Size:", 1, 1000, 10) // "W_ToolOptions_EraserToolSize"
, m_selective("Selective", false) // "W_ToolOptions_Selective" , m_selective("Selective", false) // "W_ToolOptions_Selective"
, m_invertOption("Invert", false) // "W_ToolOptions_Invert" , m_invertOption("Invert", false) // "W_ToolOptions_Invert"
@ -392,11 +401,17 @@ EraserTool::EraserTool()
m_prop.bind(m_selective); m_prop.bind(m_selective);
m_prop.bind(m_invertOption); m_prop.bind(m_invertOption);
m_prop.bind(m_multi); m_prop.bind(m_multi);
m_prop.bind(m_interpolation);
m_interpolation.addValue(LINEAR_INTERPOLATION);
m_interpolation.addValue(EASE_IN_INTERPOLATION);
m_interpolation.addValue(EASE_OUT_INTERPOLATION);
m_interpolation.addValue(EASE_IN_OUT_INTERPOLATION);
m_selective.setId("Selective"); m_selective.setId("Selective");
m_invertOption.setId("Invert"); m_invertOption.setId("Invert");
m_multi.setId("FrameRange"); m_multi.setId("FrameRange");
m_eraseType.setId("Type"); m_eraseType.setId("Type");
m_interpolation.setId("Interpolation");
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -420,6 +435,12 @@ void EraserTool::updateTranslation() {
m_eraseType.setItemUIName(FREEHAND_ERASE, tr("Freehand")); m_eraseType.setItemUIName(FREEHAND_ERASE, tr("Freehand"));
m_eraseType.setItemUIName(POLYLINE_ERASE, tr("Polyline")); m_eraseType.setItemUIName(POLYLINE_ERASE, tr("Polyline"));
m_eraseType.setItemUIName(SEGMENT_ERASE, tr("Segment")); m_eraseType.setItemUIName(SEGMENT_ERASE, tr("Segment"));
m_interpolation.setQStringName(tr(""));
m_interpolation.setItemUIName(LINEAR_INTERPOLATION, tr("Linear"));
m_interpolation.setItemUIName(EASE_IN_INTERPOLATION, tr("Ease In"));
m_interpolation.setItemUIName(EASE_OUT_INTERPOLATION, tr("Ease Out"));
m_interpolation.setItemUIName(EASE_IN_OUT_INTERPOLATION, tr("Ease In/Out"));
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -886,6 +907,15 @@ void EraserTool::multiEraseRect(TFrameId firstFrameId, TFrameId lastFrameId,
int m = fids.size(); int m = fids.size();
assert(m > 0); assert(m > 0);
enum TInbetween::TweenAlgorithm algorithm = TInbetween::LinearInterpolation;
if (m_interpolation.getValue() == EASE_IN_INTERPOLATION) {
algorithm = TInbetween::EaseInInterpolation;
} else if (m_interpolation.getValue() == EASE_OUT_INTERPOLATION) {
algorithm = TInbetween::EaseOutInterpolation;
} else if (m_interpolation.getValue() == EASE_IN_OUT_INTERPOLATION) {
algorithm = TInbetween::EaseInOutInterpolation;
}
TUndoManager::manager()->beginBlock(); TUndoManager::manager()->beginBlock();
for (int i = 0; i < m; ++i) { for (int i = 0; i < m; ++i) {
TFrameId fid = fids[i]; TFrameId fid = fids[i];
@ -893,6 +923,7 @@ void EraserTool::multiEraseRect(TFrameId firstFrameId, TFrameId lastFrameId,
TVectorImageP img = (TVectorImageP)m_level->getFrame(fid, true); TVectorImageP img = (TVectorImageP)m_level->getFrame(fid, true);
assert(img); assert(img);
double t = m > 1 ? (double)i / (double)(m - 1) : 0.5; double t = m > 1 ? (double)i / (double)(m - 1) : 0.5;
t = TInbetween::interpolation(t, algorithm);
TRectD rect = interpolateRect(firstRect, lastRect, t); TRectD rect = interpolateRect(firstRect, lastRect, t);
// m_level->setFrame(fid, img); //necessario: se la getFrame ha scompattato // m_level->setFrame(fid, img); //necessario: se la getFrame ha scompattato
// una img compressa, senza setFrame le modifiche sulla img fatte qui // una img compressa, senza setFrame le modifiche sulla img fatte qui
@ -1097,11 +1128,12 @@ void EraserTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
//---------------------------------------------------------------------- //----------------------------------------------------------------------
bool EraserTool::onPropertyChanged(std::string propertyName) { bool EraserTool::onPropertyChanged(std::string propertyName) {
EraseVectorType = ::to_string(m_eraseType.getValue()); EraseVectorType = ::to_string(m_eraseType.getValue());
EraseVectorSize = m_toolSize.getValue(); EraseVectorInterpolation = ::to_string(m_interpolation.getValue());
EraseVectorSelective = m_selective.getValue(); EraseVectorSize = m_toolSize.getValue();
EraseVectorInvert = m_invertOption.getValue(); EraseVectorSelective = m_selective.getValue();
EraseVectorRange = m_multi.getValue(); EraseVectorInvert = m_invertOption.getValue();
EraseVectorRange = m_multi.getValue();
double x = m_toolSize.getValue(); double x = m_toolSize.getValue();
@ -1125,6 +1157,7 @@ void EraserTool::onEnter() {
if (m_firstTime) { if (m_firstTime) {
m_toolSize.setValue(EraseVectorSize); m_toolSize.setValue(EraseVectorSize);
m_eraseType.setValue(::to_wstring(EraseVectorType.getValue())); m_eraseType.setValue(::to_wstring(EraseVectorType.getValue()));
m_interpolation.setValue(::to_wstring(EraseVectorInterpolation.getValue()));
m_selective.setValue(EraseVectorSelective ? 1 : 0); m_selective.setValue(EraseVectorSelective ? 1 : 0);
m_invertOption.setValue(EraseVectorInvert ? 1 : 0); m_invertOption.setValue(EraseVectorInvert ? 1 : 0);
m_multi.setValue(EraseVectorRange ? 1 : 0); m_multi.setValue(EraseVectorRange ? 1 : 0);
@ -1537,11 +1570,21 @@ void EraserTool::doMultiErase(TFrameId &firstFrameId, TFrameId &lastFrameId,
column->getLevelRange(currentRow, startRowInXSheet, endRowInXSheet); column->getLevelRange(currentRow, startRowInXSheet, endRowInXSheet);
} }
enum TInbetween::TweenAlgorithm algorithm = TInbetween::LinearInterpolation;
if (m_interpolation.getValue() == EASE_IN_INTERPOLATION) {
algorithm = TInbetween::EaseInInterpolation;
} else if (m_interpolation.getValue() == EASE_OUT_INTERPOLATION) {
algorithm = TInbetween::EaseOutInterpolation;
} else if (m_interpolation.getValue() == EASE_IN_OUT_INTERPOLATION) {
algorithm = TInbetween::EaseInOutInterpolation;
}
TUndoManager::manager()->beginBlock(); TUndoManager::manager()->beginBlock();
for (int i = 0; i < m; ++i) { for (int i = 0; i < m; ++i) {
TFrameId fid = fids[i]; TFrameId fid = fids[i];
assert(firstFrameId <= fid && fid <= lastFrameId); assert(firstFrameId <= fid && fid <= lastFrameId);
double t = m > 1 ? (double)i / (double)(m - 1) : 0.5; double t = m > 1 ? (double)i / (double)(m - 1) : 0.5;
t = TInbetween::interpolation(t, algorithm);
// Setto il fid come corrente per notificare il cambiamento dell'immagine // Setto il fid come corrente per notificare il cambiamento dell'immagine
if (app) { if (app) {
if (app->getCurrentFrame()->isEditingScene()) if (app->getCurrentFrame()->isEditingScene())

View file

@ -2499,25 +2499,27 @@ void FilmstripCmd::inbetweenWithoutUndo(
TVectorImageP img1 = sl->getFrame(fid1, false); TVectorImageP img1 = sl->getFrame(fid1, false);
if (!img0 || !img1) return; if (!img0 || !img1) return;
enum TInbetween::TweenAlgorithm algorithm;
switch (interpolation) {
case II_Linear:
algorithm = TInbetween::LinearInterpolation;
break;
case II_EaseIn:
algorithm = TInbetween::EaseInInterpolation;
break;
case II_EaseOut:
algorithm = TInbetween::EaseOutInterpolation;
break;
case II_EaseInOut:
algorithm = TInbetween::EaseInOutInterpolation;
break;
}
TInbetween inbetween(img0, img1); TInbetween inbetween(img0, img1);
int i; int i;
for (i = ia + 1; i < ib; i++) { for (i = ia + 1; i < ib; i++) {
double t = (double)(i - ia) / (double)(ib - ia); double t = (double)(i - ia) / (double)(ib - ia);
double s = t; double s = TInbetween::interpolation(t, algorithm);
// in tutte le interpolazioni : s(0) = 0, s(1) = 1
switch (interpolation) {
case II_Linear:
break;
case II_EaseIn:
s = t * (2 - t);
break; // s'(1) = 0
case II_EaseOut:
s = t * t;
break; // s'(0) = 0
case II_EaseInOut:
s = t * t * (3 - 2 * t);
break; // s'(0) = s'(1) = 0
}
TVectorImageP vi = inbetween.tween(s); TVectorImageP vi = inbetween.tween(s);
sl->setFrame(fids[i], vi); sl->setFrame(fids[i], vi);