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