1 
2 
3 // TnzTools includes
4 #include "tools/toolhandle.h"
5 #include "tools/toolutils.h"
6 #include "tools/tool.h"
7 #include "tools/cursors.h"
8 
9 // TnzQt includes
10 #include "toonzqt/imageutils.h"
11 
12 // TnzLib includes
13 #include "toonz/txshlevelhandle.h"
14 #include "toonz/tframehandle.h"
15 #include "toonz/tcolumnhandle.h"
16 #include "toonz/txsheethandle.h"
17 #include "toonz/strokegenerator.h"
18 #include "toonz/txshsimplelevel.h"
19 #include "toonz/stage2.h"
20 #include "toonz/preferences.h"
21 
22 // TnzBase includes
23 #include "tenv.h"
24 
25 // TnzCore includes
26 #include "tmathutil.h"
27 #include "tundo.h"
28 #include "tstroke.h"
29 #include "tvectorimage.h"
30 #include "ttoonzimage.h"
31 #include "tproperty.h"
32 #include "tgl.h"
33 #include "tinbetween.h"
34 #include "drawutil.h"
35 
36 // Qt includes
37 #include <QCoreApplication>  // For Qt translation support
38 
39 using namespace ToolUtils;
40 
41 TEnv::DoubleVar EraseVectorSize("InknpaintEraseVectorSize", 10);
42 TEnv::StringVar EraseVectorType("InknpaintEraseVectorType", "Normal");
43 TEnv::StringVar EraseVectorInterpolation("InknpaintEraseVectorInterpolation",
44                                          "Linear");
45 TEnv::IntVar EraseVectorSelective("InknpaintEraseVectorSelective", 0);
46 TEnv::IntVar EraseVectorInvert("InknpaintEraseVectorInvert", 0);
47 TEnv::IntVar EraseVectorRange("InknpaintEraseVectorRange", 0);
48 
49 //=============================================================================
50 // Eraser Tool
51 //=============================================================================
52 
53 namespace {
54 
55 #define NORMAL_ERASE L"Normal"
56 #define RECT_ERASE L"Rectangular"
57 #define FREEHAND_ERASE L"Freehand"
58 #define POLYLINE_ERASE L"Polyline"
59 #define SEGMENT_ERASE L"Segment"
60 
61 #define LINEAR_INTERPOLATION L"Linear"
62 #define EASE_IN_INTERPOLATION L"Ease In"
63 #define EASE_OUT_INTERPOLATION L"Ease Out"
64 #define EASE_IN_OUT_INTERPOLATION L"Ease In/Out"
65 
66 //-----------------------------------------------------------------------------
67 
68 const double minDistance2 = 16.0;  // 4 pixel
69 
70 //-----------------------------------------------------------------------------
71 
mapToVector(const std::map<int,VIStroke * > & theMap,std::vector<int> & theVect)72 void mapToVector(const std::map<int, VIStroke *> &theMap,
73                  std::vector<int> &theVect) {
74   assert(theMap.size() == theVect.size());
75   std::map<int, VIStroke *>::const_iterator it = theMap.begin();
76   UINT i                                       = 0;
77   for (; it != theMap.end(); ++it) {
78     theVect[i++] = it->first;
79   }
80 }
81 
82 //-----------------------------------------------------------------------------
83 
84 class UndoEraser final : public ToolUtils::TToolUndo {
85   std::vector<TFilledRegionInf> m_oldFillInformation, m_newFillInformation;
86 
87   int m_row;
88   int m_column;
89 
90 public:
91   std::map<int, VIStroke *> m_originalStrokes;
92   std::map<int, VIStroke *> m_newStrokes;
93 
UndoEraser(TXshSimpleLevel * level,const TFrameId & frameId)94   UndoEraser(TXshSimpleLevel *level, const TFrameId &frameId)
95       : ToolUtils::TToolUndo(level, frameId) {
96     TVectorImageP image = level->getFrame(m_frameId, true);
97     if (!image) return;
98     TTool::Application *app = TTool::getApplication();
99     if (app) {
100       m_row    = app->getCurrentFrame()->getFrame();
101       m_column = app->getCurrentColumn()->getColumnIndex();
102     }
103     ImageUtils::getFillingInformationInArea(image, m_oldFillInformation,
104                                             image->getBBox());
105   }
106 
onAdd()107   void onAdd() override {
108     TVectorImageP image = m_level->getFrame(m_frameId, true);
109     assert(!!image);
110     if (!image) return;
111     ImageUtils::getFillingInformationInArea(image, m_newFillInformation,
112                                             image->getBBox());
113   }
114 
~UndoEraser()115   ~UndoEraser() {
116     std::map<int, VIStroke *>::const_iterator it;
117     for (it = m_originalStrokes.begin(); it != m_originalStrokes.end(); ++it)
118       deleteVIStroke(it->second);
119     for (it = m_newStrokes.begin(); it != m_newStrokes.end(); ++it)
120       deleteVIStroke(it->second);
121   }
122 
addOldStroke(int index,VIStroke * stroke)123   void addOldStroke(int index, VIStroke *stroke) {
124     VIStroke *s = cloneVIStroke(stroke);
125     m_originalStrokes.insert(std::map<int, VIStroke *>::value_type(index, s));
126   }
127 
addNewStroke(int index,VIStroke * stroke)128   void addNewStroke(int index, VIStroke *stroke) {
129     VIStroke *s = cloneVIStroke(stroke);
130     m_newStrokes.insert(std::map<int, VIStroke *>::value_type(index, s));
131   }
132 
undo() const133   void undo() const override {
134     TTool::Application *app = TTool::getApplication();
135     if (!app) return;
136 
137     TFrameId currentFid;
138     if (app->getCurrentFrame()->isEditingScene()) {
139       app->getCurrentColumn()->setColumnIndex(m_column);
140       app->getCurrentFrame()->setFrame(m_row);
141       currentFid = TFrameId(m_row + 1);
142     } else {
143       app->getCurrentFrame()->setFid(m_frameId);
144       currentFid = m_frameId;
145     }
146 
147     TVectorImageP image = m_level->getFrame(m_frameId, true);
148     assert(image);
149     if (!image) return;
150     QMutexLocker lock(image->getMutex());
151     std::vector<int> newStrokeIndex(m_newStrokes.size());
152     mapToVector(m_newStrokes, newStrokeIndex);
153 
154     image->removeStrokes(newStrokeIndex, true, false);
155 
156     std::map<int, VIStroke *>::const_iterator it = m_originalStrokes.begin();
157     UINT i                                       = 0;
158     VIStroke *s;
159     for (; it != m_originalStrokes.end(); ++it) {
160       s = cloneVIStroke(it->second);
161       image->insertStrokeAt(s, it->first);
162     }
163 
164     if (image->isComputedRegionAlmostOnce())
165       image->findRegions();  // in futuro togliere. Serve perche' la
166                              // removeStrokes, se gli si dice
167     // di non calcolare le regioni, e' piu' veloce ma poi chrash tutto
168 
169     UINT size = m_oldFillInformation.size();
170     if (!size) {
171       app->getCurrentXsheet()->notifyXsheetChanged();
172       notifyImageChanged();
173       return;
174     }
175 
176     TRegion *reg;
177     i = 0;
178     for (; i < size; i++) {
179       reg = image->getRegion(m_oldFillInformation[i].m_regionId);
180       assert(reg);
181       if (reg) reg->setStyle(m_oldFillInformation[i].m_styleId);
182     }
183     app->getCurrentXsheet()->notifyXsheetChanged();
184     notifyImageChanged();
185   }
186 
redo() const187   void redo() const override {
188     TTool::Application *app = TTool::getApplication();
189     if (!app) return;
190 
191     TFrameId currentFid;
192     if (app->getCurrentFrame()->isEditingScene()) {
193       app->getCurrentColumn()->setColumnIndex(m_column);
194       app->getCurrentFrame()->setFrame(m_row);
195       currentFid = TFrameId(m_row + 1);
196     } else {
197       app->getCurrentFrame()->setFid(m_frameId);
198       currentFid = m_frameId;
199     }
200     TVectorImageP image = m_level->getFrame(m_frameId, true);
201     assert(image);
202     if (!image) return;
203 
204     QMutexLocker lock(image->getMutex());
205     std::vector<int> oldStrokeIndex(m_originalStrokes.size());
206     mapToVector(m_originalStrokes, oldStrokeIndex);
207 
208     image->removeStrokes(oldStrokeIndex, true, false);
209 
210     std::map<int, VIStroke *>::const_iterator it = m_newStrokes.begin();
211     UINT i                                       = 0;
212     VIStroke *s;
213     for (; it != m_newStrokes.end(); ++it) {
214       s = cloneVIStroke(it->second);
215       image->insertStrokeAt(s, it->first);
216     }
217 
218     if (image->isComputedRegionAlmostOnce())
219       image->findRegions();  // in futuro togliere. Serve perche' la
220                              // removeStrokes, se gli si dice
221     // di non calcolare le regioni, e' piu' veloce ma poi chrash tutto
222 
223     UINT size = m_newFillInformation.size();
224     if (!size) {
225       app->getCurrentXsheet()->notifyXsheetChanged();
226       notifyImageChanged();
227       return;
228     }
229 
230     TRegion *reg;
231     i = 0;
232     for (; i < size; i++) {
233       reg = image->getRegion(m_newFillInformation[i].m_regionId);
234       assert(reg);
235       if (reg) reg->setStyle(m_newFillInformation[i].m_styleId);
236     }
237     app->getCurrentXsheet()->notifyXsheetChanged();
238     notifyImageChanged();
239   }
240 
getSize() const241   int getSize() const override {
242     return sizeof(*this) +
243            (m_oldFillInformation.capacity() + m_newFillInformation.capacity()) *
244                sizeof(TFilledRegionInf) +
245            500;
246   }
247 
getToolName()248   QString getToolName() override { return QString("Vector Eraser Tool"); }
249 
getHistoryType()250   int getHistoryType() override { return HistoryType::EraserTool; }
251 };
252 
253 }  // namespace
254 
255 //=============================================================================
256 //  EraserTool class declaration
257 //-----------------------------------------------------------------------------
258 
259 class EraserTool final : public TTool {
260   Q_DECLARE_TR_FUNCTIONS(EraserTool)
261 
262 public:
263   EraserTool();
264   ~EraserTool();
265 
getToolType() const266   ToolType getToolType() const override { return TTool::LevelWriteTool; }
267 
268   void draw() override;
269 
270   void startErase(
271       TVectorImageP vi,
272       const TPointD &pos);  //, const TImageLocation &imageLocation);
273   void erase(TVectorImageP vi, const TPointD &pos);
274   void erase(TVectorImageP vi,
275              TRectD &rect);  //, const TImageLocation &imageLocation);
276 
277   void stopErase(TVectorImageP vi);
278 
279   void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override;
280   void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
281   void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
282   void leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e) override;
283   void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
284   bool onPropertyChanged(std::string propertyName) override;
285   void onEnter() override;
286   void onLeave() override;
287   void onActivate() override;
288 
getProperties(int targetType)289   TPropertyGroup *getProperties(int targetType) override { return &m_prop; }
290 
getCursorId() const291   int getCursorId() const override { return ToolCursor::EraserCursor; }
292   void onImageChanged() override;
293 
294   /*-- ドラッグ中にツールが切り替わった場合、Eraseの終了処理を行う --*/
295   void onDeactivate() override;
296 
297 private:
298   typedef void (EraserTool::*EraseFunction)(const TVectorImageP vi,
299                                             TStroke *stroke);
300 
301   TPropertyGroup m_prop;
302 
303   TEnumProperty m_eraseType;
304   TEnumProperty m_interpolation;
305   TDoubleProperty m_toolSize;
306   TBoolProperty m_selective;
307   TBoolProperty m_invertOption;
308   TBoolProperty m_multi;
309 
310   double m_pointSize, m_distance2;
311 
312   TPointD m_mousePos,  //!< Current mouse position.
313       m_oldMousePos,   //!< Previous mouse position.
314       m_brushPos,      //!< Position the brush will be painted at.
315       m_firstPos;      //!< Either The first point inserted either in m_track or
316                        //! m_polyline
317   //!  (depending on selected erase mode).
318   UndoEraser *m_undo;
319   std::vector<int> m_indexes;
320 
321   TRectD m_selectingRect, m_firstRect;
322 
323   TFrameId m_firstFrameId, m_veryFirstFrameId;
324 
325   TXshSimpleLevelP m_level;
326   std::pair<int, int> m_currCell;
327 
328   StrokeGenerator m_track;  //!< Lazo selection generator.
329 
330   std::vector<TPointD> m_polyline;  //!< Polyline points.
331 
332   TStroke *m_stroke;  //!< Stores the stroke generated by m_track.
333   TStroke
334       *m_firstStroke;  //!< Stores the first stroke in the "frame range" case.
335   TImageP m_activeImage = NULL;  // needed if an frame is changed mid-erase
336   double m_thick;
337 
338   bool m_firstTime, m_active, m_firstFrameSelected;
339 
340 private:
341   void resetMulti();
342 
343   void updateTranslation() override;
344 
345   // Metodi per disegnare la linea della modalita' Freehand
346   void startFreehand(const TPointD &pos);
347   void freehandDrag(const TPointD &pos);
348   void closeFreehand(const TPointD &pos);
349 
350   // Metodi per disegnare la linea della modalita' Polyline
351   void addPointPolyline(const TPointD &pos);
352   void closePolyline(const TPointD &pos);
353 
354   void eraseRegion(const TVectorImageP vi, TStroke *stroke);
355 
356   void eraseSegments(const TVectorImageP vi, TStroke *eraseStroke);
357 
358   void multiEraseRect(TFrameId firstFrameId, TFrameId lastFrameId,
359                       TRectD firstRect, TRectD lastRect, bool invert);
360   void doMultiErase(TFrameId &firstFrameId, TFrameId &lastFrameId,
361                     const TStroke *firstStroke, const TStroke *lastStroke,
362                     EraseFunction eraseFunction);
363   void doErase(double t, const TXshSimpleLevelP &sl, const TFrameId &fid,
364                const TVectorImageP &firstImage, const TVectorImageP &lastImage,
365                EraseFunction eraseFunction);
366   void multiErase(TStroke *stroke, const TMouseEvent &e,
367                   EraseFunction eraseFunction);
368 
369 } eraserTool;
370 
371 //=============================================================================
372 //  EraserTool implemention
373 //-----------------------------------------------------------------------------
374 
EraserTool()375 EraserTool::EraserTool()
376     : TTool("T_Eraser")
377     , m_eraseType("Type:")  // "W_ToolOptions_Erasetype"
378     , m_interpolation("interpolation:")
379     , m_toolSize("Size:", 1, 1000, 10)  // "W_ToolOptions_EraserToolSize"
380     , m_selective("Selective", false)   // "W_ToolOptions_Selective"
381     , m_invertOption("Invert", false)   // "W_ToolOptions_Invert"
382     , m_multi("Frame Range", false)     // "W_ToolOptions_FrameRange"
383     , m_pointSize(-1)
384     , m_undo(0)
385     , m_currCell(-1, -1)
386     , m_stroke(0)
387     , m_thick(5)
388     , m_active(false)
389     , m_firstTime(true) {
390   bind(TTool::VectorImage);
391 
392   m_toolSize.setNonLinearSlider();
393 
394   m_prop.bind(m_toolSize);
395   m_prop.bind(m_eraseType);
396   m_eraseType.addValue(NORMAL_ERASE);
397   m_eraseType.addValue(RECT_ERASE);
398   m_eraseType.addValue(FREEHAND_ERASE);
399   m_eraseType.addValue(POLYLINE_ERASE);
400   m_eraseType.addValue(SEGMENT_ERASE);
401   m_prop.bind(m_selective);
402   m_prop.bind(m_invertOption);
403   m_prop.bind(m_multi);
404   m_prop.bind(m_interpolation);
405   m_interpolation.addValue(LINEAR_INTERPOLATION);
406   m_interpolation.addValue(EASE_IN_INTERPOLATION);
407   m_interpolation.addValue(EASE_OUT_INTERPOLATION);
408   m_interpolation.addValue(EASE_IN_OUT_INTERPOLATION);
409 
410   m_selective.setId("Selective");
411   m_invertOption.setId("Invert");
412   m_multi.setId("FrameRange");
413   m_eraseType.setId("Type");
414   m_interpolation.setId("Interpolation");
415 }
416 
417 //-----------------------------------------------------------------------------
418 
~EraserTool()419 EraserTool::~EraserTool() {
420   if (m_stroke) delete m_stroke;
421 
422   if (m_firstStroke) delete m_firstStroke;
423 }
424 
425 //-----------------------------------------------------------------------------
426 
updateTranslation()427 void EraserTool::updateTranslation() {
428   m_toolSize.setQStringName(tr("Size:"));
429   m_selective.setQStringName(tr("Selective"));
430   m_invertOption.setQStringName(tr("Invert"));
431   m_multi.setQStringName(tr("Frame Range"));
432   m_eraseType.setQStringName(tr("Type:"));
433   m_eraseType.setItemUIName(NORMAL_ERASE, tr("Normal"));
434   m_eraseType.setItemUIName(RECT_ERASE, tr("Rectangular"));
435   m_eraseType.setItemUIName(FREEHAND_ERASE, tr("Freehand"));
436   m_eraseType.setItemUIName(POLYLINE_ERASE, tr("Polyline"));
437   m_eraseType.setItemUIName(SEGMENT_ERASE, tr("Segment"));
438 
439   m_interpolation.setQStringName(tr(""));
440   m_interpolation.setItemUIName(LINEAR_INTERPOLATION, tr("Linear"));
441   m_interpolation.setItemUIName(EASE_IN_INTERPOLATION, tr("Ease In"));
442   m_interpolation.setItemUIName(EASE_OUT_INTERPOLATION, tr("Ease Out"));
443   m_interpolation.setItemUIName(EASE_IN_OUT_INTERPOLATION, tr("Ease In/Out"));
444 }
445 
446 //-----------------------------------------------------------------------------
447 
draw()448 void EraserTool::draw() {
449   if (m_pointSize <= 0) return;
450 
451   double pixelSize2 = getPixelSize() * getPixelSize();
452   m_thick           = pixelSize2 / 2.0;
453 
454   TImageP image(getImage(false));
455   TVectorImageP vi = image;
456   if (vi) {
457     bool blackBg = ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg;
458     if (m_eraseType.getValue() == RECT_ERASE) {
459       TPixel color = blackBg ? TPixel32::White : TPixel32::Red;
460       if (m_multi.getValue() && m_firstFrameSelected)
461         drawRect(m_firstRect, color, 0x3F33, true);
462 
463       if (m_active || (m_multi.getValue() && !m_firstFrameSelected))
464         drawRect(m_selectingRect, color, 0xFFFF, true);
465     }
466     if (m_eraseType.getValue() == NORMAL_ERASE) {
467       // If toggled off, don't draw brush outline
468       if (!Preferences::instance()->isCursorOutlineEnabled()) return;
469 
470       tglColor(TPixel32(255, 0, 255));
471       tglDrawCircle(m_brushPos, m_pointSize);
472     }
473     if ((m_eraseType.getValue() == FREEHAND_ERASE ||
474          m_eraseType.getValue() == POLYLINE_ERASE ||
475          m_eraseType.getValue() == SEGMENT_ERASE) &&
476         m_multi.getValue() && m_firstStroke) {
477       TPixel color = blackBg ? TPixel32::White : TPixel32::Red;
478       tglColor(color);
479       glPushAttrib(GL_ALL_ATTRIB_BITS);
480       glEnable(GL_BLEND);
481       glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
482       if (m_firstFrameSelected) {
483         glLineStipple(1, 0x3F33);
484         glEnable(GL_LINE_STIPPLE);
485       }
486       drawStrokeCenterline(*m_firstStroke, 1);
487       glPopAttrib();
488     }
489     if (m_eraseType.getValue() == POLYLINE_ERASE && !m_polyline.empty()) {
490       TPixel color = blackBg ? TPixel32::White : TPixel32::Black;
491       tglColor(color);
492       tglDrawCircle(m_polyline[0], 2);
493       glBegin(GL_LINE_STRIP);
494       for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]);
495       tglVertex(m_mousePos);
496       glEnd();
497     } else if ((m_eraseType.getValue() == FREEHAND_ERASE ||
498                 m_eraseType.getValue() == SEGMENT_ERASE) &&
499                !m_track.isEmpty()) {
500       TPixel color = blackBg ? TPixel32::White : TPixel32::Black;
501       tglColor(color);
502       glPushMatrix();
503       m_track.drawAllFragments();
504       glPopMatrix();
505     }
506   }
507 }
508 
509 //-----------------------------------------------------------------------------
510 
resetMulti()511 void EraserTool::resetMulti() {
512   m_firstFrameSelected = false;
513   m_firstRect.empty();
514 
515   m_selectingRect.empty();
516   TTool::Application *application = TTool::getApplication();
517   if (!application) return;
518 
519   m_firstFrameId = m_veryFirstFrameId = getCurrentFid();
520   m_level = application->getCurrentLevel()->getLevel()
521                 ? application->getCurrentLevel()->getLevel()->getSimpleLevel()
522                 : 0;
523 
524   if (m_firstStroke) {
525     delete m_firstStroke;
526     m_firstStroke = 0;
527   }
528 }
529 
530 //-----------------------------------------------------------------------------
531 
startErase(TVectorImageP vi,const TPointD & pos)532 void EraserTool::startErase(
533     TVectorImageP vi,
534     const TPointD &pos)  //, const TImageLocation &imageLocation)
535 {
536   UINT size = vi->getStrokeCount();
537   m_indexes.resize(size);
538   for (UINT i = 0; i < size; i++) m_indexes[i] = i;
539 
540   if (m_undo) delete m_undo;
541   TXshSimpleLevel *level =
542       TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
543   m_undo        = new UndoEraser(level, getCurrentFid());
544   m_oldMousePos = pos;
545   m_distance2   = minDistance2 * getPixelSize() * getPixelSize();
546   erase(vi, pos);
547 }
548 
549 //-----------------------------------------------------------------------------
550 
erase(TVectorImageP vi,const TPointD & pos)551 void EraserTool::erase(TVectorImageP vi, const TPointD &pos) {
552   std::vector<int>::iterator it = m_indexes.begin();
553   m_distance2 += tdistance2(m_oldMousePos, pos);
554 
555   if (m_distance2 < minDistance2 * getPixelSize() * getPixelSize()) return;
556 
557   m_distance2   = 0;
558   m_oldMousePos = pos;
559 
560   // quadrato circoscritto alla circonferenza
561   TRectD circumscribedSquare(pos.x - m_pointSize, pos.y - m_pointSize,
562                              pos.x + m_pointSize, pos.y + m_pointSize);
563   if (!circumscribedSquare.overlaps(vi->getBBox())) {
564     invalidate();
565     return;
566   }
567 
568   std::vector<double> intersections;
569   std::vector<DoublePair> sortedWRanges;
570 
571   std::vector<TStroke *> splitStrokes;
572   double rectEdge_2 = m_pointSize * M_SQRT1_2;
573 
574   // quadrato iscritto nella circonferenza
575   TRectD enrolledSquare(pos.x - rectEdge_2, pos.y - rectEdge_2,
576                         pos.x + rectEdge_2, pos.y + rectEdge_2);
577 
578   UINT i            = 0;
579   double pointSize2 = sq(m_pointSize);
580 
581   std::vector<int> oneStrokeIndex(1);
582   int index = TTool::getApplication()->getCurrentLevelStyleIndex();
583   QMutexLocker lock(vi->getMutex());
584   while (i < vi->getStrokeCount()) {
585     assert(it != m_indexes.end());
586 
587     TStroke *oldStroke = vi->getStroke(i);
588     if (!vi->inCurrentGroup(i) ||
589         (m_selective.getValue() && oldStroke->getStyle() != index)) {
590       i++;
591       it++;
592       continue;
593     }
594 
595     TRectD strokeBBox = oldStroke->getBBox();
596 
597     if (!circumscribedSquare.overlaps(strokeBBox)) {  // stroke all'esterno del
598                                                       // quadrato circoscritto
599                                                       // alla circonferenxa
600       i++;
601       it++;
602       continue;
603     }
604 
605     if (enrolledSquare.contains(strokeBBox)) {  // stroke tutta contenuta nel
606                                                 // quadrato iscritto nella
607                                                 // circonferenza
608       if (*it != -1) m_undo->addOldStroke(*it, vi->getVIStroke(i));
609       oneStrokeIndex[0] = i;
610       vi->removeStrokes(oneStrokeIndex, true, true);
611 
612       it = m_indexes.erase(it);
613       continue;
614     }
615 
616     splitStrokes.clear();
617     intersections.clear();
618     /*int intersNum=*/intersect(*oldStroke, pos, m_pointSize, intersections);
619 
620     /*
621 #ifdef _DEBUG
622 if(intersections.size()==2 && intersections[0]==intersections[1])
623 {
624 intersections.clear();
625 intersect( *oldStroke, pos, m_pointSize, intersections );
626 }
627 #endif
628 */
629 
630     if (intersections.empty()) {
631       // BASTEREBBE CONTROLLARE UN SOLO PUNTO PERCHE' SE LA
632       // STROKE NON INTERSECA IL CERCHIO E CONTENTIENE ALMENO
633       // UN PUNTO, LA CONTIENE TUTTA. MA SICCOME NON MI FIDO
634       // DELLA INTERSECT, NE CONTROLLO UN PAIO E AVVANTAGGIO (CON L' AND)
635       // IL NON CONTENIMENTO, DATO CHE E' MEGLIO CANCELLARE UNA COSA IN
636       // MENO, CHE UNA IN PIU'
637       if (tdistance2(oldStroke->getPoint(0), pos) < pointSize2 &&
638           tdistance2(oldStroke->getPoint(1), pos) <
639               pointSize2) {  // stroke tutta contenuta nella circonferenxa
640         if (*it != -1) m_undo->addOldStroke(*it, vi->getVIStroke(i));
641         oneStrokeIndex[0] = i;
642         vi->removeStrokes(oneStrokeIndex, true, true);
643         it = m_indexes.erase(it);
644       } else {  // non colpita
645         i++;
646         it++;
647       }
648       continue;
649     }
650 
651     //------------------------ almeno un'intersezione
652     //---------------------------------------------------------
653 
654     if (intersections.size() == 1) {
655       if (oldStroke->isSelfLoop()) {  // una sola intersezione di una stroke
656                                       // chiusa con un cerchio dovrebbe accadere
657         // solo in caso di sfioramento, quindi faccio finta di nulla
658         i++;
659         it++;
660         continue;
661       }
662 
663       if (*it != -1) m_undo->addOldStroke(*it, vi->getVIStroke(i));
664 
665       double w0            = intersections[0];
666       TThickPoint hitPoint = oldStroke->getThickPoint(w0);
667       int chunck;
668       double t;
669       oldStroke->getChunkAndT(w0, chunck, t);
670 
671       int chunckIndex;
672       double w1;
673       if (tdistance2(oldStroke->getPoint(0), pos) < pointSize2) {
674         chunckIndex = oldStroke->getChunkCount() - 1;
675         w1          = 1;
676       } else {
677         chunckIndex = 0;
678         w1          = 0;
679       }
680 
681       UINT cI;
682       std::vector<TThickPoint> points;
683       if (w1 == 0) {
684         for (cI = chunckIndex; cI < (UINT)chunck; cI++) {
685           points.push_back(oldStroke->getChunk(cI)->getThickP0());
686           points.push_back(oldStroke->getChunk(cI)->getThickP1());
687         }
688 
689         TThickQuadratic t1, t2;
690         oldStroke->getChunk(chunck)->split(t, t1, t2);
691         points.push_back(t1.getThickP0());
692         points.push_back(t1.getThickP1());
693         points.push_back(hitPoint);
694       } else {
695         TThickQuadratic t1, t2;
696         oldStroke->getChunk(chunck)->split(t, t1, t2);
697         points.push_back(hitPoint);
698         points.push_back(t2.getThickP1());
699         points.push_back(t2.getThickP2());
700 
701         for (cI = chunck + 1; cI <= (UINT)chunckIndex; cI++) {
702           points.push_back(oldStroke->getChunk(cI)->getThickP1());
703           points.push_back(oldStroke->getChunk(cI)->getThickP2());
704         }
705       }
706 
707       oldStroke->reshape(&(points[0]), points.size());
708 
709       vi->notifyChangedStrokes(i);  // per adesso cosi', pero' e' lento
710 
711       *it = -1;
712       i++;
713       it++;
714       continue;
715     }
716 
717     //---------- piu'
718     // intersezioni--------------------------------------------------------
719 
720     if (intersections.size() & 1 &&
721         oldStroke->isSelfLoop()) {  // non dovrebbe mai accadere
722       assert(0);
723       i++;
724       it++;
725       continue;
726     }
727 
728     if (intersections.size() == 2 &&
729         intersections[0] == intersections[1]) {  // solo sfiorata
730       i++;
731       it++;
732       continue;
733     }
734 
735     UINT oldStrokeSize = vi->getStrokeCount();
736 
737     if (*it != -1) m_undo->addOldStroke(*it, vi->getVIStroke(i));
738 
739     sortedWRanges.clear();
740 
741     if (tdistance2(vi->getStroke(i)->getPoint(0), pos) > pointSize2) {
742       if (intersections[0] == 0.0)
743         intersections.erase(intersections.begin());
744       else
745         intersections.insert(intersections.begin(), 0.0);
746     }
747 
748     if (intersections[0] != 1.0) intersections.push_back(1.0);
749 
750     sortedWRanges.reserve(intersections.size() / 2);
751 
752     for (UINT j = 0; j < intersections.size() - 1; j += 2)
753       sortedWRanges.push_back(
754           std::make_pair(intersections[j], intersections[j + 1]));
755 
756 #ifdef _DEBUG
757 
758     for (UINT kkk = 0; kkk < sortedWRanges.size() - 1; kkk++) {
759       assert(sortedWRanges[kkk].first < sortedWRanges[kkk].second);
760       assert(sortedWRanges[kkk].second <= sortedWRanges[kkk + 1].first);
761     }
762     assert(sortedWRanges.back().first < sortedWRanges.back().second);
763 
764 #endif
765 
766     vi->splitStroke(i, sortedWRanges);
767 
768     UINT addedStroke = vi->getStrokeCount() -
769                        (oldStrokeSize - 1);  //-1 perche' e' quella tolta
770 
771     i += addedStroke;
772 
773     *it = -1;
774     m_indexes.insert(it, addedStroke - 1, -1);
775     it = m_indexes.begin() + i;
776   }
777 
778   invalidate();
779 }
780 
781 //-----------------------------------------------------------------------------
782 
erase(TVectorImageP vi,TRectD & rect)783 void EraserTool::erase(TVectorImageP vi, TRectD &rect) {
784   if (rect.x0 > rect.x1) std::swap(rect.x1, rect.x0);
785   if (rect.y0 > rect.y1) std::swap(rect.y1, rect.y0);
786   int i     = 0;
787   int index = TTool::getApplication()->getCurrentLevelStyleIndex();
788   std::vector<int> eraseStrokes;
789 
790   TXshSimpleLevel *level =
791       TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
792   m_undo = new UndoEraser(level, getCurrentFid());
793   for (i = 0; i < (int)vi->getStrokeCount(); i++) {
794     if (!vi->inCurrentGroup(i)) continue;
795     TStroke *stroke = vi->getStroke(i);
796     if (!m_invertOption.getValue()) {
797       if ((!m_selective.getValue() || stroke->getStyle() == index) &&
798           rect.contains(stroke->getBBox())) {
799         m_undo->addOldStroke(i, vi->getVIStroke(i));
800         eraseStrokes.push_back(i);
801       }
802     } else {
803       if ((!m_selective.getValue() || stroke->getStyle() == index) &&
804           !rect.contains(stroke->getBBox())) {
805         m_undo->addOldStroke(i, vi->getVIStroke(i));
806         eraseStrokes.push_back(i);
807       }
808     }
809   }
810   for (i = (int)eraseStrokes.size() - 1; i >= 0; i--)
811     vi->deleteStroke(eraseStrokes[i]);
812   TUndoManager::manager()->add(m_undo);
813   m_undo = 0;
814   invalidate();
815 }
816 
817 //-----------------------------------------------------------------------------
818 
stopErase(TVectorImageP vi)819 void EraserTool::stopErase(TVectorImageP vi) {
820   assert(m_undo != 0);
821 
822   UINT size = m_indexes.size();
823 
824   assert(size == vi->getStrokeCount());
825   UINT i = 0;
826   for (; i < size; i++) {
827     if (m_indexes[i] == -1) m_undo->addNewStroke(i, vi->getVIStroke(i));
828   }
829   TUndoManager::manager()->add(m_undo);
830   m_undo   = 0;
831   m_active = false;
832   invalidate();
833   notifyImageChanged();
834 }
835 
836 //-----------------------------------------------------------------------------
837 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)838 void EraserTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
839   m_brushPos = m_mousePos = pos;
840 
841   m_active = true;
842 
843   TImageP image(getImage(true));
844   m_activeImage = image;
845   if (m_eraseType.getValue() == NORMAL_ERASE) {
846     if (TVectorImageP vi = image) startErase(vi, pos /*,imageLocation*/);
847   } else if (m_eraseType.getValue() == RECT_ERASE) {
848     m_selectingRect.x0 = pos.x;
849     m_selectingRect.y0 = pos.y;
850     m_selectingRect.x1 = pos.x + 1;
851     m_selectingRect.y1 = pos.y + 1;
852     invalidate();
853   } else if (m_eraseType.getValue() == FREEHAND_ERASE ||
854              m_eraseType.getValue() == SEGMENT_ERASE) {
855     startFreehand(pos);
856   } else if (m_eraseType.getValue() == POLYLINE_ERASE) {
857     addPointPolyline(pos);
858   }
859 }
860 
861 //-----------------------------------------------------------------------------
862 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)863 void EraserTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
864   m_brushPos = m_mousePos = pos;
865 
866   if (!m_active) return;
867 
868   TImageP image(getImage(true));
869   if (m_eraseType.getValue() == RECT_ERASE) {
870     m_selectingRect.x1 = pos.x;
871     m_selectingRect.y1 = pos.y;
872     invalidate();
873     return;
874   } else if (m_eraseType.getValue() == NORMAL_ERASE) {
875     if (!m_undo) leftButtonDown(pos, e);
876     if (TVectorImageP vi = image) erase(vi, pos);
877   } else if (m_eraseType.getValue() == FREEHAND_ERASE ||
878              m_eraseType.getValue() == SEGMENT_ERASE) {
879     freehandDrag(pos);
880   }
881 }
882 
883 //-----------------------------------------------------------------------------
884 
multiEraseRect(TFrameId firstFrameId,TFrameId lastFrameId,TRectD firstRect,TRectD lastRect,bool invert)885 void EraserTool::multiEraseRect(TFrameId firstFrameId, TFrameId lastFrameId,
886                                 TRectD firstRect, TRectD lastRect,
887                                 bool invert) {
888   int r0 = firstFrameId.getNumber();
889   int r1 = lastFrameId.getNumber();
890 
891   if (r0 > r1) {
892     std::swap(r0, r1);
893     std::swap(firstFrameId, lastFrameId);
894     std::swap(firstRect, lastRect);
895   }
896   if ((r1 - r0) < 1) return;
897 
898   std::vector<TFrameId> allFids;
899   m_level->getFids(allFids);
900   std::vector<TFrameId>::iterator i0 = allFids.begin();
901   while (i0 != allFids.end() && *i0 < firstFrameId) i0++;
902   if (i0 == allFids.end()) return;
903   std::vector<TFrameId>::iterator i1 = i0;
904   while (i1 != allFids.end() && *i1 <= lastFrameId) i1++;
905   assert(i0 < i1);
906   std::vector<TFrameId> fids(i0, i1);
907   int m = fids.size();
908   assert(m > 0);
909 
910   enum TInbetween::TweenAlgorithm algorithm = TInbetween::LinearInterpolation;
911   if (m_interpolation.getValue() == EASE_IN_INTERPOLATION) {
912     algorithm = TInbetween::EaseInInterpolation;
913   } else if (m_interpolation.getValue() == EASE_OUT_INTERPOLATION) {
914     algorithm = TInbetween::EaseOutInterpolation;
915   } else if (m_interpolation.getValue() == EASE_IN_OUT_INTERPOLATION) {
916     algorithm = TInbetween::EaseInOutInterpolation;
917   }
918 
919   TUndoManager::manager()->beginBlock();
920   for (int i = 0; i < m; ++i) {
921     TFrameId fid = fids[i];
922     assert(firstFrameId <= fid && fid <= lastFrameId);
923     TVectorImageP img = (TVectorImageP)m_level->getFrame(fid, true);
924     assert(img);
925     double t    = m > 1 ? (double)i / (double)(m - 1) : 0.5;
926     t           = TInbetween::interpolation(t, algorithm);
927     TRectD rect = interpolateRect(firstRect, lastRect, t);
928     // m_level->setFrame(fid, img); //necessario: se la getFrame ha scompattato
929     // una img compressa, senza setFrame le modifiche sulla img fatte qui
930     // andrebbero perse.
931 
932     // Setto fid come corrente per notificare il cambiamento dell'immagine
933     TTool::Application *app = TTool::getApplication();
934     if (app) {
935       if (app->getCurrentFrame()->isEditingScene())
936         app->getCurrentFrame()->setFrame(fid.getNumber() - 1);
937       else
938         app->getCurrentFrame()->setFid(fid);
939     }
940 
941     erase(img, rect);
942 
943     notifyImageChanged();
944   }
945   TUndoManager::manager()->endBlock();
946 }
947 
948 //-----------------------------------------------------------------------------
949 
onImageChanged()950 void EraserTool::onImageChanged() {
951   if (m_active) {
952     stopErase(m_activeImage);
953   }
954   if (!m_multi.getValue()) return;
955   TTool::Application *application = TTool::getApplication();
956   if (!application) return;
957   TXshSimpleLevel *xshl = 0;
958   if (application->getCurrentLevel()->getLevel())
959     xshl = application->getCurrentLevel()->getLevel()->getSimpleLevel();
960 
961   if (!xshl || m_level.getPointer() != xshl ||
962       (m_eraseType.getValue() == RECT_ERASE && m_selectingRect.isEmpty()) ||
963       ((m_eraseType.getValue() == FREEHAND_ERASE ||
964         m_eraseType.getValue() == POLYLINE_ERASE ||
965         m_eraseType.getValue() == SEGMENT_ERASE) &&
966        !m_firstStroke))
967     resetMulti();
968   else if (m_firstFrameId == getCurrentFid())
969     m_firstFrameSelected = false;  // nel caso sono passato allo stato 1 e torno
970                                    // all'immagine iniziale, torno allo stato
971                                    // iniziale
972   else {                           // cambio stato.
973     m_firstFrameSelected = true;
974     if (m_eraseType.getValue() == RECT_ERASE) {
975       assert(!m_selectingRect.isEmpty());
976       m_firstRect = m_selectingRect;
977     }
978   }
979 }
980 
981 //-----------------------------------------------------------------------------
982 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)983 void EraserTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) {
984   if (!m_active) return;
985   m_active = false;
986   TImageP image(getImage(true));
987   TVectorImageP vi = image;
988 
989   TTool::Application *application = TTool::getApplication();
990   if (!vi || !application) return;
991   if (m_eraseType.getValue() == NORMAL_ERASE) {
992     if (!m_undo) leftButtonDown(pos, e);
993     stopErase(vi);
994   } else if (m_eraseType.getValue() == RECT_ERASE) {
995     if (m_selectingRect.x0 > m_selectingRect.x1)
996       std::swap(m_selectingRect.x1, m_selectingRect.x0);
997     if (m_selectingRect.y0 > m_selectingRect.y1)
998       std::swap(m_selectingRect.y1, m_selectingRect.y0);
999 
1000     if (m_multi.getValue()) {
1001       if (m_firstFrameSelected) {
1002         multiEraseRect(m_firstFrameId, getCurrentFid(), m_firstRect,
1003                        m_selectingRect, m_invertOption.getValue());
1004         if (e.isShiftPressed()) {
1005           m_firstRect    = m_selectingRect;
1006           m_firstFrameId = getCurrentFid();
1007         } else {
1008           if (application->getCurrentFrame()->isEditingScene()) {
1009             application->getCurrentColumn()->setColumnIndex(m_currCell.first);
1010             application->getCurrentFrame()->setFrame(m_currCell.second);
1011           } else
1012             application->getCurrentFrame()->setFid(m_veryFirstFrameId);
1013           resetMulti();
1014         }
1015         invalidate();  // invalidate(m_selectingRect.enlarge(2));
1016       } else {
1017         if (application->getCurrentFrame()->isEditingScene())
1018           m_currCell = std::pair<int, int>(
1019               application->getCurrentColumn()->getColumnIndex(),
1020               application->getCurrentFrame()->getFrame());
1021       }
1022       return;
1023     } else {
1024       erase(vi, m_selectingRect);
1025       notifyImageChanged();
1026       m_selectingRect.empty();
1027     }
1028   } else if (m_eraseType.getValue() == FREEHAND_ERASE) {
1029     closeFreehand(pos);
1030     if (m_multi.getValue()) {
1031       multiErase(m_stroke, e, &EraserTool::eraseRegion);
1032       invalidate();
1033     } else {
1034       eraseRegion(vi, m_stroke);
1035       invalidate();
1036       notifyImageChanged();
1037     }
1038     m_track.clear();
1039   } else if (m_eraseType.getValue() == SEGMENT_ERASE) {
1040     double error = (30.0 / 11) * sqrt(getPixelSize() * getPixelSize());
1041     m_stroke     = m_track.makeStroke(error);
1042     m_stroke->setStyle(1);
1043     if (m_multi.getValue()) {
1044       multiErase(m_stroke, e, &EraserTool::eraseSegments);
1045       invalidate();
1046     } else {
1047       eraseSegments(vi, m_stroke);
1048       invalidate();
1049       notifyImageChanged();
1050     }
1051     m_track.clear();
1052   }
1053 }
1054 
1055 //-----------------------------------------------------------------------------
1056 
1057 //! Viene chiusa la polyline e si da il via alla cancellazione.
1058 /*!Viene creato lo \b stroke rappresentante la polyline disegnata.
1059   Se e' selzionato il metodo di cancellazione "frame range" viene richiamato il
1060   metodo \b multiEreserRegion, altrimenti
1061   viene richiamato il metodo \b eraseRegion*/
leftButtonDoubleClick(const TPointD & pos,const TMouseEvent & e)1062 void EraserTool::leftButtonDoubleClick(const TPointD &pos,
1063                                        const TMouseEvent &e) {
1064   TVectorImageP vi = getImage(true);
1065   if (m_eraseType.getValue() == POLYLINE_ERASE) {
1066     closePolyline(pos);
1067 
1068     std::vector<TThickPoint> strokePoints;
1069     for (UINT i = 0; i < m_polyline.size() - 1; i++) {
1070       strokePoints.push_back(TThickPoint(m_polyline[i], 1));
1071       strokePoints.push_back(
1072           TThickPoint(0.5 * (m_polyline[i] + m_polyline[i + 1]), 1));
1073     }
1074     strokePoints.push_back(TThickPoint(m_polyline.back(), 1));
1075     m_polyline.clear();
1076     TStroke *stroke = new TStroke(strokePoints);
1077     assert(stroke->getPoint(0) == stroke->getPoint(1));
1078     if (m_multi.getValue())
1079       multiErase(stroke, e, &EraserTool::eraseRegion);
1080     else {
1081       eraseRegion(vi, stroke);
1082       m_active = false;
1083       notifyImageChanged();
1084     }
1085     invalidate();
1086   }
1087 }
1088 
1089 //-----------------------------------------------------------------------------
1090 
mouseMove(const TPointD & pos,const TMouseEvent & e)1091 void EraserTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
1092   struct Locals {
1093     EraserTool *m_this;
1094 
1095     void setValue(TDoubleProperty &prop, double value) {
1096       prop.setValue(value);
1097 
1098       m_this->onPropertyChanged(prop.getName());
1099       TTool::getApplication()->getCurrentTool()->notifyToolChanged();
1100     }
1101 
1102     void addValue(TDoubleProperty &prop, double add) {
1103       const TDoubleProperty::Range &range = prop.getRange();
1104       setValue(prop, tcrop(prop.getValue() + add, range.first, range.second));
1105     }
1106 
1107   } locals = {this};
1108 
1109   switch (e.getModifiersMask()) {
1110   case TMouseEvent::ALT_KEY: {
1111     // User wants to alter the maximum brush size
1112     const TPointD &diff = pos - m_mousePos;
1113     double add          = (fabs(diff.x) > fabs(diff.y)) ? diff.x : diff.y;
1114 
1115     locals.addValue(m_toolSize, add);
1116     break;
1117   }
1118 
1119   default:
1120     m_brushPos = pos;
1121     break;
1122   }
1123 
1124   m_oldMousePos = m_mousePos = pos;
1125   invalidate();
1126 }
1127 
1128 //----------------------------------------------------------------------
1129 
onPropertyChanged(std::string propertyName)1130 bool EraserTool::onPropertyChanged(std::string propertyName) {
1131   EraseVectorType          = ::to_string(m_eraseType.getValue());
1132   EraseVectorInterpolation = ::to_string(m_interpolation.getValue());
1133   EraseVectorSize          = m_toolSize.getValue();
1134   EraseVectorSelective     = m_selective.getValue();
1135   EraseVectorInvert        = m_invertOption.getValue();
1136   EraseVectorRange         = m_multi.getValue();
1137 
1138   double x = m_toolSize.getValue();
1139 
1140   double minRange = 1;
1141   double maxRange = 100;
1142 
1143   double minSize = 2;
1144   double maxSize = 100;
1145 
1146   m_pointSize =
1147       ((x - minRange) / (maxRange - minRange) * (maxSize - minSize) + minSize) *
1148       0.5;
1149   invalidate();
1150 
1151   return true;
1152 }
1153 
1154 //-----------------------------------------------------------------------------
1155 
onEnter()1156 void EraserTool::onEnter() {
1157   if (m_firstTime) {
1158     m_toolSize.setValue(EraseVectorSize);
1159     m_eraseType.setValue(::to_wstring(EraseVectorType.getValue()));
1160     m_interpolation.setValue(::to_wstring(EraseVectorInterpolation.getValue()));
1161     m_selective.setValue(EraseVectorSelective ? 1 : 0);
1162     m_invertOption.setValue(EraseVectorInvert ? 1 : 0);
1163     m_multi.setValue(EraseVectorRange ? 1 : 0);
1164     m_firstTime = false;
1165   }
1166 
1167   double x = m_toolSize.getValue();
1168 
1169   double minRange = 1;
1170   double maxRange = 100;
1171 
1172   double minSize = 2;
1173   double maxSize = 100;
1174 
1175   m_pointSize =
1176       ((x - minRange) / (maxRange - minRange) * (maxSize - minSize) + minSize) *
1177       0.5;
1178 
1179   //  getApplication()->editImage();
1180 }
1181 
1182 //-----------------------------------------------------------------------------
1183 
onLeave()1184 void EraserTool::onLeave() {
1185   draw();
1186   m_pointSize = -1;
1187 }
1188 
1189 //-----------------------------------------------------------------------------
1190 
onActivate()1191 void EraserTool::onActivate() {
1192   resetMulti();
1193   m_polyline.clear();
1194   onEnter();
1195 }
1196 
1197 //-----------------------------------------------------------------------------
1198 
1199 //! Viene aggiunto \b pos a \b m_track e disegnato il primo pezzetto del lazzo.
1200 //! Viene inizializzato \b m_firstPos
startFreehand(const TPointD & pos)1201 void EraserTool::startFreehand(const TPointD &pos) {
1202   m_track.clear();
1203   m_firstPos = pos;
1204   m_track.add(TThickPoint(pos, m_thick), getPixelSize() * getPixelSize());
1205 }
1206 
1207 //-----------------------------------------------------------------------------
1208 
1209 //! Viene aggiunto \b pos a \b m_track e disegnato un altro pezzetto del lazzo.
freehandDrag(const TPointD & pos)1210 void EraserTool::freehandDrag(const TPointD &pos) {
1211 #if defined(MACOSX)
1212 //		m_viewer->enableRedraw(false);
1213 #endif
1214   m_track.add(TThickPoint(pos, m_thick), getPixelSize() * getPixelSize());
1215   invalidate(m_track.getModifiedRegion());
1216 }
1217 
1218 //-----------------------------------------------------------------------------
1219 
1220 //! Viene chiuso il lazzo (si aggiunge l'ultimo punto ad m_track) e viene creato
1221 //! lo stroke rappresentante il lazzo.
closeFreehand(const TPointD & pos)1222 void EraserTool::closeFreehand(const TPointD &pos) {
1223 #if defined(MACOSX)
1224 //		m_viewer->enableRedraw(true);
1225 #endif
1226   if (m_track.isEmpty()) return;
1227   m_track.add(TThickPoint(m_firstPos, m_thick),
1228               getPixelSize() * getPixelSize());
1229   m_track.filterPoints();
1230   double error = (30.0 / 11) * sqrt(getPixelSize() * getPixelSize());
1231   m_stroke     = m_track.makeStroke(error);
1232   m_stroke->setStyle(1);
1233 }
1234 
1235 //-----------------------------------------------------------------------------
1236 
1237 //! Viene aggiunto un punto al vettore m_polyline.
addPointPolyline(const TPointD & pos)1238 void EraserTool::addPointPolyline(const TPointD &pos) {
1239   m_firstPos = pos;
1240   m_polyline.push_back(pos);
1241 }
1242 
1243 //-----------------------------------------------------------------------------
1244 
1245 //! Agginge l'ultimo pos a \b m_polyline e chiude la spezzata (aggiunge \b
1246 //! m_polyline.front() alla fine del vettore)
closePolyline(const TPointD & pos)1247 void EraserTool::closePolyline(const TPointD &pos) {
1248   if (m_polyline.size() <= 1) return;
1249   if (m_polyline.back() != pos) m_polyline.push_back(pos);
1250   if (m_polyline.back() != m_polyline.front())
1251     m_polyline.push_back(m_polyline.front());
1252   invalidate();
1253 }
1254 
1255 //-----------------------------------------------------------------------------
1256 
doublePairCompare(DoublePair p1,DoublePair p2)1257 static bool doublePairCompare(DoublePair p1, DoublePair p2) {
1258   return p1.first < p2.first;
1259 }
1260 
eraseSegments(const TVectorImageP vi,TStroke * eraseStroke)1261 void EraserTool::eraseSegments(const TVectorImageP vi, TStroke *eraseStroke) {
1262   if (!vi || !eraseStroke) return;
1263 
1264   int strokeNumber = vi->getStrokeCount();
1265   int colorStyle   = TTool::getApplication()->getCurrentLevelStyleIndex();
1266   std::vector<int> touchedStrokeIndex;
1267   std::vector<std::vector<double>> touchedStrokeW;
1268   std::vector<std::vector<DoublePair>> touchedStrokeRanges;
1269 
1270   // find all touched strokes and where it is touched
1271   for (int i = 0; i < strokeNumber; ++i) {
1272     std::vector<DoublePair> intersections;
1273     std::vector<double> ws;
1274     TStroke *stroke = vi->getStroke(i);
1275     bool touched    = false;
1276 
1277     if (m_selective.getValue() && stroke->getStyle() != colorStyle) {
1278       continue;
1279     }
1280 
1281     intersect(eraseStroke, stroke, intersections, false);
1282 
1283     for (auto &intersection : intersections) {
1284       touched = true;
1285       ws.push_back(intersection.second);
1286     }
1287 
1288     if (touched) {
1289       touchedStrokeIndex.push_back(i);
1290       touchedStrokeW.push_back(ws);
1291     }
1292   }
1293 
1294   // if the eraser did not touch any strokes, return
1295   if (touchedStrokeIndex.size() == 0) {
1296     return;
1297   }
1298 
1299   // find closest intersections of each end of the touched place of each stroke
1300   for (int i = 0; i < touchedStrokeIndex.size(); ++i) {
1301     std::vector<DoublePair> range;
1302     for (auto w : touchedStrokeW[i]) {
1303       std::vector<DoublePair> intersections;
1304       double lowerW = 0.0, higherW = 1.0;
1305       double higherW0 = 1.0,
1306              lowerW1  = 0.0;  // these two value are used when the stroke is
1307                               // self-loop-ed, it assumes touched W is 0 or 1 to
1308                               // find closet intersection
1309 
1310       int strokeIndex = touchedStrokeIndex[i];
1311       TStroke *stroke = vi->getStroke(strokeIndex);
1312 
1313       // check self intersection first
1314       intersect(stroke, stroke, intersections, false);
1315       for (auto &intersection : intersections) {
1316         if (areAlmostEqual(intersection.first, 0, 1e-6)) {
1317           continue;
1318         }
1319         if (areAlmostEqual(intersection.second, 1, 1e-6)) {
1320           continue;
1321         }
1322 
1323         if (intersection.first < w) {
1324           lowerW = std::max(lowerW, intersection.first);
1325         } else {
1326           higherW = std::min(higherW, intersection.first);
1327         }
1328 
1329         if (intersection.second < w) {
1330           lowerW = std::max(lowerW, intersection.second);
1331         } else {
1332           higherW = std::min(higherW, intersection.second);
1333         }
1334 
1335         lowerW1  = std::max(lowerW1, intersection.first);
1336         higherW0 = std::min(higherW0, intersection.first);
1337         lowerW1  = std::max(lowerW1, intersection.second);
1338         higherW0 = std::min(higherW0, intersection.second);
1339       }
1340 
1341       // then check intersection with other strokes
1342       for (int j = 0; j < strokeNumber; ++j) {
1343         if (j == strokeIndex) {
1344           continue;
1345         }
1346 
1347         TStroke *intersectedStroke = vi->getStroke(j);
1348         intersect(stroke, intersectedStroke, intersections, false);
1349         for (auto &intersection : intersections) {
1350           if (intersection.first < w) {
1351             lowerW = std::max(lowerW, intersection.first);
1352           } else {
1353             higherW = std::min(higherW, intersection.first);
1354           }
1355           lowerW1  = std::max(lowerW1, intersection.first);
1356           higherW0 = std::min(higherW0, intersection.first);
1357         }
1358       }
1359 
1360       range.push_back(std::make_pair(lowerW, higherW));
1361       if (stroke->isSelfLoop()) {
1362         if (lowerW == 0.0) {
1363           range.push_back(std::make_pair(lowerW1, 1.0));
1364         } else if (higherW == 1.0) {
1365           range.push_back(std::make_pair(0.0, higherW0));
1366         }
1367       }
1368     }
1369     touchedStrokeRanges.push_back(range);
1370   }
1371 
1372   // merge all ranges of the same stroke by using interval merging algorithm
1373   for (auto &ranges : touchedStrokeRanges) {
1374     std::vector<DoublePair> merged;
1375 
1376     std::sort(ranges.begin(), ranges.end(), doublePairCompare);
1377 
1378     merged.push_back(ranges[0]);
1379     for (auto &range : ranges) {
1380       if (merged.back().second < range.first &&
1381           !areAlmostEqual(merged.back().second, range.first, 1e-3)) {
1382         merged.push_back(range);
1383       } else if (merged.back().second < range.second) {
1384         merged.back().second = range.second;
1385       }
1386     }
1387 
1388     ranges = merged;
1389   }
1390 
1391   // create complement range
1392   for (auto &ranges : touchedStrokeRanges) {
1393     std::vector<DoublePair> complement;
1394 
1395     double last = 0.0;
1396     for (auto &range : ranges) {
1397       if (!areAlmostEqual(last, range.first, 1e-3)) {
1398         complement.push_back(std::make_pair(last, range.first));
1399       }
1400       last = range.second;
1401     }
1402 
1403     if (!areAlmostEqual(last, 1.0, 1e-3)) {
1404       complement.push_back(std::make_pair(last, 1.0));
1405     }
1406     ranges = complement;
1407   }
1408 
1409   // calculate how many lines are added for caculating the final index of added
1410   // strokes
1411   int added = 0;
1412   for (int i = touchedStrokeIndex.size() - 1; i >= 0; --i) {
1413     bool willbeJoined = vi->getStroke(touchedStrokeIndex[i])->isSelfLoop() &&
1414                         touchedStrokeRanges[i][0].first == 0.0 &&
1415                         touchedStrokeRanges[i].back().second == 1.0;
1416 
1417     added += touchedStrokeRanges[i].size();
1418     if (willbeJoined) {
1419       --added;
1420     }
1421   }
1422 
1423   // do the erasing and construct the undo action
1424   TXshSimpleLevel *level =
1425       TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
1426   UndoEraser *undo = new UndoEraser(level, getCurrentFid());
1427   for (int i = touchedStrokeIndex.size() - 1; i >= 0; --i) {
1428     undo->addOldStroke(touchedStrokeIndex[i],
1429                        vi->getVIStroke(touchedStrokeIndex[i]));
1430 
1431     if (touchedStrokeRanges[i].size() == 0) {
1432       vi->deleteStroke(touchedStrokeIndex[i]);
1433     } else {
1434       bool willbeJoined = vi->getStroke(touchedStrokeIndex[i])->isSelfLoop() &&
1435                           touchedStrokeRanges[i][0].first == 0.0 &&
1436                           touchedStrokeRanges[i].back().second == 1.0;
1437 
1438       vi->splitStroke(touchedStrokeIndex[i], touchedStrokeRanges[i]);
1439 
1440       int size = touchedStrokeRanges[i].size();
1441       if (willbeJoined) {
1442         --size;
1443       }
1444       added -= size;
1445       for (int j = 0; j < size; ++j) {
1446         int finalIndex   = touchedStrokeIndex[i] + j - i + added;
1447         int currentIndex = touchedStrokeIndex[i] + j;
1448         undo->addNewStroke(finalIndex, vi->getVIStroke(currentIndex));
1449       }
1450     }
1451   }
1452 
1453   TUndoManager::manager()->add(undo);
1454   return;
1455 }
1456 
1457 //-----------------------------------------------------------------------------
1458 
1459 //! Cancella stroke presenti in \b vi e contenuti nella regione delimitata da \b
1460 //! stroke.
1461 /*!Vengono cercati gli stroke da cancellare facendo i dovuti controlli sui
1462    parametri \b m_invertOption e \b m_selective.
1463         Se uno stroke deve essere cancellato viene inserito in \b eraseStrokes.
1464         Gli stroke vengono cancellati tutti alla fine.*/
eraseRegion(const TVectorImageP vi,TStroke * stroke)1465 void EraserTool::eraseRegion(
1466     const TVectorImageP vi,
1467     TStroke *stroke)  //, const TImageLocation &imageLocation)
1468 {
1469   if (!vi || !stroke) return;
1470   TVectorImage eraseImg;
1471   TStroke *eraseStroke = new TStroke(*stroke);
1472   eraseImg.addStroke(eraseStroke);
1473   eraseImg.findRegions();
1474   int strokeIndex, regionIndex, colorStyle;
1475   colorStyle = TTool::getApplication()->getCurrentLevelStyleIndex();
1476   std::vector<int> eraseStrokes;
1477 
1478   TXshSimpleLevel *level =
1479       TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
1480   m_undo = new UndoEraser(level, getCurrentFid());
1481 
1482   if (!m_invertOption.getValue()) {
1483     for (strokeIndex = 0; strokeIndex < (int)vi->getStrokeCount();
1484          strokeIndex++) {
1485       if (!vi->inCurrentGroup(strokeIndex)) continue;
1486       TStroke *currentStroke = vi->getStroke(strokeIndex);
1487       for (regionIndex = 0; regionIndex < (int)eraseImg.getRegionCount();
1488            regionIndex++) {
1489         TRegion *region = eraseImg.getRegion(regionIndex);
1490         if ((!m_selective.getValue() ||
1491              (m_selective.getValue() &&
1492               currentStroke->getStyle() == colorStyle)) &&
1493             region->contains(*currentStroke, true)) {
1494           eraseStrokes.push_back(strokeIndex);
1495           m_undo->addOldStroke(strokeIndex, vi->getVIStroke(strokeIndex));
1496         }
1497       }
1498     }
1499   } else {
1500     for (strokeIndex = 0; strokeIndex < (int)vi->getStrokeCount();
1501          strokeIndex++) {
1502       TStroke *currentStroke = vi->getStroke(strokeIndex);
1503       bool eraseIt           = false;
1504       for (regionIndex = 0; regionIndex < (int)eraseImg.getRegionCount();
1505            regionIndex++) {
1506         TRegion *region = eraseImg.getRegion(regionIndex);
1507         if (!m_selective.getValue() ||
1508             (m_selective.getValue() && currentStroke->getStyle() == colorStyle))
1509           eraseIt = true;
1510         if (region->contains(*currentStroke, true)) {
1511           eraseIt = false;
1512           break;
1513         }
1514       }
1515       if (eraseIt) {
1516         m_undo->addOldStroke(strokeIndex, vi->getVIStroke(strokeIndex));
1517         eraseStrokes.push_back(strokeIndex);
1518       }
1519     }
1520   }
1521   int i;
1522   for (i = (int)eraseStrokes.size() - 1; i >= 0; i--)
1523     vi->deleteStroke(eraseStrokes[i]);
1524   TUndoManager::manager()->add(m_undo);
1525   m_undo = 0;
1526 }
1527 
1528 //-----------------------------------------------------------------------------
1529 
1530 //! Viene richiamata la doErase sui frame compresi tra \b firstFrameId e \b
1531 //! lastFrameId.
1532 /*!Viene caricato il vettore \b fids con i TFrameId delle immagini sulle quali
1533  * si deve effettuare una cancellazione.*/
doMultiErase(TFrameId & firstFrameId,TFrameId & lastFrameId,const TStroke * firstStroke,const TStroke * lastStroke,EraserTool::EraseFunction eraseFunction)1534 void EraserTool::doMultiErase(TFrameId &firstFrameId, TFrameId &lastFrameId,
1535                               const TStroke *firstStroke,
1536                               const TStroke *lastStroke,
1537                               EraserTool::EraseFunction eraseFunction) {
1538   TXshSimpleLevel *sl =
1539       TTool::getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel();
1540   TStroke *first           = new TStroke();
1541   TStroke *last            = new TStroke();
1542   *first                   = *firstStroke;
1543   *last                    = *lastStroke;
1544   TVectorImageP firstImage = new TVectorImage();
1545   TVectorImageP lastImage  = new TVectorImage();
1546   firstImage->addStroke(first);
1547   lastImage->addStroke(last);
1548 
1549   bool backward = false;
1550   if (firstFrameId > lastFrameId) {
1551     std::swap(firstFrameId, lastFrameId);
1552     backward = true;
1553   }
1554   assert(firstFrameId <= lastFrameId);
1555 
1556   std::vector<TFrameId> allFids;
1557   sl->getFids(allFids);
1558   std::vector<TFrameId>::iterator i0 = allFids.begin();
1559   while (i0 != allFids.end() && *i0 < firstFrameId) i0++;
1560   if (i0 == allFids.end()) return;
1561   std::vector<TFrameId>::iterator i1 = i0;
1562   while (i1 != allFids.end() && *i1 <= lastFrameId) i1++;
1563   assert(i0 < i1);
1564   std::vector<TFrameId> fids(i0, i1);
1565   int m = fids.size();
1566   assert(m > 0);
1567 
1568   // Find the starting frame for the current level in the XSheet.
1569   TTool::Application *app = TTool::getApplication();
1570   int startRowInXSheet = 0, endRowInXSheet = 0;
1571   if (app && app->getCurrentFrame()->isEditingScene()) {
1572     int currentRow     = getFrame();
1573     TXsheet *xSheet    = getXsheet();
1574     TXshColumn *column = xSheet->getColumn(getColumnIndex());
1575     column->getLevelRange(currentRow, startRowInXSheet, endRowInXSheet);
1576   }
1577 
1578   enum TInbetween::TweenAlgorithm algorithm = TInbetween::LinearInterpolation;
1579   if (m_interpolation.getValue() == EASE_IN_INTERPOLATION) {
1580     algorithm = TInbetween::EaseInInterpolation;
1581   } else if (m_interpolation.getValue() == EASE_OUT_INTERPOLATION) {
1582     algorithm = TInbetween::EaseOutInterpolation;
1583   } else if (m_interpolation.getValue() == EASE_IN_OUT_INTERPOLATION) {
1584     algorithm = TInbetween::EaseInOutInterpolation;
1585   }
1586 
1587   TUndoManager::manager()->beginBlock();
1588   for (int i = 0; i < m; ++i) {
1589     TFrameId fid = fids[i];
1590     assert(firstFrameId <= fid && fid <= lastFrameId);
1591     double t = m > 1 ? (double)i / (double)(m - 1) : 0.5;
1592     t        = TInbetween::interpolation(t, algorithm);
1593     // Setto il fid come corrente per notificare il cambiamento dell'immagine
1594     if (app) {
1595       if (app->getCurrentFrame()->isEditingScene())
1596         app->getCurrentFrame()->setFrame(startRowInXSheet + fid.getNumber() -
1597                                          1);
1598       else
1599         app->getCurrentFrame()->setFid(fid);
1600     }
1601     doErase(backward ? 1 - t : t, sl, fid, firstImage, lastImage,
1602             eraseFunction);
1603     notifyImageChanged();
1604   }
1605   TUndoManager::manager()->endBlock();
1606 }
1607 
1608 //-----------------------------------------------------------------------------
1609 
1610 //! Viene richiamata la \b eraseRegion per il frame giusto nel caso della
1611 //! modalita' "Frame Range".
1612 /*!Nei casi in cui \b t e' diverso da zero e uno, viene generata una nuova \b
1613    TVectorImageP richiamando la \b TInbetween.
1614         La nuova immagine contiene lo stroke da dare alla eraseRegion. \b fid e'
1615    il TFrameId dell'immagine sulla quale
1616         bisogna effettuare la cancellazione.*/
doErase(double t,const TXshSimpleLevelP & sl,const TFrameId & fid,const TVectorImageP & firstImage,const TVectorImageP & lastImage,EraserTool::EraseFunction eraseFunction)1617 void EraserTool::doErase(double t, const TXshSimpleLevelP &sl,
1618                          const TFrameId &fid, const TVectorImageP &firstImage,
1619                          const TVectorImageP &lastImage,
1620                          EraserTool::EraseFunction eraseFunction) {
1621   //	TImageLocation imageLocation(m_level->getName(),fid);
1622   TVectorImageP img = sl->getFrame(fid, true);
1623   if (t == 0)
1624     (this->*eraseFunction)(img, firstImage->getStroke(0));  //,imageLocation);
1625   else if (t == 1)
1626     (this->*eraseFunction)(img, lastImage->getStroke(0));  //,imageLocation);
1627   else {
1628     assert(firstImage->getStrokeCount() == 1);
1629     assert(lastImage->getStrokeCount() == 1);
1630     TVectorImageP vi = TInbetween(firstImage, lastImage).tween(t);
1631     assert(vi->getStrokeCount() == 1);
1632     (this->*eraseFunction)(img, vi->getStroke(0));  //,imageLocation);
1633   }
1634 }
1635 
1636 //-----------------------------------------------------------------------------
1637 
1638 //! Effettua la cancellazione nella modalita' "Frame range".
1639 /*! Se il primo frame e' gia stato selezionato richiama la \b doMultiErase;
1640  altrimenti viene inizializzato
1641  \b m_firstStroke.*/
multiErase(TStroke * stroke,const TMouseEvent & e,EraserTool::EraseFunction eraseFunction)1642 void EraserTool::multiErase(TStroke *stroke, const TMouseEvent &e,
1643                             EraserTool::EraseFunction eraseFunction) {
1644   TTool::Application *application = TTool::getApplication();
1645   if (!application) return;
1646 
1647   if (m_firstFrameSelected) {
1648     if (m_firstStroke && stroke) {
1649       TFrameId tmpFrameId = getCurrentFid();
1650       doMultiErase(m_firstFrameId, tmpFrameId, m_firstStroke, stroke,
1651                    eraseFunction);
1652     }
1653     if (e.isShiftPressed()) {
1654       m_firstStroke  = new TStroke(*stroke);
1655       m_firstFrameId = getCurrentFid();
1656     } else {
1657       if (application->getCurrentFrame()->isEditingScene()) {
1658         application->getCurrentColumn()->setColumnIndex(m_currCell.first);
1659         application->getCurrentFrame()->setFrame(m_currCell.second);
1660       } else
1661         application->getCurrentFrame()->setFid(m_veryFirstFrameId);
1662       resetMulti();
1663     }
1664   } else {
1665     m_firstStroke = new TStroke(*stroke);
1666     if (application->getCurrentFrame()->isEditingScene())
1667       m_currCell =
1668           std::pair<int, int>(application->getCurrentColumn()->getColumnIndex(),
1669                               application->getCurrentFrame()->getFrame());
1670   }
1671 }
1672 
1673 //-----------------------------------------------------------------------------
1674 /*! When the tool is switched during dragging, Erase end processing is performed
1675  */
onDeactivate()1676 void EraserTool::onDeactivate() {
1677   if (!m_active) return;
1678 
1679   m_active = false;
1680 
1681   // TODO : finishing erase procedure is only available for normal type.
1682   // Supporting other types is needed. 2016/1/22 shun_iwasawa
1683   if (m_eraseType.getValue() != NORMAL_ERASE) return;
1684 
1685   TImageP image(getImage(true));
1686   TVectorImageP vi                = image;
1687   TTool::Application *application = TTool::getApplication();
1688   if (!vi || !application) return;
1689 
1690   stopErase(vi);
1691 }
1692 
1693 // TTool *getEraserTool() {return &eraserTool;}
1694