1 
2 
3 #include "selectiontool.h"
4 #include "rasterselectiontool.h"
5 #include "vectorselectiontool.h"
6 #include "tcurveutil.h"
7 #include "tenv.h"
8 #include "drawutil.h"
9 #include "tools/toolhandle.h"
10 #include "tools/cursors.h"
11 #include "toonz/stage2.h"
12 #include "toonz/tobjecthandle.h"
13 
14 #include <QKeyEvent>
15 
16 #include <memory>
17 
18 using namespace ToolUtils;
19 using namespace DragSelectionTool;
20 
21 TEnv::StringVar SelectionType("SelectionType", "Rectangular");
22 
23 //-----------------------------------------------------------------------------
24 
createNewDragTool(SelectionTool * st,Args...args)25 template <typename Tv, typename Tr, typename... Args> DragSelectionTool::DragTool* createNewDragTool(SelectionTool* st, Args... args) {
26   VectorSelectionTool *vst = dynamic_cast<VectorSelectionTool *>(st);
27   RasterSelectionTool *rst = dynamic_cast<RasterSelectionTool *>(st);
28   if (vst)
29     return new Tv(vst, args...);
30   else if (rst)
31     return new Tr(rst, args...);
32   return nullptr;
33 }
34 
createNewMoveSelectionTool(SelectionTool * st)35 DragSelectionTool::DragTool *createNewMoveSelectionTool(SelectionTool *st) {
36   return createNewDragTool<VectorMoveSelectionTool, RasterMoveSelectionTool>(st);
37 }
38 
createNewRotationTool(SelectionTool * st)39 DragSelectionTool::DragTool *createNewRotationTool(SelectionTool *st) {
40   return createNewDragTool<VectorRotationTool, RasterRotationTool>(st);
41 }
42 
createNewFreeDeformTool(SelectionTool * st)43 DragSelectionTool::DragTool *createNewFreeDeformTool(SelectionTool *st) {
44   return createNewDragTool<VectorFreeDeformTool, RasterFreeDeformTool>(st);
45 }
46 
createNewScaleTool(SelectionTool * st,ScaleType type)47 DragSelectionTool::DragTool *createNewScaleTool(SelectionTool *st, ScaleType type) {
48   return createNewDragTool<VectorScaleTool, RasterScaleTool>(st, type);
49 }
50 
51 //=============================================================================
52 namespace {
53 //-----------------------------------------------------------------------------
54 
55 // Return index of point with min x or y
tminPoint(std::vector<TPointD> points,bool isX)56 int tminPoint(std::vector<TPointD> points, bool isX) {
57   int i;
58   int index = 0;
59   TPointD p = points[0];
60   for (i = 1; i < (int)points.size(); i++) {
61     TPointD nextP = points[i];
62     if ((isX && p.x < nextP.x) || (!isX && p.y < nextP.y)) continue;
63     index = i;
64   }
65   return index;
66 }
67 
68 //-----------------------------------------------------------------------------
69 
tminPoint(TPointD p0,TPointD p1,bool isX)70 int tminPoint(TPointD p0, TPointD p1, bool isX) {
71   std::vector<TPointD> v;
72   v.push_back(p0);
73   v.push_back(p1);
74   return tminPoint(v, isX);
75 }
76 
77 //=============================================================================
78 }  // namespace
79 //-----------------------------------------------------------------------------
80 
81 //=============================================================================
82 // FourPoints
83 //-----------------------------------------------------------------------------
84 
orderedPoints() const85 FourPoints DragSelectionTool::FourPoints::orderedPoints() const {
86   FourPoints newPoints;
87   int i;
88   std::vector<TPointD> allPoints;
89   allPoints.push_back(m_p00);
90   allPoints.push_back(m_p01);
91   allPoints.push_back(m_p10);
92   allPoints.push_back(m_p11);
93   int minXindex1 = tminPoint(allPoints, true);
94   std::vector<TPointD> points;
95   for (i = 0; i < 4; i++)
96     if (i != minXindex1) points.push_back(allPoints[i]);
97   int minXindex2 = tminPoint(points, true);
98 
99   int index = tminPoint(allPoints[minXindex1], points[minXindex2], false);
100   TPointD newPoint1 = allPoints[minXindex1];
101   TPointD newPoint2 = points[minXindex2];
102   if (index == 1) std::swap(newPoint1, newPoint2);
103   newPoints.setP00(newPoint1);
104   newPoints.setP01(newPoint2);
105 
106   std::vector<TPointD> points2;
107   for (i = 0; i < 3; i++)
108     if (i != minXindex2) points2.push_back(points[i]);
109 
110   index = tminPoint(points2, false);
111   newPoints.setP10(points2[index]);
112   newPoints.setP11(points2[(index == 0) ? 1 : 0]);
113   return newPoints;
114 }
115 
116 //-----------------------------------------------------------------------------
117 
getPoint(int index) const118 TPointD DragSelectionTool::FourPoints::getPoint(int index) const {
119   if (index == 0)
120     return m_p00;
121   else if (index == 1)
122     return m_p10;
123   else if (index == 2)
124     return m_p11;
125   else if (index == 3)
126     return m_p01;
127   else if (index == 4)
128     return (m_p00 + m_p10) * 0.5;
129   else if (index == 5)
130     return (m_p10 + m_p11) * 0.5;
131   else if (index == 6)
132     return (m_p11 + m_p01) * 0.5;
133   else if (index == 7)
134     return (m_p01 + m_p00) * 0.5;
135   return TPointD();
136 }
137 
138 //-----------------------------------------------------------------------------
139 
setPoint(int index,const TPointD & p)140 void DragSelectionTool::FourPoints::setPoint(int index, const TPointD &p) {
141   if (index == 0)
142     m_p00 = p;
143   else if (index == 1)
144     m_p10 = p;
145   else if (index == 2)
146     m_p11 = p;
147   else if (index == 3)
148     m_p01 = p;
149 }
150 
151 //-----------------------------------------------------------------------------
152 
enlarge(double d)153 FourPoints DragSelectionTool::FourPoints::enlarge(double d) {
154   TPointD v   = normalize(getP10() - getP00());
155   TPointD p00 = getP00() - d * v;
156   TPointD p10 = getP10() + d * v;
157   v           = normalize(getP11() - getP10());
158   p10         = p10 - d * v;
159   TPointD p11 = getP11() + d * v;
160   v           = normalize(getP01() - getP11());
161   p11         = p11 - d * v;
162   TPointD p01 = getP01() + d * v;
163   v           = normalize(getP00() - getP01());
164   p01         = p01 - d * v;
165   p00         = p00 + d * v;
166   return FourPoints(p00, p01, p10, p11);
167 }
168 
169 //-----------------------------------------------------------------------------
170 
isEmpty()171 bool DragSelectionTool::FourPoints::isEmpty() {
172   return ((getP00().x == getP01().x && getP01().x == getP10().x &&
173            getP10().x == getP11().x) ||
174           (getP00().y == getP01().y && getP01().y == getP10().y &&
175            getP10().y == getP11().y));
176 }
177 
178 //-----------------------------------------------------------------------------
179 
empty()180 void DragSelectionTool::FourPoints::empty() {
181   m_p00 = TPointD();
182   m_p01 = TPointD();
183   m_p10 = TPointD();
184   m_p11 = TPointD();
185 }
186 
187 //-----------------------------------------------------------------------------
188 
contains(TPointD p)189 bool DragSelectionTool::FourPoints::contains(TPointD p) {
190   double maxDistance =
191       std::max(tdistance2(getP00(), getP11()), tdistance2(getP10(), getP01()));
192   TPointD outP = p + maxDistance * TPointD(1, 1);
193   TSegment segment(outP, p);
194   std::vector<DoublePair> d;
195   int inters = intersect(TSegment(getP00(), getP10()), segment, d);
196   inters += intersect(TSegment(getP10(), getP11()), segment, d);
197   inters += intersect(TSegment(getP11(), getP01()), segment, d);
198   inters += intersect(TSegment(getP01(), getP00()), segment, d);
199   return inters % 2 == 1;
200 }
201 
202 //-----------------------------------------------------------------------------
203 
getBox() const204 TRectD DragSelectionTool::FourPoints::getBox() const {
205   double x0 = std::min({getP00().x, getP10().x, getP01().x, getP11().x});
206   double y0 = std::min({getP00().y, getP10().y, getP01().y, getP11().y});
207   double x1 = std::max({getP00().x, getP10().x, getP01().x, getP11().x});
208   double y1 = std::max({getP00().y, getP10().y, getP01().y, getP11().y});
209   return TRectD(TPointD(x0, y0), TPointD(x1, y1));
210 }
211 
212 //-----------------------------------------------------------------------------
213 
operator =(const TRectD & r)214 FourPoints &DragSelectionTool::FourPoints::operator=(const TRectD &r) {
215   setP00(r.getP00());
216   setP01(r.getP01());
217   setP10(r.getP10());
218   setP11(r.getP11());
219   return *this;
220 }
221 
222 //-----------------------------------------------------------------------------
223 
operator ==(const FourPoints & p) const224 bool DragSelectionTool::FourPoints::operator==(const FourPoints &p) const {
225   return getP00() == p.getP00() && getP01() == p.getP01() &&
226          getP10() == p.getP10() && getP11() == p.getP11();
227 }
228 
229 //-----------------------------------------------------------------------------
230 
operator *(const TAffine & aff) const231 FourPoints DragSelectionTool::FourPoints::operator*(const TAffine &aff) const {
232   FourPoints p;
233   p.setP00(aff * getP00());
234   p.setP10(aff * getP10());
235   p.setP11(aff * getP11());
236   p.setP01(aff * getP01());
237   return p;
238 }
239 
240 //-----------------------------------------------------------------------------
241 
drawFourPoints(const FourPoints & rect,const TPixel32 & color,unsigned short stipple,bool doContrast)242 void DragSelectionTool::drawFourPoints(const FourPoints &rect,
243                                        const TPixel32 &color,
244                                        unsigned short stipple,
245                                        bool doContrast) {
246   GLint src, dst;
247   bool isEnabled;
248   tglColor(color);
249   if (doContrast) {
250     if (color == TPixel32::Black) tglColor(TPixel32(90, 90, 90));
251     isEnabled = glIsEnabled(GL_BLEND);
252     glGetIntegerv(GL_BLEND_SRC, &src);
253     glGetIntegerv(GL_BLEND_DST, &dst);
254     glEnable(GL_BLEND);
255     glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
256   }
257 
258   if (stipple != 0xffff) {
259     glLineStipple(1, stipple);
260     glEnable(GL_LINE_STIPPLE);
261   }
262 
263   glBegin(GL_LINE_STRIP);
264   tglVertex(rect.getP00());
265   tglVertex(rect.getP01());
266   tglVertex(rect.getP11());
267   tglVertex(rect.getP10());
268   tglVertex(rect.getP00());
269   glEnd();
270   glDisable(GL_LINE_STIPPLE);
271   if (doContrast) {
272     if (!isEnabled) glDisable(GL_BLEND);
273     glBlendFunc(src, dst);
274   }
275 }
276 
277 //=============================================================================
278 namespace {
279 //-----------------------------------------------------------------------------
280 
281 //=============================================================================
282 // UndoMoveCenter
283 //-----------------------------------------------------------------------------
284 
285 class UndoMoveCenter final : public TUndo {
286   SelectionTool *m_tool;
287   TAffine m_aff;
288 
289 public:
UndoMoveCenter(SelectionTool * tool,const TAffine & aff)290   UndoMoveCenter(SelectionTool *tool, const TAffine &aff)
291       : m_tool(tool), m_aff(aff) {}
~UndoMoveCenter()292   ~UndoMoveCenter() {}
undo() const293   void undo() const override {
294     m_tool->setCenter(m_aff.inv() * m_tool->getCenter());
295     m_tool->invalidate();
296   }
redo() const297   void redo() const override {
298     m_tool->setCenter(m_aff * m_tool->getCenter());
299     m_tool->invalidate();
300   }
getSize() const301   int getSize() const override { return sizeof(*this) + sizeof(*m_tool); }
302 
getHistoryString()303   QString getHistoryString() override { return QObject::tr("Move Center"); }
304 };
305 
306 //=============================================================================
307 // MoveCenterTool
308 //-----------------------------------------------------------------------------
309 
310 class MoveCenterTool final : public DragTool {
311   TPointD m_startPos;
312   TAffine m_transform;
313 
314 public:
MoveCenterTool(SelectionTool * tool)315   MoveCenterTool(SelectionTool *tool)
316       : DragTool(tool), m_startPos(), m_transform() {}
translateCenter(TAffine aff)317   void translateCenter(TAffine aff) {
318     getTool()->setCenter(aff * getTool()->getCenter());
319     m_transform *= aff;
320     getTool()->invalidate();
321   }
leftButtonDown(const TPointD & pos,const TMouseEvent & e)322   void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override {
323     m_startPos = pos;
324   }
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)325   void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override {
326     TPointD delta      = pos - m_startPos;
327     FourPoints bbox    = getTool()->getBBox();
328     TPointD bboxCenter = 0.5 * (bbox.getP11() + bbox.getP00());
329     double maxDistance2 =
330         32 * getTool()->getPixelSize() * getTool()->getPixelSize();
331     TAffine aff       = m_transform.inv() * TTranslation(delta);
332     TPointD newCenter = aff * getTool()->getCenter();
333     if (tdistance2(newCenter, bboxCenter) < maxDistance2)
334       translateCenter(TTranslation(bboxCenter - getTool()->getCenter()));
335     else
336       translateCenter(aff);
337   }
leftButtonUp(const TPointD & pos,const TMouseEvent & e)338   void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override {
339     UndoMoveCenter *undo = new UndoMoveCenter(getTool(), m_transform);
340     TUndoManager::manager()->add(undo);
341   }
draw()342   void draw() override {}
343 };
344 
345 //=============================================================================
346 }  // namespace
347 //-----------------------------------------------------------------------------
348 
349 //=============================================================================
350 // DeformTool
351 //-----------------------------------------------------------------------------
352 
DeformTool(SelectionTool * tool)353 DragSelectionTool::DeformTool::DeformTool(SelectionTool *tool)
354     : DragTool(tool)
355     , m_curPos()
356     , m_isDragging(false)
357     , m_startScaleValue(tool->m_deformValues.m_scaleValue) {}
358 
359 //-----------------------------------------------------------------------------
360 
getSymmetricPointIndex(int index) const361 int DragSelectionTool::DeformTool::getSymmetricPointIndex(int index) const {
362   if (index == 0 || index == 4 || index == 1 || index == 5) return index + 2;
363   return index - 2;
364 }
365 
366 //-----------------------------------------------------------------------------
367 
getBeforePointIndex(int index) const368 int DragSelectionTool::DeformTool::getBeforePointIndex(int index) const {
369   if (index < 4) return (index == 0) ? 7 : index + 3;
370   return index - 4;
371 }
372 
373 //-----------------------------------------------------------------------------
374 
getNextPointIndex(int index) const375 int DragSelectionTool::DeformTool::getNextPointIndex(int index) const {
376   if (index < 4) return index + 4;
377   return (index == 7) ? 0 : index - 3;
378 }
379 
380 //-----------------------------------------------------------------------------
381 
getBeforeVertexIndex(int index) const382 int DragSelectionTool::DeformTool::getBeforeVertexIndex(int index) const {
383   if (index < 4) return (index == 0) ? 3 : index - 1;
384   return index - 4;
385 }
386 
387 //-----------------------------------------------------------------------------
388 
getNextVertexIndex(int index) const389 int DragSelectionTool::DeformTool::getNextVertexIndex(int index) const {
390   if (index < 4) return (index == 3) ? 0 : index + 1;
391   return (index == 7) ? 0 : index - 3;
392 }
393 
394 //-----------------------------------------------------------------------------
395 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)396 void DragSelectionTool::DeformTool::leftButtonDown(const TPointD &pos,
397                                                    const TMouseEvent &e) {
398   m_isDragging = true;
399   m_curPos     = pos;
400   setStartPos(pos);
401 }
402 
403 //-----------------------------------------------------------------------------
404 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)405 void DragSelectionTool::DeformTool::leftButtonUp(const TPointD &pos,
406                                                  const TMouseEvent &e) {
407   addTransformUndo();
408   m_isDragging = false;
409 }
410 
411 //=============================================================================
412 // Rotation
413 //-----------------------------------------------------------------------------
414 
Rotation(DeformTool * deformTool)415 DragSelectionTool::Rotation::Rotation(DeformTool *deformTool)
416     : m_curAng(), m_dstAng(), m_deformTool(deformTool) {}
417 
418 //-----------------------------------------------------------------------------
419 
getStartCenter() const420 TPointD DragSelectionTool::Rotation::getStartCenter() const {
421   return m_deformTool->getTool()->getCenter();
422 }
423 
424 //-----------------------------------------------------------------------------
425 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)426 void DragSelectionTool::Rotation::leftButtonDrag(const TPointD &pos,
427                                                  const TMouseEvent &e) {
428   SelectionTool *tool  = m_deformTool->getTool();
429   TPointD center       = tool->getCenter();
430   TPointD curPos       = m_deformTool->getCurPos();
431   TPointD delta        = pos - curPos;
432   TPointD a            = pos - center;
433   TPointD b            = (pos - delta) - center;
434   double a2            = norm2(a);
435   double b2            = norm2(b);
436   const double epsilon = 1e-8;
437   double dang          = 0;
438   double scale         = 1;
439   if (a2 <= epsilon || b2 <= epsilon) return;
440   dang = asin(cross(a, b) / sqrt(a2 * b2)) * -M_180_PI;
441   if (e.isShiftPressed()) {
442     m_dstAng += dang;
443     double ang = tfloor((int)(m_dstAng + 22.5), 45);
444     dang       = ang - m_curAng;
445     m_curAng   = ang;
446   } else {
447     m_dstAng += dang;
448     dang     = m_dstAng - m_curAng;
449     m_curAng = m_dstAng;
450   }
451 
452   tool->m_deformValues.m_rotationAngle =
453       tool->m_deformValues.m_rotationAngle + dang;
454   m_deformTool->transform(TRotation(center, dang), dang);
455   m_deformTool->setCurPos(pos);
456   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
457 }
458 
459 //-----------------------------------------------------------------------------
460 
draw()461 void DragSelectionTool::Rotation::draw() {
462   tglDrawSegment(m_deformTool->getCurPos(),
463                  m_deformTool->getTool()->getCenter());
464 }
465 
466 //=============================================================================
467 // FreeDeform
468 //-----------------------------------------------------------------------------
469 
FreeDeform(DeformTool * deformTool)470 DragSelectionTool::FreeDeform::FreeDeform(DeformTool *deformTool)
471     : m_deformTool(deformTool) {}
472 
473 //-----------------------------------------------------------------------------
474 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)475 void DragSelectionTool::FreeDeform::leftButtonDrag(const TPointD &pos,
476                                                    const TMouseEvent &e) {
477   SelectionTool *tool = m_deformTool->getTool();
478   TPointD delta       = pos - m_deformTool->getCurPos();
479   TPointD center      = tool->getCenter();
480   int index           = tool->getSelectedPoint();
481   FourPoints bbox     = tool->getBBox();
482   FourPoints newBbox  = bbox;
483   if (index < 4)
484     bbox.setPoint(index, bbox.getPoint(index) + delta);
485   else {
486     int beforeIndex = m_deformTool->getBeforeVertexIndex(index);
487     bbox.setPoint(beforeIndex, bbox.getPoint(beforeIndex) + delta);
488     bbox.setPoint(index, bbox.getPoint(index) + delta);
489     int nextIndex = m_deformTool->getNextVertexIndex(index);
490     bbox.setPoint(nextIndex, bbox.getPoint(nextIndex) + delta);
491   }
492   tool->setBBox(bbox);
493   m_deformTool->setCurPos(pos);
494   m_deformTool->applyTransform(bbox);
495 }
496 
497 //=============================================================================
498 // MoveSelection
499 //-----------------------------------------------------------------------------
500 
MoveSelection(DeformTool * deformTool)501 DragSelectionTool::MoveSelection::MoveSelection(DeformTool *deformTool)
502     : m_deformTool(deformTool), m_lastDelta(), m_firstPos() {}
503 
504 //-----------------------------------------------------------------------------
505 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)506 void DragSelectionTool::MoveSelection::leftButtonDown(const TPointD &pos,
507                                                       const TMouseEvent &e) {
508   m_deformTool->setCurPos(pos);
509   m_firstPos = pos;
510 }
511 
512 //-----------------------------------------------------------------------------
513 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)514 void DragSelectionTool::MoveSelection::leftButtonDrag(const TPointD &pos,
515                                                       const TMouseEvent &e) {
516   TAffine aff;
517   TPointD curPos = m_deformTool->getCurPos();
518   TPointD delta  = pos - curPos;
519   if (e.isShiftPressed()) {
520     if (m_lastDelta == TPointD()) {
521       TPointD totalDelta = curPos - m_firstPos;
522       aff                = TTranslation(totalDelta).inv();
523     } else
524       aff = TTranslation(m_lastDelta).inv();
525     if (fabs((curPos - m_firstPos).x) > fabs((curPos - m_firstPos).y))
526       m_lastDelta = TPointD((curPos - m_firstPos).x, 0);
527     else
528       m_lastDelta = TPointD(0, (curPos - m_firstPos).y);
529     aff *= TTranslation(m_lastDelta);
530   } else
531     aff         = TTranslation(delta);
532   double factor = 1.0 / Stage::inch;
533   m_deformTool->getTool()->m_deformValues.m_moveValue =
534       m_deformTool->getTool()->m_deformValues.m_moveValue + factor * delta;
535   m_deformTool->transform(aff);
536   m_deformTool->setCurPos(pos);
537   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
538 }
539 
540 //=============================================================================
541 // Scale
542 //-----------------------------------------------------------------------------
543 
Scale(DeformTool * deformTool,ScaleType type)544 DragSelectionTool::Scale::Scale(DeformTool *deformTool, ScaleType type)
545     : m_deformTool(deformTool)
546     , m_startCenter(deformTool->getTool()->getCenter())
547     , m_type(type)
548     , m_isShiftPressed(false)
549     , m_isAltPressed(false)
550     , m_scaleInCenter(true) {
551   int i;
552   for (i = 0; i < (int)m_deformTool->getTool()->getBBoxsCount(); i++)
553     m_startBboxs.push_back(m_deformTool->getTool()->getBBox(i));
554 }
555 
556 //-----------------------------------------------------------------------------
557 
getIntersectionPoint(const TPointD & point0,const TPointD & point1,const TPointD & point2,const TPointD & point3,const TPointD & p) const558 TPointD DragSelectionTool::Scale::getIntersectionPoint(const TPointD &point0,
559                                                        const TPointD &point1,
560                                                        const TPointD &point2,
561                                                        const TPointD &point3,
562                                                        const TPointD &p) const {
563   // Parametri della retta passante per point0, point1
564   double d1x = point0.x - point1.x;
565   double m1  = d1x == 0 ? 0 : (point0.y - point1.y) / d1x;
566   double q1  = point1.y - m1 * point1.x;
567   // Parametri della retta passante per p parallela alla retta passante per
568   // point1, point2
569   double d2x = point2.x - point3.x;
570   double m2  = d2x == 0 ? 0 : (point2.y - point3.y) / d2x;
571   double q2  = p.y - m2 * p.x;
572   // Calcolo l'intersezione tra le due rette
573   double x, y, m, q;
574   if (d1x == 0) {
575     x = point0.x;
576     m = m2;
577     q = q2;
578   } else if (d2x == 0) {
579     x = p.x;
580     m = m1;
581     q = q1;
582   } else {
583     assert(m1 != m2);
584     x = (q1 - q2) / (m2 - m1);
585     m = m1;
586     q = q1;
587   }
588   y = m * x + q;
589   return TPointD(x, y);
590 }
591 
592 //-----------------------------------------------------------------------------
593 
bboxScale(int index,const FourPoints & oldBbox,const TPointD & pos)594 DragSelectionTool::FourPoints DragSelectionTool::Scale::bboxScale(
595     int index, const FourPoints &oldBbox, const TPointD &pos) {
596   FourPoints bbox = oldBbox;
597   TPointD p       = oldBbox.getPoint(index);
598   int nextIndex   = m_deformTool->getNextVertexIndex(index);
599   TPointD nextP   = oldBbox.getPoint(nextIndex);
600   int nextIndex2  = m_deformTool->getNextVertexIndex(nextIndex);
601   TPointD next2P  = oldBbox.getPoint(nextIndex2);
602   TPointD newP    = getIntersectionPoint(next2P, nextP, nextP, p, pos);
603   bbox.setPoint(nextIndex, newP);
604 
605   int beforeIndex  = m_deformTool->getBeforeVertexIndex(index);
606   TPointD beforeP  = oldBbox.getPoint(beforeIndex);
607   int before2Index = m_deformTool->getBeforeVertexIndex(beforeIndex);
608   TPointD before2P = oldBbox.getPoint(before2Index);
609   newP             = getIntersectionPoint(before2P, beforeP, beforeP, p, pos);
610   bbox.setPoint(beforeIndex, newP);
611 
612   if (index < 4) bbox.setPoint(index, pos);
613 
614   return bbox;
615 }
616 
617 //-----------------------------------------------------------------------------
618 
computeScaleValue(int movedIndex,const FourPoints newBbox)619 TPointD DragSelectionTool::Scale::computeScaleValue(int movedIndex,
620                                                     const FourPoints newBbox) {
621   TPointD p = m_startBboxs[0].getPoint(movedIndex);
622   if (movedIndex < 4) {
623     int beforeIndex = m_deformTool->getBeforePointIndex(movedIndex);
624     int nextIndex   = m_deformTool->getNextPointIndex(movedIndex);
625     FourPoints bbox = bboxScale(nextIndex, newBbox, p);
626     TPointD scale1  = computeScaleValue(beforeIndex, bbox);
627     bbox            = bboxScale(beforeIndex, newBbox, p);
628     TPointD scale2  = computeScaleValue(nextIndex, bbox);
629     if (movedIndex % 2 == 0)
630       return TPointD(scale1.x, scale2.y);
631     else
632       return TPointD(scale2.x, scale1.y);
633   }
634   int symmetricIndex = m_deformTool->getSymmetricPointIndex(movedIndex);
635   TPointD s          = m_startBboxs[0].getPoint(symmetricIndex);
636   TPointD center     = m_scaleInCenter ? m_startCenter : s;
637   TPointD nearP =
638       m_startBboxs[0].getPoint(m_deformTool->getBeforePointIndex(movedIndex));
639   TPointD pc   = getIntersectionPoint(nearP, p, p, s, center);
640   TPointD newp = newBbox.getPoint(movedIndex);
641   TPointD news = newBbox.getPoint(symmetricIndex);
642   TPointD newNearP =
643       newBbox.getPoint(m_deformTool->getBeforePointIndex(movedIndex));
644   TPointD newpc = getIntersectionPoint(newNearP, newp, newp, news, center);
645 
646   double newD             = tdistance2(newpc, center);
647   double oldD             = tdistance2(pc, center);
648   double f                = sqrt(newD / oldD) - 1;
649   TPointD startScaleValue = m_deformTool->getStartScaleValue();
650   if (movedIndex % 2 == 1) {
651     double sign = (pc.x < center.x && newpc.x < center.x) ||
652                           (pc.x > center.x && newpc.x > center.x)
653                       ? 1
654                       : -1;
655     double x =
656         startScaleValue.x == 0 ? f : startScaleValue.x + startScaleValue.x * f;
657     return TPointD(sign * x, startScaleValue.y);
658   } else {
659     double sign = (pc.y < center.y && newpc.y < center.y) ||
660                           (pc.y > center.y && newpc.y > center.y)
661                       ? 1
662                       : -1;
663     double y =
664         startScaleValue.y == 0 ? f : startScaleValue.y + startScaleValue.y * f;
665     return TPointD(startScaleValue.x, sign * y);
666   }
667 }
668 
669 //-----------------------------------------------------------------------------
670 
getScaledPoint(int index,const FourPoints & oldBbox,const TPointD scaleValue,const TPointD center)671 TPointD DragSelectionTool::Scale::getScaledPoint(int index,
672                                                  const FourPoints &oldBbox,
673                                                  const TPointD scaleValue,
674                                                  const TPointD center) {
675   TPointD p          = oldBbox.getPoint(index);
676   int symmetricIndex = m_deformTool->getSymmetricPointIndex(index);
677   TPointD s          = oldBbox.getPoint(symmetricIndex);
678   if (index < 4) {
679     int beforeIndex = m_deformTool->getBeforePointIndex(index);
680     int nextIndex   = m_deformTool->getNextPointIndex(index);
681     TPointD newbp   = getScaledPoint(beforeIndex, oldBbox, scaleValue, center);
682     TPointD newnp   = getScaledPoint(nextIndex, oldBbox, scaleValue, center);
683     TPointD bp = oldBbox.getPoint(m_deformTool->getBeforePointIndex(index));
684     TPointD np = oldBbox.getPoint(m_deformTool->getNextPointIndex(index));
685     TPointD in = getIntersectionPoint(np, p, bp, p, newbp);
686     return getIntersectionPoint(newbp, in, np, p, newnp);
687   }
688   TPointD nearP = oldBbox.getPoint(m_deformTool->getBeforePointIndex(index));
689   TPointD nearS =
690       oldBbox.getPoint(m_deformTool->getBeforePointIndex(symmetricIndex));
691   TPointD pc = getIntersectionPoint(nearP, p, p, s, center);
692   TPointD sc = getIntersectionPoint(nearS, s, p, s, center);
693   if (center == pc) return pc;
694   TPointD v       = normalize(center - pc);
695   double currentD = tdistance(sc, pc);
696   double startD   = (index % 2 == 1)
697                       ? currentD / m_deformTool->getStartScaleValue().x
698                       : currentD / m_deformTool->getStartScaleValue().y;
699   double factor = (index % 2 == 1) ? scaleValue.x : scaleValue.y;
700   double d = (currentD - startD * factor) * tdistance(center, pc) / currentD;
701   return TPointD(pc.x + d * v.x, pc.y + d * v.y);
702 }
703 
704 //-----------------------------------------------------------------------------
705 
getNewCenter(int index,const FourPoints bbox,const TPointD scaleValue)706 TPointD DragSelectionTool::Scale::getNewCenter(int index, const FourPoints bbox,
707                                                const TPointD scaleValue) {
708   int xIndex, yIndex;
709   if (index < 4) {
710     xIndex = m_deformTool->getBeforePointIndex(index);
711     yIndex = m_deformTool->getNextPointIndex(index);
712   } else {
713     xIndex =
714         m_deformTool->getNextPointIndex(m_deformTool->getNextPointIndex(index));
715     yIndex = index;
716   }
717   if (index % 2 == 1) std::swap(xIndex, yIndex);
718   FourPoints xBbox = bboxScale(xIndex, bbox, m_startCenter);
719   TPointD xCenter  = getScaledPoint(
720       xIndex, xBbox, scaleValue,
721       xBbox.getPoint(m_deformTool->getSymmetricPointIndex(xIndex)));
722   FourPoints yBbox = bboxScale(yIndex, bbox, m_startCenter);
723   TPointD yCenter  = getScaledPoint(
724       yIndex, yBbox, scaleValue,
725       yBbox.getPoint(m_deformTool->getSymmetricPointIndex(yIndex)));
726   TPointD in = getIntersectionPoint(bbox.getP00(), bbox.getP10(), bbox.getP10(),
727                                     bbox.getP11(), xCenter);
728   return getIntersectionPoint(in, xCenter, bbox.getP00(), bbox.getP10(),
729                               yCenter);
730 }
731 
732 //-----------------------------------------------------------------------------
733 
bboxScaleInCenter(int index,const FourPoints & oldBbox,const TPointD newPos,TPointD & scaleValue,const TPointD center,bool recomputeScaleValue)734 FourPoints DragSelectionTool::Scale::bboxScaleInCenter(
735     int index, const FourPoints &oldBbox, const TPointD newPos,
736     TPointD &scaleValue, const TPointD center, bool recomputeScaleValue) {
737   TPointD oldp = oldBbox.getPoint(index);
738   if (areAlmostEqual(oldp.x, newPos.x, 1e-2) &&
739       areAlmostEqual(oldp.y, newPos.y, 1e-2))
740     return oldBbox;
741   FourPoints bbox                     = bboxScale(index, oldBbox, newPos);
742   if (recomputeScaleValue) scaleValue = computeScaleValue(index, bbox);
743   if (!m_scaleInCenter) return bbox;
744   int symmetricIndex = m_deformTool->getSymmetricPointIndex(index);
745   // Gestisco il caso particolare in cui uno dei fattori di scalatura e' -100% e
746   // center e' al centro della bbox
747   if (bbox.getPoint(index) == oldBbox.getPoint(symmetricIndex)) {
748     bbox.setPoint(symmetricIndex, oldBbox.getPoint(index));
749     bbox.setPoint(m_deformTool->getNextPointIndex(symmetricIndex),
750                   oldBbox.getPoint(m_deformTool->getBeforePointIndex(index)));
751     bbox.setPoint(m_deformTool->getBeforePointIndex(symmetricIndex),
752                   oldBbox.getPoint(m_deformTool->getNextPointIndex(index)));
753   } else
754     bbox =
755         bboxScale(symmetricIndex, bbox,
756                   getScaledPoint(symmetricIndex, oldBbox, scaleValue, center));
757   return bbox;
758 }
759 
760 //-----------------------------------------------------------------------------
761 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)762 void DragSelectionTool::Scale::leftButtonDown(const TPointD &pos,
763                                               const TMouseEvent &e) {
764   m_isShiftPressed = e.isShiftPressed();
765   m_isAltPressed   = e.isAltPressed();
766 }
767 
768 //-----------------------------------------------------------------------------
769 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)770 void DragSelectionTool::Scale::leftButtonDrag(const TPointD &pos,
771                                               const TMouseEvent &e) {
772   SelectionTool *tool = m_deformTool->getTool();
773   bool isBboxReset    = false;
774   if (m_isShiftPressed != e.isShiftPressed() ||
775       m_isAltPressed != e.isAltPressed()) {
776     m_deformTool->applyTransform(m_startBboxs[0]);
777     tool->setBBox(m_startBboxs[0]);
778     tool->setCenter(m_startCenter);
779     isBboxReset      = true;
780     m_isShiftPressed = e.isShiftPressed();
781     m_isAltPressed   = e.isAltPressed();
782   }
783   TPointD newPos    = pos;
784   int selectedIndex = tool->getSelectedPoint();
785   if (m_isShiftPressed && m_type == ScaleType::GLOBAL) {
786     TPointD point = tool->getBBox().getPoint(selectedIndex);
787     TPointD delta;
788     if (!isBboxReset)
789       delta = pos - m_deformTool->getCurPos();
790     else
791       delta            = pos - m_deformTool->getStartPos();
792     int symmetricIndex = m_deformTool->getSymmetricPointIndex(selectedIndex);
793     TPointD symmetricPoint = tool->getBBox().getPoint(symmetricIndex);
794     TPointD v              = normalize(point - symmetricPoint);
795     delta                  = v * (v * delta);
796     newPos                 = point + delta;
797   }
798   m_scaleInCenter = m_isAltPressed;
799   m_deformTool->setCurPos(pos);
800   TPointD scaleValue = m_deformTool->transform(selectedIndex, newPos);
801   tool->m_deformValues.m_scaleValue = scaleValue;
802   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
803 }
804 
805 //=============================================================================
806 // SelectionTool
807 //-----------------------------------------------------------------------------
808 
SelectionTool(int targetType)809 SelectionTool::SelectionTool(int targetType)
810     : TTool("T_Selection")
811     , m_firstTime(true)
812     , m_dragTool(0)
813     , m_what(Outside)
814     , m_leftButtonMousePressed(false)
815     , m_shiftPressed(false)
816     , m_selecting(false)
817     , m_mousePosition(TPointD())
818     , m_stroke(0)
819     , m_justSelected(false)
820     , m_strokeSelectionType("Type:")
821     , m_deformValues()
822     , m_cursorId(ToolCursor::CURSOR_ARROW) {
823   bind(targetType);
824   m_prop.bind(m_strokeSelectionType);
825 
826   m_strokeSelectionType.addValue(RECT_SELECTION);
827   m_strokeSelectionType.addValue(FREEHAND_SELECTION);
828   m_strokeSelectionType.addValue(POLYLINE_SELECTION);
829   m_strokeSelectionType.setId("Type");
830 }
831 
832 //-----------------------------------------------------------------------------
833 
~SelectionTool()834 SelectionTool::~SelectionTool() {
835   delete m_dragTool;
836   if (m_stroke) {
837     delete m_stroke;
838     m_stroke = 0;
839   }
840   if (!m_freeDeformers.empty()) clearPointerContainer(m_freeDeformers);
841 }
842 
843 //-----------------------------------------------------------------------------
844 
clearDeformers()845 void SelectionTool::clearDeformers() { clearPointerContainer(m_freeDeformers); }
846 
847 //-----------------------------------------------------------------------------
848 
getCenter(int index) const849 TPointD SelectionTool::getCenter(int index) const {
850   if (m_centers.empty()) return TPointD();
851   assert((int)m_centers.size() > index);
852   return m_centers[index];
853 }
854 
855 //-----------------------------------------------------------------------------
856 
setCenter(const TPointD & center,int index)857 void SelectionTool::setCenter(const TPointD &center, int index) {
858   if (m_centers.empty()) return;
859   assert((int)m_centers.size() > index);
860   m_centers[index] = center;
861 }
862 
863 //-----------------------------------------------------------------------------
864 
getBBoxsCount() const865 int SelectionTool::getBBoxsCount() const { return m_bboxs.size(); }
866 
867 //-----------------------------------------------------------------------------
868 
getBBox(int index) const869 DragSelectionTool::FourPoints SelectionTool::getBBox(int index) const {
870   if (m_bboxs.empty()) return DragSelectionTool::FourPoints();
871   assert((int)m_bboxs.size() > index);
872   return m_bboxs[index];
873 }
874 
875 //-----------------------------------------------------------------------------
876 
setBBox(const DragSelectionTool::FourPoints & points,int index)877 void SelectionTool::setBBox(const DragSelectionTool::FourPoints &points,
878                             int index) {
879   if (m_bboxs.empty()) return;
880   assert((int)m_bboxs.size() > index);
881   m_bboxs[index] = points;
882 }
883 
884 //-----------------------------------------------------------------------------
885 
getFreeDeformer(int index) const886 FreeDeformer *SelectionTool::getFreeDeformer(int index) const {
887   if (m_freeDeformers.empty()) return 0;
888   return m_freeDeformers[index];
889 }
890 
891 //-----------------------------------------------------------------------------
892 
updateTranslation()893 void SelectionTool::updateTranslation() {
894   m_strokeSelectionType.setQStringName(tr("Type:"));
895   m_strokeSelectionType.setItemUIName(RECT_SELECTION, tr("Rectangular"));
896   m_strokeSelectionType.setItemUIName(FREEHAND_SELECTION, tr("Freehand"));
897   m_strokeSelectionType.setItemUIName(POLYLINE_SELECTION, tr("Polyline"));
898 }
899 //-----------------------------------------------------------------------------
900 
updateAction(TPointD pos,const TMouseEvent & e)901 void SelectionTool::updateAction(TPointD pos, const TMouseEvent &e) {
902   TImageP image    = getImage(false);
903   TToonzImageP ti  = image;
904   TRasterImageP ri = image;
905   TVectorImageP vi = image;
906 
907   m_what     = Outside;
908   m_cursorId = ToolCursor::StrokeSelectCursor;
909 
910   if (!ti && !vi && !ri) return;
911 
912   bool shift = e.isShiftPressed();
913 
914   if (shift && !m_leftButtonMousePressed && isModifiableSelectionType()) {
915     m_what     = ADD_SELECTION;
916     m_cursorId = ToolCursor::SplineEditorCursorAdd;
917   } else if (m_leftButtonMousePressed)
918     return;
919 
920   if (!isSelectionEditable()) return;
921 
922   FourPoints bbox = getBBox();
923 
924   double pixelSize = getPixelSize();
925   if (!bbox.isEmpty()) {
926     double maxDist  = 17 * pixelSize;
927     double maxDist2 = maxDist * maxDist;
928     double p        = (15 * pixelSize);
929     m_selectedPoint = NONE;
930     if (tdistance2(bbox.getP00(), pos) < maxDist2 + p)
931       m_selectedPoint = P00;
932     else if (tdistance2(bbox.getP11(), pos) < maxDist2 + p)
933       m_selectedPoint = P11;
934     else if (tdistance2(bbox.getP01(), pos) < maxDist2 + p)
935       m_selectedPoint = P01;
936     else if (tdistance2(bbox.getP10(), pos) < maxDist2 + p)
937       m_selectedPoint = P10;
938 
939     if (tdistance2(bbox.getBottomLeft() + TPointD(-p, -p), pos) < maxDist2) {
940       m_what     = ROTATION;
941       m_cursorId = ToolCursor::RotBottomLeft;
942       return;
943     } else if (tdistance2(bbox.getBottomRight() + TPointD(p, -p), pos) <
944                maxDist2) {
945       m_what     = ROTATION;
946       m_cursorId = ToolCursor::RotBottomRight;
947       return;
948     } else if (tdistance2(bbox.getTopRight() + TPointD(p, p), pos) < maxDist2) {
949       m_what     = ROTATION;
950       m_cursorId = ToolCursor::RotCursor;
951       return;
952     } else if (tdistance2(bbox.getTopLeft() + TPointD(-p, p), pos) < maxDist2) {
953       m_what     = ROTATION;
954       m_cursorId = ToolCursor::RotTopLeft;
955       return;
956     }
957     maxDist  = 5 * pixelSize;
958     maxDist2 = maxDist * maxDist;
959     if (!isLevelType() && !isSelectedFramesType() &&
960         tdistance2(getCenter(), pos) < maxDist2) {
961       m_what     = MOVE_CENTER;
962       m_cursorId = ToolCursor::PointingHandCursor;
963       return;
964     }
965     if (tdistance2(bbox.getP00(), pos) < maxDist2 ||
966         tdistance2(bbox.getP11(), pos) < maxDist2 ||
967         tdistance2(bbox.getP01(), pos) < maxDist2 ||
968         tdistance2(bbox.getP10(), pos) < maxDist2) {
969       if (!e.isCtrlPressed() || isLevelType() || isSelectedFramesType()) {
970         m_what = SCALE;
971         if (tdistance2(bbox.getTopRight(), pos) < maxDist2 ||
972             tdistance2(bbox.getBottomLeft(), pos) < maxDist2)
973           m_cursorId = ToolCursor::ScaleCursor;
974         else
975           m_cursorId = ToolCursor::ScaleInvCursor;
976       } else {
977         m_cursorId = ToolCursor::DistortCursor;
978         m_what     = DEFORM;
979       }
980       return;
981     }
982     if (isCloseToSegment(pos, TSegment(bbox.getPoint(0), bbox.getPoint(3)),
983                          maxDist))
984       m_selectedPoint = P0M;
985     else if (isCloseToSegment(pos, TSegment(bbox.getPoint(1), bbox.getPoint(2)),
986                               maxDist))
987       m_selectedPoint = P1M;
988     else if (isCloseToSegment(pos, TSegment(bbox.getPoint(3), bbox.getPoint(2)),
989                               maxDist))
990       m_selectedPoint = PM1;
991     else if (isCloseToSegment(pos, TSegment(bbox.getPoint(0), bbox.getPoint(1)),
992                               maxDist))
993       m_selectedPoint = PM0;
994     if (m_selectedPoint == P0M || m_selectedPoint == P1M) {
995       if (!e.isCtrlPressed() || isLevelType() || isSelectedFramesType()) {
996         m_cursorId = ToolCursor::ScaleHCursor;
997         m_what     = SCALE_X;
998       } else {
999         m_cursorId = ToolCursor::DistortCursor;
1000         m_what     = DEFORM;
1001       }
1002       return;
1003     }
1004     if (m_selectedPoint == PM1 || m_selectedPoint == PM0) {
1005       if (!e.isCtrlPressed() || isLevelType() || isSelectedFramesType()) {
1006         m_cursorId = ToolCursor::ScaleVCursor;
1007         m_what     = SCALE_Y;
1008       } else {
1009         m_cursorId = ToolCursor::DistortCursor;
1010         m_what     = DEFORM;
1011       }
1012       return;
1013     }
1014     TPointD hpos = bbox.getP10() - TPointD(14 * pixelSize, 15 * pixelSize);
1015     TRectD rect(hpos - TPointD(14 * pixelSize, 5 * pixelSize),
1016                 hpos + TPointD(14 * pixelSize, 5 * pixelSize));
1017     if (!m_deformValues.m_isSelectionModified && rect.contains(pos) && vi &&
1018         !TTool::getApplication()->getCurrentObject()->isSpline()) {
1019       m_what     = GLOBAL_THICKNESS;
1020       m_cursorId = ToolCursor::PumpCursor;
1021       return;
1022     }
1023   }
1024   m_selectedPoint = NONE;
1025   if ((isLevelType() || isSelectedFramesType()) && !isSameStyleType()) {
1026     m_what = Inside;
1027     m_cursorId = ToolCursor::LevelSelectCursor;
1028   }
1029 
1030   if (shift) return;
1031   if (!vi && bbox.contains(pos)) {
1032     m_what = Inside;
1033     if (isLevelType() || isSelectedFramesType())
1034       m_cursorId = ToolCursor::LevelSelectCursor;
1035     else
1036       m_cursorId = ToolCursor::MoveCursor;
1037   }
1038 }
1039 
1040 //-----------------------------------------------------------------------------
1041 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)1042 void SelectionTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
1043   TImageP image = getImage(false);
1044   if (!image) return;
1045   if (m_polyline.size() == 0) {
1046     modifySelectionOnClick(image, pos, e);
1047 
1048     if (m_what == ROTATION) m_dragTool = createNewRotationTool(this);
1049     if (!e.isShiftPressed() && m_what == Inside)
1050       m_dragTool = createNewMoveSelectionTool(this);
1051     else if (m_what == MOVE_CENTER)
1052       m_dragTool = new MoveCenterTool(this);
1053     else if (m_what == SCALE)
1054       m_dragTool = createNewScaleTool(this, ScaleType::GLOBAL);
1055     else if (m_what == SCALE_X)
1056       m_dragTool = createNewScaleTool(this, ScaleType::HORIZONTAL);
1057     else if (m_what == SCALE_Y)
1058       m_dragTool = createNewScaleTool(this, ScaleType::VERTICAL);
1059     else if (m_what == DEFORM)
1060       m_dragTool = createNewFreeDeformTool(this);
1061     else if (m_what == GLOBAL_THICKNESS)
1062       m_dragTool = new VectorChangeThicknessTool((VectorSelectionTool *)this);
1063     if (m_dragTool) m_dragTool->leftButtonDown(pos, e);
1064   } else
1065     m_selecting = true;
1066   if (m_selecting) {
1067     if (m_stroke) {
1068       delete m_stroke;
1069       m_stroke = 0;
1070     }
1071     if (m_strokeSelectionType.getValue() == FREEHAND_SELECTION)
1072       startFreehand(pos);
1073     if (m_strokeSelectionType.getValue() == POLYLINE_SELECTION)
1074       addPointPolyline(pos);
1075     else if (m_polyline.size() != 0)
1076       m_polyline.clear();
1077   }
1078   m_firstPos = m_curPos    = pos;
1079   m_leftButtonMousePressed = true;
1080   m_shiftPressed           = e.isShiftPressed();
1081 }
1082 
1083 //-----------------------------------------------------------------------------
1084 
mouseMove(const TPointD & pos,const TMouseEvent & e)1085 void SelectionTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
1086   updateAction(pos, e);
1087 
1088   if (m_strokeSelectionType.getValue() == POLYLINE_SELECTION) {
1089     m_mousePosition = pos;
1090     invalidate();
1091   }
1092 }
1093 
1094 //-----------------------------------------------------------------------------
1095 
keyDown(QKeyEvent * event)1096 bool SelectionTool::keyDown(QKeyEvent *event) {
1097   if (isSelectionEmpty()) return false;
1098 
1099   TPointD delta;
1100 
1101   switch (event->key()) {
1102   case Qt::Key_Up:
1103     delta.y = 1;
1104     break;
1105   case Qt::Key_Down:
1106     delta.y = -1;
1107     break;
1108   case Qt::Key_Left:
1109     delta.x = -1;
1110     break;
1111   case Qt::Key_Right:
1112     delta.x = 1;
1113     break;
1114   default:
1115     return false;
1116     break;
1117   }
1118 
1119   if (event->modifiers() & Qt::ShiftModifier) {
1120     delta.x *= 10.0;
1121     delta.y *= 10.0;
1122   } else if (event->modifiers() & Qt::ControlModifier) {
1123     delta.x *= 0.1;
1124     delta.y *= 0.1;
1125   }
1126 
1127   TImageP image = getImage(true);
1128 
1129   TToonzImageP ti  = (TToonzImageP)image;
1130   TRasterImageP ri = (TRasterImageP)image;
1131   TVectorImageP vi = (TVectorImageP)image;
1132 
1133   if (!ti && !vi && !ri) return false;
1134 
1135   std::unique_ptr<DragTool> dragTool(createNewMoveSelectionTool(this));
1136   TAffine aff        = TTranslation(delta);
1137   dragTool->transform(aff);
1138   double factor = 1.0 / Stage::inch;
1139   m_deformValues.m_moveValue += factor * delta;
1140   dragTool->addTransformUndo();
1141   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
1142 
1143   invalidate();
1144   return true;
1145 }
1146 
1147 //-----------------------------------------------------------------------------
1148 
getCursorId() const1149 int SelectionTool::getCursorId() const {
1150   TImageP image    = getImage(false);
1151   TToonzImageP ti  = (TToonzImageP)image;
1152   TRasterImageP ri = (TRasterImageP)image;
1153   TVectorImageP vi = (TVectorImageP)image;
1154 
1155   if (!ti && !vi && !ri) return ToolCursor::StrokeSelectCursor;
1156 
1157   return m_cursorId;
1158 }
1159 
1160 //-----------------------------------------------------------------------------
1161 
drawPolylineSelection()1162 void SelectionTool::drawPolylineSelection() {
1163   if (m_polyline.empty()) return;
1164   TPixel color = ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg
1165                      ? TPixel32::White
1166                      : TPixel32::Black;
1167   tglColor(color);
1168   tglDrawCircle(m_polyline[0], 2);
1169   glBegin(GL_LINE_STRIP);
1170   for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]);
1171   tglVertex(m_mousePosition);
1172   glEnd();
1173 }
1174 
1175 //-----------------------------------------------------------------------------
1176 
drawFreehandSelection()1177 void SelectionTool::drawFreehandSelection() {
1178   if (m_track.isEmpty()) return;
1179   TPixel color = ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg
1180                      ? TPixel32::White
1181                      : TPixel32::Black;
1182   tglColor(color);
1183   m_track.drawAllFragments();
1184 }
1185 
1186 //-----------------------------------------------------------------------------
1187 
drawRectSelection(const TImage * image)1188 void SelectionTool::drawRectSelection(const TImage *image) {
1189   const TVectorImage *vi   = dynamic_cast<const TVectorImage *>(image);
1190   unsigned short stipple   = 0x3F33;
1191   FourPoints selectingRect = m_selectingRect;
1192   if (vi && m_curPos.x >= m_firstPos.x) stipple = 0xFF00;
1193   drawFourPoints(selectingRect, TPixel32::Black, stipple, true);
1194 }
1195 
1196 //-----------------------------------------------------------------------------
1197 
drawCommandHandle(const TImage * image)1198 void SelectionTool::drawCommandHandle(const TImage *image) {
1199   const TVectorImage *vi = dynamic_cast<const TVectorImage *>(image);
1200   TPixel32 frameColor(210, 210, 210);
1201   TPixel32 frameColor2(0, 0, 0);
1202   FourPoints rect = getBBox();
1203 
1204   drawFourPoints(rect, frameColor, 0xffff, true);
1205 
1206   tglColor(frameColor);
1207 
1208   if (m_dragTool) m_dragTool->draw();
1209 
1210   if (!isSelectionEditable()) return;
1211 
1212   double pixelSize = getPixelSize();
1213   if (!isLevelType() && !isSelectedFramesType()) {
1214     TPointD c = getCenter() + TPointD(-pixelSize, +pixelSize);
1215 
1216     tglColor(frameColor);
1217     tglDrawCircle(c, pixelSize * 5);
1218     tglDrawSegment(c - TPointD(pixelSize * 15, 0),
1219                    c + TPointD(pixelSize * 15, 0));
1220     tglDrawSegment(c - TPointD(0, pixelSize * 15),
1221                    c + TPointD(0, pixelSize * 15));
1222     tglColor(frameColor2);
1223     tglDrawCircle(getCenter(), pixelSize * 5);
1224     tglDrawSegment(getCenter() - TPointD(pixelSize * 15, 0),
1225                    getCenter() + TPointD(pixelSize * 15, 0));
1226     tglDrawSegment(getCenter() - TPointD(0, pixelSize * 15),
1227                    getCenter() + TPointD(0, pixelSize * 15));
1228   }
1229 
1230   TPointD bl(rect.getP00().x - pixelSize, rect.getP00().y + pixelSize);
1231   TPointD tl(rect.getP01().x - pixelSize, rect.getP01().y + pixelSize);
1232   TPointD br(rect.getP10().x - pixelSize, rect.getP10().y + pixelSize);
1233   TPointD tr(rect.getP11().x - pixelSize, rect.getP11().y + pixelSize);
1234 
1235   drawSquare(bl, pixelSize * 4, frameColor);
1236   drawSquare(tl, pixelSize * 4, frameColor);
1237   drawSquare(br, pixelSize * 4, frameColor);
1238   drawSquare(tr, pixelSize * 4, frameColor);
1239 
1240   drawSquare(rect.getP00(), pixelSize * 4, frameColor2);
1241   drawSquare(rect.getP01(), pixelSize * 4, frameColor2);
1242   drawSquare(rect.getP10(), pixelSize * 4, frameColor2);
1243   drawSquare(rect.getP11(), pixelSize * 4, frameColor2);
1244 
1245   if (vi && !m_deformValues.m_isSelectionModified) {
1246     TPointD thickCommandPos =
1247         rect.getP10() - TPointD(14 * pixelSize, 15 * pixelSize);
1248     drawRectWhitArrow(thickCommandPos, pixelSize);
1249   }
1250 
1251   drawSquare(0.5 * (br + tr), pixelSize * 4, frameColor);
1252   drawSquare(0.5 * (tl + tr), pixelSize * 4, frameColor);
1253   drawSquare(0.5 * (br + bl), pixelSize * 4, frameColor);
1254   drawSquare(0.5 * (tl + bl), pixelSize * 4, frameColor);
1255 
1256   drawSquare(0.5 * (rect.getP10() + rect.getP11()), pixelSize * 4, frameColor2);
1257   drawSquare(0.5 * (rect.getP01() + rect.getP11()), pixelSize * 4, frameColor2);
1258   drawSquare(0.5 * (rect.getP10() + rect.getP00()), pixelSize * 4, frameColor2);
1259   drawSquare(0.5 * (rect.getP01() + rect.getP00()), pixelSize * 4, frameColor2);
1260 }
1261 
1262 //-----------------------------------------------------------------------------
1263 
onActivate()1264 void SelectionTool::onActivate() {
1265   if (m_firstTime) {
1266     m_strokeSelectionType.setValue(::to_wstring(SelectionType.getValue()));
1267     m_firstTime = false;
1268   }
1269   if (isLevelType() || isSelectedFramesType()) return;
1270 
1271   doOnActivate();
1272 }
1273 
1274 //-----------------------------------------------------------------------------
1275 
onDeactivate()1276 void SelectionTool::onDeactivate() {
1277   if (isLevelType() || isSelectedFramesType()) return;
1278 
1279   doOnDeactivate();
1280 }
1281 
1282 //-----------------------------------------------------------------------------
1283 
onSelectionChanged()1284 void SelectionTool::onSelectionChanged() {
1285   computeBBox();
1286   invalidate();
1287   m_polyline.clear();
1288 }
1289 
1290 //-----------------------------------------------------------------------------
1291 
onPropertyChanged(std::string propertyName)1292 bool SelectionTool::onPropertyChanged(std::string propertyName) {
1293   if (propertyName == m_strokeSelectionType.getName()) {
1294     SelectionType = ::to_string(m_strokeSelectionType.getValue());
1295     return true;
1296   }
1297   return false;
1298 }
1299 
1300 //-----------------------------------------------------------------------------
1301 
1302 //! Viene aggiunto \b pos a \b m_track e disegnato il primo pezzetto del lazzo.
1303 //! Viene inizializzato \b m_firstPos
startFreehand(const TPointD & pos)1304 void SelectionTool::startFreehand(const TPointD &pos) {
1305   m_track.clear();
1306   m_firstPos       = pos;
1307   double pixelSize = getPixelSize();
1308   m_track.add(TThickPoint(pos, 0), pixelSize * pixelSize);
1309 }
1310 
1311 //-----------------------------------------------------------------------------
1312 
1313 //! Viene aggiunto \b pos a \b m_track e disegnato un altro pezzetto del lazzo.
freehandDrag(const TPointD & pos)1314 void SelectionTool::freehandDrag(const TPointD &pos) {
1315   double pixelSize = getPixelSize();
1316   m_track.add(TThickPoint(pos, 0), pixelSize * pixelSize);
1317 }
1318 
1319 //-----------------------------------------------------------------------------
1320 
1321 //! Viene chiuso il lazzo (si aggiunge l'ultimo punto ad m_track) e viene creato
1322 //! lo stroke rappresentante il lazzo.
closeFreehand(const TPointD & pos)1323 void SelectionTool::closeFreehand(const TPointD &pos) {
1324   if (m_track.isEmpty()) return;
1325   double pixelSize = getPixelSize();
1326   m_track.add(TThickPoint(m_firstPos, 0), pixelSize * pixelSize);
1327   m_track.filterPoints();
1328   double error = (30.0 / 11) * pixelSize;
1329   m_stroke     = m_track.makeStroke(error);
1330   m_stroke->setStyle(1);
1331 }
1332 
1333 //-----------------------------------------------------------------------------
1334 
1335 //! Viene aggiunto un punto al vettore m_polyline.
addPointPolyline(const TPointD & pos)1336 void SelectionTool::addPointPolyline(const TPointD &pos) {
1337   m_firstPos      = pos;
1338   m_mousePosition = pos;
1339   m_polyline.push_back(pos);
1340 }
1341 
1342 //-----------------------------------------------------------------------------
1343 
1344 //! Agginge l'ultimo pos a \b m_polyline e chiude la spezzata (aggiunge \b
1345 //! m_polyline.front() alla fine del vettore).
closePolyline(const TPointD & pos)1346 void SelectionTool::closePolyline(const TPointD &pos) {
1347   if (m_polyline.size() <= 1) return;
1348   if (m_polyline.back() != pos) m_polyline.push_back(pos);
1349   if (m_polyline.back() != m_polyline.front())
1350     m_polyline.push_back(m_polyline.front());
1351 
1352   std::vector<TThickPoint> strokePoints;
1353   for (UINT i = 0; i < m_polyline.size() - 1; i++) {
1354     strokePoints.push_back(TThickPoint(m_polyline[i], 0));
1355     strokePoints.push_back(
1356         TThickPoint(0.5 * (m_polyline[i] + m_polyline[i + 1]), 0));
1357   }
1358   strokePoints.push_back(TThickPoint(m_polyline.back(), 0));
1359   m_polyline.clear();
1360   m_stroke = new TStroke(strokePoints);
1361   assert(m_stroke->getPoint(0) == m_stroke->getPoint(1));
1362   invalidate();
1363 }
1364 
1365 //-----------------------------------------------------------------------------
1366 
1367 // returns true if the pressed key is recognized and processed in the tool
1368 // instead of triggering the shortcut command.
isEventAcceptable(QEvent * e)1369 bool SelectionTool::isEventAcceptable(QEvent *e) {
1370   if (!isEnabled()) return false;
1371   if (isSelectionEmpty()) return false;
1372   // arrow keys will be used for moving the selected region
1373   QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
1374   int key             = keyEvent->key();
1375   return (key == Qt::Key_Up || key == Qt::Key_Down || key == Qt::Key_Left ||
1376           key == Qt::Key_Right);
1377 }
1378