1 
2 
3 #include "vectorselectiontool.h"
4 
5 // TnzTools includes
6 #include "tools/toolhandle.h"
7 #include "tools/imagegrouping.h"
8 #include "tools/cursors.h"
9 
10 // TnzQt includes
11 #include "toonzqt/selectioncommandids.h"
12 #include "toonzqt/tselectionhandle.h"
13 #include "toonzqt/imageutils.h"
14 
15 // TnzLib includes
16 #include "toonz/txsheethandle.h"
17 #include "toonz/txshlevelhandle.h"
18 #include "toonz/tobjecthandle.h"
19 #include "toonz/tstageobject.h"
20 
21 // TnzBase includes
22 #include "tenv.h"
23 
24 // TnzCore includes
25 #include "drawutil.h"
26 
27 // boost includes
28 #include <boost/bind.hpp>
29 
30 using namespace ToolUtils;
31 using namespace DragSelectionTool;
32 
33 //********************************************************************************
34 //    Global variables
35 //********************************************************************************
36 
37 namespace {
38 
39 VectorSelectionTool l_vectorSelectionTool(TTool::Vectors);
40 TEnv::IntVar l_strokeSelectConstantThickness("SelectionToolConstantThickness",
41                                              0);
42 TEnv::IntVar l_strokeSelectIncludeIntersection(
43     "SelectionToolIncludeIntersection", 0);
44 
45 const int l_dragThreshold = 10;  //!< Distance, in pixels, the user has to
46                                  //!  move from a button press to trigger a
47                                  //!  selection drag.
48 }  // namespace
49 
50 //********************************************************************************
51 //    Local namespace  stuff
52 //********************************************************************************
53 
54 namespace {
55 
getFourPointsFromVectorImage(const TVectorImageP & img,const std::set<int> & styleIds,double & maxThickness)56 FourPoints getFourPointsFromVectorImage(const TVectorImageP &img,
57                                         const std::set<int> &styleIds,
58                                         double &maxThickness) {
59   FourPoints p;
60 
61   if (styleIds.empty()) {
62     p = img->getBBox();
63 
64     for (UINT i = 0; i < img->getStrokeCount(); i++) {
65       TStroke *s = img->getStroke(i);
66 
67       for (int j = 0; j < s->getControlPointCount(); j++) {
68         double thick = s->getControlPoint(j).thick;
69         if (maxThickness < thick) maxThickness = thick;
70       }
71     }
72   } else {
73     TRectD bbox;
74 
75     for (UINT i = 0; i < img->getStrokeCount(); i++) {
76       TStroke *s = img->getStroke(i);
77       if (!styleIds.count(s->getStyle())) continue;
78 
79       if (bbox.isEmpty())
80         bbox = s->getBBox();
81       else
82         bbox += s->getBBox();
83 
84       for (int j = 0; j < s->getControlPointCount(); j++) {
85         double thick = s->getControlPoint(j).thick;
86         if (maxThickness < thick) maxThickness = thick;
87       }
88     }
89 
90     p = bbox;
91   }
92 
93   return p;
94 }
95 
96 //-----------------------------------------------------------------------------
97 
getStrokeIndexFromPos(UINT & index,const TVectorImageP & vi,const TPointD & pos,double pixelSize,TAffine aff)98 bool getStrokeIndexFromPos(UINT &index, const TVectorImageP &vi,
99                            const TPointD &pos, double pixelSize, TAffine aff) {
100   if (!vi) return false;
101   double t, dist2 = 0;
102   double maxDist   = 5 * pixelSize;
103   double maxDist2  = maxDist * maxDist;
104   double checkDist = maxDist2 * 4;
105 
106   if (vi->getNearestStroke(pos, t, index, dist2)) {
107     TStroke *strokeRef = vi->getStroke(index);
108     TThickPoint cursor = strokeRef->getThickPoint(t);
109     double len         = cursor.thick * pixelSize * sqrt(aff.det());
110     checkDist          = std::max(checkDist, (len * len));
111   }
112 
113   return (dist2 < checkDist);
114 }
115 
116 //-----------------------------------------------------------------------------
117 
currentOrNotSelected(const VectorSelectionTool & tool,const TFrameId & fid)118 static bool currentOrNotSelected(const VectorSelectionTool &tool,
119                                  const TFrameId &fid) {
120   return (tool.getCurrentFid() == fid ||
121           (tool.isSelectedFramesType() &&
122            tool.getSelectedFrames().count(fid) == 0));
123 }
124 
125 //-----------------------------------------------------------------------------
126 
notifySelectionChanged()127 inline void notifySelectionChanged() {
128   TTool::getApplication()->getCurrentSelection()->notifySelectionChanged();
129 }
130 
131 }  // namespace
132 
133 //********************************************************************************
134 //    VectorFreeDeformer  implementation
135 //********************************************************************************
136 
VectorFreeDeformer(TVectorImageP vi,std::set<int> strokeIndexes)137 VectorFreeDeformer::VectorFreeDeformer(TVectorImageP vi,
138                                        std::set<int> strokeIndexes)
139     : FreeDeformer()
140     , m_vi(vi)
141     , m_strokeIndexes(strokeIndexes)
142     , m_preserveThickness(false)
143     , m_computeRegion(false)
144     , m_flip(false) {
145   TRectD r;
146 
147   std::set<int>::iterator it, iEnd = m_strokeIndexes.end();
148   for (it = m_strokeIndexes.begin(); it != iEnd; ++it) {
149     TStroke *stroke = m_vi->getStroke(*it);
150     r += stroke->getBBox();
151     m_originalStrokes.push_back(new TStroke(*stroke));
152   }
153 
154   m_originalP00 = r.getP00();
155   m_originalP11 = r.getP11();
156   m_newPoints.push_back(m_originalP00);
157   m_newPoints.push_back(r.getP10());
158   m_newPoints.push_back(m_originalP11);
159   m_newPoints.push_back(r.getP01());
160 }
161 
162 //-----------------------------------------------------------------------------
163 
~VectorFreeDeformer()164 VectorFreeDeformer::~VectorFreeDeformer() {
165   clearPointerContainer(m_originalStrokes);
166 }
167 
168 //-----------------------------------------------------------------------------
169 
setPreserveThickness(bool preserveThickness)170 void VectorFreeDeformer::setPreserveThickness(bool preserveThickness) {
171   m_preserveThickness = preserveThickness;
172 }
173 
174 //-----------------------------------------------------------------------------
175 
setComputeRegion(bool computeRegion)176 void VectorFreeDeformer::setComputeRegion(bool computeRegion) {
177   m_computeRegion = computeRegion;
178 }
179 
180 //-----------------------------------------------------------------------------
181 
setFlip(bool flip)182 void VectorFreeDeformer::setFlip(bool flip) { m_flip = flip; }
183 
184 //-----------------------------------------------------------------------------
185 
setPoint(int index,const TPointD & p)186 void VectorFreeDeformer::setPoint(int index, const TPointD &p) {
187   m_newPoints[index] = p;
188 }
189 
190 //-----------------------------------------------------------------------------
191 
setPoints(const TPointD & p0,const TPointD & p1,const TPointD & p2,const TPointD & p3)192 void VectorFreeDeformer::setPoints(const TPointD &p0, const TPointD &p1,
193                                    const TPointD &p2, const TPointD &p3) {
194   m_newPoints[0] = p0;
195   m_newPoints[1] = p1;
196   m_newPoints[2] = p2;
197   m_newPoints[3] = p3;
198 }
199 
200 //-----------------------------------------------------------------------------
201 
deformRegions()202 void VectorFreeDeformer::deformRegions() {
203   if (m_strokeIndexes.empty() || !m_computeRegion) return;
204 
205   std::vector<int> selectedIndexes(m_strokeIndexes.begin(),
206                                    m_strokeIndexes.end());
207 
208   m_vi->notifyChangedStrokes(selectedIndexes, m_originalStrokes, m_flip);
209   m_computeRegion = false;
210 }
211 
212 //-----------------------------------------------------------------------------
213 
deformImage()214 void VectorFreeDeformer::deformImage() {
215   // debug
216   assert(m_strokeIndexes.size() == m_originalStrokes.size());
217 
218   // release
219   if (m_strokeIndexes.size() != m_originalStrokes.size()) {
220     return;
221   }
222 
223   QMutexLocker lock(m_vi->getMutex());
224 
225   std::size_t i = 0;
226   for (auto it = m_strokeIndexes.begin(), end = m_strokeIndexes.end();
227        it != end; ++it) {
228     TStroke *stroke         = m_vi->getStroke(*it);
229     TStroke *originalStroke = m_originalStrokes[i++];
230 
231     assert(stroke->getControlPointCount() ==
232            originalStroke->getControlPointCount());
233     for (int j = 0, count = stroke->getControlPointCount(); j < count; ++j) {
234       TThickPoint p = deform(originalStroke->getControlPoint(j));
235       stroke->setControlPoint(j, p);
236     }
237   }
238 
239   if (m_computeRegion) deformRegions();
240 }
241 
242 //-----------------------------------------------------------------------------
243 
deform(TThickPoint point)244 TThickPoint VectorFreeDeformer::deform(TThickPoint point) {
245   double vs  = m_originalP11.x - m_originalP00.x;
246   double s   = (vs == 0) ? 0 : (point.x - m_originalP00.x) / vs;
247   double vt  = m_originalP11.y - m_originalP00.y;
248   double t   = (vt == 0) ? 0 : (point.y - m_originalP00.y) / vt;
249   TPointD A  = m_newPoints[0];
250   TPointD B  = m_newPoints[1];
251   TPointD C  = m_newPoints[2];
252   TPointD D  = m_newPoints[3];
253   TPointD AD = (1 - t) * A + t * D;
254   TPointD BC = (1 - t) * B + t * C;
255   TPointD p  = (1 - s) * AD + s * BC;
256 
257   double thickness = point.thick;
258   if (!m_preserveThickness) {
259     double eps          = 1.e-2;
260     TPointD p0x         = TPointD(p.x - eps, p.x);
261     TPointD p1x         = TPointD(p.x + eps, p.x);
262     TPointD p0y         = TPointD(p.x, p.y - eps);
263     TPointD p1y         = TPointD(p.x, p.y + eps);
264     m_preserveThickness = true;
265     TThickPoint newp0x  = deform(p0x);
266     TThickPoint newp1x  = deform(p1x);
267     TThickPoint newp0y  = deform(p0y);
268     TThickPoint newp1y  = deform(p1y);
269     m_preserveThickness = false;
270     double newA         = fabs(cross(newp1x - newp0x, newp1y - newp0y));
271     double a            = 4 * eps * eps;
272     thickness *= sqrt(newA / a);
273   }
274   return TThickPoint(p, thickness);
275 }
276 
277 //********************************************************************************
278 //    UndoChangeStrokes  implementation
279 //********************************************************************************
280 
UndoChangeStrokes(TXshSimpleLevel * level,const TFrameId & frameId,VectorSelectionTool * tool,const StrokeSelection & selection)281 DragSelectionTool::UndoChangeStrokes::UndoChangeStrokes(
282     TXshSimpleLevel *level, const TFrameId &frameId, VectorSelectionTool *tool,
283     const StrokeSelection &selection)
284     : ToolUtils::TToolUndo(level, frameId)
285     , m_tool(tool)
286     , m_selectionCount(tool->getSelectionCount())  // Not related to selection
287     , m_oldBBox(tool->getBBox())
288     , m_oldCenter(tool->getCenter())
289     , m_oldDeformValues(tool->m_deformValues)
290     , m_newDeformValues()
291     , m_flip(false) {
292   TVectorImageP vi = m_level->getFrame(m_frameId, false);
293   if (!vi) {
294     assert(vi);
295     return;
296   }
297 
298   const StrokeSelection::IndexesContainer &indexes = selection.getSelection();
299   m_indexes.assign(indexes.begin(), indexes.end());
300 
301   registerStrokes(true);
302 }
303 
304 //-----------------------------------------------------------------------------
305 
UndoChangeStrokes(TXshSimpleLevel * level,const TFrameId & frameId,VectorSelectionTool * tool,const LevelSelection & selection)306 DragSelectionTool::UndoChangeStrokes::UndoChangeStrokes(
307     TXshSimpleLevel *level, const TFrameId &frameId, VectorSelectionTool *tool,
308     const LevelSelection &selection)
309     : ToolUtils::TToolUndo(level, frameId)
310     , m_tool(tool)
311     , m_selectionCount(tool->getSelectionCount())  // Not related to selection
312     , m_oldBBox(tool->getBBox())
313     , m_oldCenter(tool->getCenter())
314     , m_oldDeformValues(tool->m_deformValues)
315     , m_newDeformValues()
316     , m_flip(false) {
317   TVectorImageP vi = m_level->getFrame(m_frameId, false);
318   if (!vi) {
319     assert(vi);
320     return;
321   }
322 
323   m_indexes = getSelectedStrokes(*vi, selection);
324 
325   registerStrokes(true);
326 }
327 
328 //-----------------------------------------------------------------------------
329 
~UndoChangeStrokes()330 DragSelectionTool::UndoChangeStrokes::~UndoChangeStrokes() {
331   clearPointerContainer(m_oldStrokes);
332   clearPointerContainer(m_newStrokes);
333 }
334 
335 //-----------------------------------------------------------------------------
336 
registerStrokes(bool beforeModify)337 void DragSelectionTool::UndoChangeStrokes::registerStrokes(bool beforeModify) {
338   TVectorImageP vi = m_level->getFrame(m_frameId, false);
339   if (!vi) {
340     assert(vi);
341     return;
342   }
343 
344   std::vector<TStroke *> &strokes = beforeModify ? m_oldStrokes : m_newStrokes;
345 
346   TRectD bbox;
347 
348   int s, sCount = int(m_indexes.size());
349   for (s = 0; s != sCount; ++s) {
350     TStroke *stroke = vi->getStroke(m_indexes[s]);
351     bbox += stroke->getBBox();
352 
353     strokes.push_back(new TStroke(*stroke));
354   }
355 
356   if (beforeModify && !bbox.isEmpty()) {
357     ImageUtils::getFillingInformationOverlappingArea(vi, m_regionsData, bbox);
358   } else {
359     m_newBBox         = m_tool->getBBox();
360     m_newCenter       = m_tool->getCenter();
361     m_newDeformValues = m_tool->m_deformValues;
362   }
363 }
364 
365 //-----------------------------------------------------------------------------
366 
transform(const std::vector<TStroke * > & strokes,FourPoints bbox,TPointD center,DragSelectionTool::DeformValues deformValues) const367 void DragSelectionTool::UndoChangeStrokes::transform(
368     const std::vector<TStroke *> &strokes, FourPoints bbox, TPointD center,
369     DragSelectionTool::DeformValues deformValues) const {
370   TVectorImageP image = m_level->getFrame(m_frameId, true);
371   if (!image) {
372     assert(image);
373     return;
374   }
375 
376   int s, sCount = int(m_indexes.size());
377   for (s = 0; s != sCount; ++s) {
378     int index = m_indexes[s];
379 
380     TStroke *sourcesStroke = strokes[s], *stroke = image->getStroke(index);
381 
382     int cp, cpCount = stroke->getControlPointCount();
383     for (cp = 0; cp != cpCount; ++cp)
384       stroke->setControlPoint(cp, sourcesStroke->getControlPoint(cp));
385   }
386 
387   image->notifyChangedStrokes(m_indexes, strokes, m_flip);
388 
389   if (!m_tool->isSelectionEmpty() &&
390       m_selectionCount == m_tool->getSelectionCount()) {
391     m_tool->setBBox(bbox);
392     m_tool->setCenter(center);
393   } else
394     m_tool->computeBBox();
395 
396   m_tool->notifyImageChanged(m_frameId);
397   m_tool->m_deformValues = deformValues;
398   TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
399   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
400 }
401 
402 //-----------------------------------------------------------------------------
403 
restoreRegions() const404 void DragSelectionTool::UndoChangeStrokes::restoreRegions() const {
405   TVectorImageP vi = m_level->getFrame(m_frameId, true);
406   if (!vi) {
407     assert(vi);
408     return;
409   }
410 
411   ImageUtils::assignFillingInformation(*vi, m_regionsData);
412 }
413 
414 //-----------------------------------------------------------------------------
415 
undo() const416 void DragSelectionTool::UndoChangeStrokes::undo() const {
417   transform(m_oldStrokes, m_oldBBox, m_oldCenter, m_oldDeformValues);
418   restoreRegions();
419 }
420 
421 //-----------------------------------------------------------------------------
422 
redo() const423 void DragSelectionTool::UndoChangeStrokes::redo() const {
424   transform(m_newStrokes, m_newBBox, m_newCenter, m_newDeformValues);
425 }
426 
427 //-----------------------------------------------------------------------------
428 
getSize() const429 int DragSelectionTool::UndoChangeStrokes::getSize() const {
430   return sizeof(*this) + sizeof(*m_tool);
431 }
432 
433 //=============================================================================
434 // UndoChangeOutlineStyle
435 //-----------------------------------------------------------------------------
436 
437 class UndoChangeOutlineStyle final : public ToolUtils::TToolUndo {
438   std::vector<TStroke::OutlineOptions> m_oldOptions, m_newOptions;
439   FourPoints m_oldBBox, m_newBBox;
440   VectorSelectionTool *m_tool;
441   std::vector<int> m_indexes;
442   int m_selectionCount;
443 
444 public:
445   UndoChangeOutlineStyle(TXshSimpleLevel *level, const TFrameId &frameId,
446                          VectorSelectionTool *tool);
~UndoChangeOutlineStyle()447   ~UndoChangeOutlineStyle() {}
448   void registerStrokes(bool beforeModify = false);
449   void transform(const std::vector<TStroke::OutlineOptions> &options,
450                  FourPoints bbox) const;
451   void undo() const override;
452   void redo() const override;
453   int getSize() const override;
454 };
455 
456 //-----------------------------------------------------------------------------
457 
UndoChangeOutlineStyle(TXshSimpleLevel * level,const TFrameId & frameId,VectorSelectionTool * tool)458 UndoChangeOutlineStyle::UndoChangeOutlineStyle(TXshSimpleLevel *level,
459                                                const TFrameId &frameId,
460                                                VectorSelectionTool *tool)
461     : ToolUtils::TToolUndo(level, frameId)
462     , m_tool(tool)
463     , m_oldBBox(tool->getBBox())
464     , m_selectionCount(tool->getSelectionCount()) {
465   TVectorImageP image = m_level->getFrame(m_frameId, false);
466   assert(!!image);
467   if (!image) return;
468   StrokeSelection *strokeSelection =
469       dynamic_cast<StrokeSelection *>(tool->getSelection());
470   int i;
471   for (i = 0; i < (int)image->getStrokeCount(); i++) {
472     if (!strokeSelection->isSelected(i) && !m_tool->isLevelType() &&
473         !m_tool->isSelectedFramesType())
474       continue;
475     m_indexes.push_back(i);
476   }
477   registerStrokes(true);
478 }
479 
480 //-----------------------------------------------------------------------------
481 
registerStrokes(bool beforeModify)482 void UndoChangeOutlineStyle::registerStrokes(bool beforeModify) {
483   TVectorImageP image = m_level->getFrame(m_frameId, false);
484   assert(!!image);
485   if (!image) return;
486   int i;
487   for (i = 0; i < (int)m_indexes.size(); i++) {
488     if (beforeModify)
489       m_oldOptions.push_back(image->getStroke(m_indexes[i])->outlineOptions());
490     else
491       m_newOptions.push_back(image->getStroke(m_indexes[i])->outlineOptions());
492   }
493   if (!beforeModify) m_newBBox = m_tool->getBBox();
494 }
495 
496 //-----------------------------------------------------------------------------
497 
transform(const std::vector<TStroke::OutlineOptions> & options,FourPoints bbox) const498 void UndoChangeOutlineStyle::transform(
499     const std::vector<TStroke::OutlineOptions> &options,
500     FourPoints bbox) const {
501   TVectorImageP image = m_level->getFrame(m_frameId, true);
502   assert(!!image);
503   if (!image) return;
504   int i;
505   for (i = 0; i < (int)m_indexes.size(); i++) {
506     int index                                 = m_indexes[i];
507     image->getStroke(index)->outlineOptions() = options[i];
508   }
509   if (!m_tool->isSelectionEmpty() &&
510       m_selectionCount == m_tool->getSelectionCount())
511     m_tool->setBBox(bbox);
512   else
513     m_tool->computeBBox();
514   m_tool->notifyImageChanged(m_frameId);
515   TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
516   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
517 }
518 
519 //-----------------------------------------------------------------------------
520 
undo() const521 void UndoChangeOutlineStyle::undo() const {
522   transform(m_oldOptions, m_oldBBox);
523   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
524 }
525 
526 //-----------------------------------------------------------------------------
527 
redo() const528 void UndoChangeOutlineStyle::redo() const {
529   transform(m_newOptions, m_newBBox);
530   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
531 }
532 
533 //-----------------------------------------------------------------------------
534 
getSize() const535 int UndoChangeOutlineStyle::getSize() const {
536   // NOTE: This is definitely wrong... sizeof(vector) DOES NOT correspond to its
537   // actual size - as it is internally allocated with new!!
538   return sizeof(*this) + sizeof(*m_tool);
539 }
540 
541 //=============================================================================
542 // VectorDeformTool::VFDScopedBlock
543 //-----------------------------------------------------------------------------
544 
545 struct VectorDeformTool::VFDScopedBlock  //! Type bounding the scope of free
546                                          //! deformers in the tool.
547 {
VFDScopedBlockVectorDeformTool::VFDScopedBlock548   VFDScopedBlock(SelectionTool *tool) : m_tool(tool) {
549     m_tool->setNewFreeDeformer();
550   }
~VFDScopedBlockVectorDeformTool::VFDScopedBlock551   ~VFDScopedBlock() { m_tool->clearDeformers(); }
552 
553 private:
554   SelectionTool *m_tool;  //!< [\p external]  Tool owning the deformers.
555 };
556 
557 //=============================================================================
558 // VectorDeformTool
559 //-----------------------------------------------------------------------------
560 
VectorDeformTool(VectorSelectionTool * tool)561 DragSelectionTool::VectorDeformTool::VectorDeformTool(VectorSelectionTool *tool)
562     : DeformTool(tool), m_undo() {
563   if (!TTool::getApplication()->getCurrentObject()->isSpline()) {
564     m_undo.reset(new UndoChangeStrokes(
565         TTool::getApplication()->getCurrentLevel()->getSimpleLevel(),
566         tool->getCurrentFid(), tool, tool->strokeSelection()));
567   }
568 }
569 
570 //-----------------------------------------------------------------------------
571 
~VectorDeformTool()572 DragSelectionTool::VectorDeformTool::~VectorDeformTool() {
573   // DO NOT REMOVE - DESTRUCTS TYPES INCOMPLETE IN THE HEADER
574 }
575 
576 //-----------------------------------------------------------------------------
577 
applyTransform(FourPoints bbox)578 void DragSelectionTool::VectorDeformTool::applyTransform(FourPoints bbox) {
579   SelectionTool *tool = getTool();
580 
581   std::unique_ptr<VFDScopedBlock> localVfdScopedBlock;
582   if (!m_vfdScopedBlock) {
583     std::unique_ptr<VFDScopedBlock> &vfdScopedBlock =
584         m_isDragging ? m_vfdScopedBlock : localVfdScopedBlock;
585 
586     vfdScopedBlock.reset(new VFDScopedBlock(tool));
587   }
588 
589   VectorFreeDeformer *freeDeformer =
590       static_cast<VectorFreeDeformer *>(tool->getFreeDeformer());
591 
592   const bool stayedTheSame = bbox.getP00() == freeDeformer->getPoint(0) &&
593                              bbox.getP10() == freeDeformer->getPoint(1) &&
594                              bbox.getP11() == freeDeformer->getPoint(2) &&
595                              bbox.getP01() == freeDeformer->getPoint(3);
596 
597   freeDeformer->setPoints(bbox.getP00(), bbox.getP10(), bbox.getP11(),
598                           bbox.getP01());
599   freeDeformer->setComputeRegion(!m_isDragging);
600   freeDeformer->setPreserveThickness(tool->isConstantThickness());
601   freeDeformer->setFlip(isFlip());
602 
603   if (!TTool::getApplication()->getCurrentObject()->isSpline() && m_undo)
604     m_undo->setFlip(isFlip());
605 
606   freeDeformer->deformImage();
607 
608   tool->invalidate();
609 
610   if (!m_isDragging) tool->notifyImageChanged();
611 
612   if (!stayedTheSame) tool->m_deformValues.m_isSelectionModified = true;
613 
614   if (!m_isDragging && (tool->isLevelType() || tool->isSelectedFramesType()))
615     transformWholeLevel();
616 }
617 
618 //-----------------------------------------------------------------------------
619 
addTransformUndo()620 void DragSelectionTool::VectorDeformTool::addTransformUndo() {
621   if (TTool::getApplication()->getCurrentObject()->isSpline())
622     TUndoManager::manager()->add(
623         new UndoPath(getTool()
624                          ->getXsheet()
625                          ->getStageObject(getTool()->getObjectId())
626                          ->getSpline()));
627   else if (m_undo) {
628     m_undo->registerStrokes();
629     TUndoManager::manager()->add(m_undo.release());
630   }
631 }
632 
633 //-----------------------------------------------------------------------------
634 
transformWholeLevel()635 void DragSelectionTool::VectorDeformTool::transformWholeLevel() {
636   VectorSelectionTool *tool = dynamic_cast<VectorSelectionTool *>(m_tool);
637   assert(tool);
638 
639   assert(!tool->levelSelection().isEmpty());
640 
641   TXshSimpleLevel *level =
642       TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
643 
644   std::vector<TFrameId> fids;
645   level->getFids(fids);
646 
647   // Remove unwanted fids
648   fids.erase(std::remove_if(
649                  fids.begin(), fids.end(),
650                  boost::bind(::currentOrNotSelected, boost::cref(*tool), _1)),
651              fids.end());
652 
653   TUndoManager::manager()->beginBlock();
654   {
655     addTransformUndo();  // For current frame
656 
657     int f, fCount = int(fids.size());
658     for (f = 0; f != fCount; ++f) {
659       const TFrameId &fid = fids[f];
660       int t               = f + 1;  // Current frame's data is always at index 0
661                                     // The others are thus shifted by 1
662       // Skip nonselected frames
663       if (tool->getCurrentFid() == fid ||
664           (tool->isSelectedFramesType() &&
665            tool->getSelectedFrames().count(fid) == 0))
666         continue;
667 
668       TVectorImageP vi = level->getFrame(fid, true);
669       if (!vi) continue;
670 
671       std::unique_ptr<UndoChangeStrokes> undo(
672           new UndoChangeStrokes(level, fid, tool, tool->levelSelection()));
673 
674       std::set<int> strokesIndices;
675 
676       for (int s = 0; s < (int)vi->getStrokeCount(); ++s)
677         strokesIndices.insert(s);
678 
679       FourPoints bbox = tool->getBBox(t);
680 
681       VectorFreeDeformer *freeDeformer =
682           static_cast<VectorFreeDeformer *>(tool->getFreeDeformer(t));
683       freeDeformer->setPoints(bbox.getPoint(0), bbox.getPoint(1),
684                               bbox.getPoint(2), bbox.getPoint(3));
685       freeDeformer->setComputeRegion(true);
686       freeDeformer->setPreserveThickness(tool->isConstantThickness());
687       freeDeformer->setFlip(isFlip());
688       freeDeformer->deformImage();
689 
690       undo->registerStrokes();
691 
692       TUndoManager::manager()->add(undo.release());
693     }
694   }
695   TUndoManager::manager()->endBlock();
696 
697   // Finally, notify changed frames
698   std::for_each(fids.begin(), fids.end(),
699                 boost::bind(  // NOTE: current frame is not here - it should be,
700                     &TTool::notifyImageChanged, m_tool,
701                     _1));  //       but it's currently unnecessary, in fact...
702 
703   // notifyImageChanged(fid) must be invoked OUTSIDE of the loop - since it
704   // seems to imply notifyImageChanged()
705   // on CURRENT image - which seems wrong, but it's too low-level and I'm not
706   // changing it.
707   // This reasonably leads to computeBBox(), but bboxes are taken as INPUT to
708   // this transformation...            >:(
709 }
710 
711 //-----------------------------------------------------------------------------
712 
isFlip()713 bool DragSelectionTool::VectorDeformTool::isFlip() {
714   TPointD scaleValue = getTool()->m_deformValues.m_scaleValue;
715   return m_startScaleValue.x * scaleValue.x < 0 ||
716          m_startScaleValue.y * scaleValue.y < 0;
717 }
718 
719 //-----------------------------------------------------------------------------
720 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)721 void DragSelectionTool::VectorDeformTool::leftButtonUp(const TPointD &pos,
722                                                        const TMouseEvent &e) {
723   std::unique_ptr<VFDScopedBlock> vfdScopedBlock(std::move(m_vfdScopedBlock));
724 
725   SelectionTool *tool = getTool();
726   VectorFreeDeformer *deformer =
727       dynamic_cast<VectorFreeDeformer *>(tool->getFreeDeformer());
728   if (!deformer) return;
729 
730   deformer->setComputeRegion(true);
731   deformer->setFlip(isFlip());
732   deformer->deformRegions();
733 
734   if (!tool->isLevelType() && !tool->isSelectedFramesType())
735     addTransformUndo();
736   else
737     transformWholeLevel();
738 
739   m_isDragging = false;
740 
741   tool->notifyImageChanged();
742 
743   VectorSelectionTool *selectionTool =
744       dynamic_cast<VectorSelectionTool *>(m_tool);
745   selectionTool->setResetCenter(true);
746 }
747 
748 //=============================================================================
749 // VectorRotationTool
750 //-----------------------------------------------------------------------------
751 
VectorRotationTool(VectorSelectionTool * tool)752 DragSelectionTool::VectorRotationTool::VectorRotationTool(
753     VectorSelectionTool *tool)
754     : VectorDeformTool(tool), m_rotation(new Rotation(this)) {}
755 
756 //-----------------------------------------------------------------------------
757 
transform(TAffine aff,double angle)758 void DragSelectionTool::VectorRotationTool::transform(TAffine aff,
759                                                       double angle) {
760   SelectionTool *tool = getTool();
761   FourPoints newBbox(tool->getBBox() * aff);
762   TPointD center = m_rotation->getStartCenter();
763   int i;
764   for (i = 0; i < tool->getBBoxsCount(); i++) {
765     aff = TRotation(center, angle);
766     tool->setBBox(tool->getBBox(i) * aff, i);
767   }
768   applyTransform(newBbox);
769 }
770 
771 //-----------------------------------------------------------------------------
772 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)773 void DragSelectionTool::VectorRotationTool::leftButtonDrag(
774     const TPointD &pos, const TMouseEvent &e) {
775   VectorSelectionTool *tool = dynamic_cast<VectorSelectionTool *>(m_tool);
776   tool->setResetCenter(false);
777   m_rotation->leftButtonDrag(pos, e);
778 }
779 
780 //-----------------------------------------------------------------------------
781 
draw()782 void DragSelectionTool::VectorRotationTool::draw() { m_rotation->draw(); }
783 
784 //=============================================================================
785 // VectorFreeDeformTool
786 //-----------------------------------------------------------------------------
787 
VectorFreeDeformTool(VectorSelectionTool * tool)788 DragSelectionTool::VectorFreeDeformTool::VectorFreeDeformTool(
789     VectorSelectionTool *tool)
790     : VectorDeformTool(tool), m_freeDeform(new FreeDeform(this)) {}
791 
792 //-----------------------------------------------------------------------------
793 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)794 void DragSelectionTool::VectorFreeDeformTool::leftButtonDrag(
795     const TPointD &pos, const TMouseEvent &e) {
796   VectorSelectionTool *tool = dynamic_cast<VectorSelectionTool *>(m_tool);
797   tool->setResetCenter(false);
798   m_freeDeform->leftButtonDrag(pos, e);
799 }
800 
801 //=============================================================================
802 // VectorMoveSelectionTool
803 //-----------------------------------------------------------------------------
804 
VectorMoveSelectionTool(VectorSelectionTool * tool)805 DragSelectionTool::VectorMoveSelectionTool::VectorMoveSelectionTool(
806     VectorSelectionTool *tool)
807     : VectorDeformTool(tool), m_moveSelection(new MoveSelection(this)) {}
808 
809 //-----------------------------------------------------------------------------
810 
transform(TAffine aff)811 void DragSelectionTool::VectorMoveSelectionTool::transform(TAffine aff) {
812   SelectionTool *tool = getTool();
813   int i;
814   for (i = 0; i < (int)tool->getBBoxsCount(); i++)
815     tool->setBBox(tool->getBBox(i) * aff, i);
816   getTool()->setCenter(aff * tool->getCenter());
817   applyTransform(tool->getBBox());
818 }
819 
820 //-----------------------------------------------------------------------------
821 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)822 void DragSelectionTool::VectorMoveSelectionTool::leftButtonDown(
823     const TPointD &pos, const TMouseEvent &e) {
824   m_moveSelection->leftButtonDown(pos, e);
825   VectorDeformTool::leftButtonDown(pos, e);
826 }
827 
828 //-----------------------------------------------------------------------------
829 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)830 void DragSelectionTool::VectorMoveSelectionTool::leftButtonDrag(
831     const TPointD &pos, const TMouseEvent &e) {
832   VectorSelectionTool *tool = dynamic_cast<VectorSelectionTool *>(m_tool);
833   tool->setResetCenter(false);
834   if (e.isCtrlPressed() ||
835       norm2(pos - getStartPos()) > l_dragThreshold * getTool()->getPixelSize())
836     m_moveSelection->leftButtonDrag(pos, e);
837   else  // snap to the original position
838     m_moveSelection->leftButtonDrag(getStartPos(), e);
839 }
840 
841 //=============================================================================
842 // VectorScaleTool
843 //-----------------------------------------------------------------------------
844 
VectorScaleTool(VectorSelectionTool * tool,ScaleType type)845 DragSelectionTool::VectorScaleTool::VectorScaleTool(VectorSelectionTool *tool,
846                                                     ScaleType type)
847     : VectorDeformTool(tool), m_scale(new Scale(this, type)) {}
848 
849 //-----------------------------------------------------------------------------
850 
transform(int index,TPointD newPos)851 TPointD DragSelectionTool::VectorScaleTool::transform(int index,
852                                                       TPointD newPos) {
853   SelectionTool *tool = getTool();
854   TPointD scaleValue  = tool->m_deformValues.m_scaleValue;
855 
856   std::vector<FourPoints> startBboxs = m_scale->getStartBboxs();
857   TPointD center                     = m_scale->getStartCenter();
858   FourPoints bbox = m_scale->bboxScaleInCenter(index, startBboxs[0], newPos,
859                                                scaleValue, center, true);
860   if (bbox == startBboxs[0]) return scaleValue;
861 
862   bool scaleInCenter = m_scale->scaleInCenter();
863   // Se non ho scalato rispetto al centro calcolo la posizione del nuovo centro
864   if (!scaleInCenter)
865     tool->setCenter(m_scale->getNewCenter(index, startBboxs[0], scaleValue));
866 
867   if (tool->isLevelType() || tool->isSelectedFramesType()) {
868     int i;
869     for (i = 1; i < (int)tool->getBBoxsCount(); i++) {
870       FourPoints oldBbox = startBboxs[i];
871       TPointD frameCenter =
872           scaleInCenter ? center
873                         : oldBbox.getPoint(getSymmetricPointIndex(index));
874       TPointD newp =
875           m_scale->getScaledPoint(index, oldBbox, scaleValue, frameCenter);
876       FourPoints newBbox = m_scale->bboxScaleInCenter(
877           index, oldBbox, newp, scaleValue, frameCenter, false);
878       tool->setBBox(newBbox, i);
879       if (!scaleInCenter)
880         tool->setCenter(m_scale->getNewCenter(index, oldBbox, scaleValue), i);
881     }
882   }
883   tool->setBBox(bbox);
884   applyTransform(bbox);
885   return scaleValue;
886 }
887 
888 //-----------------------------------------------------------------------------
889 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)890 void DragSelectionTool::VectorScaleTool::leftButtonDown(const TPointD &pos,
891                                                         const TMouseEvent &e) {
892   m_scale->leftButtonDown(pos, e);
893   VectorDeformTool::leftButtonDown(pos, e);
894 }
895 
896 //-----------------------------------------------------------------------------
897 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)898 void DragSelectionTool::VectorScaleTool::leftButtonDrag(const TPointD &pos,
899                                                         const TMouseEvent &e) {
900   VectorSelectionTool *tool = dynamic_cast<VectorSelectionTool *>(m_tool);
901   tool->setResetCenter(false);
902   m_scale->leftButtonDrag(pos, e);
903 }
904 
905 //=============================================================================
906 // VectorChangeThicknessTool
907 //-----------------------------------------------------------------------------
908 
VectorChangeThicknessTool(VectorSelectionTool * tool)909 DragSelectionTool::VectorChangeThicknessTool::VectorChangeThicknessTool(
910     VectorSelectionTool *tool)
911     : DragTool(tool), m_curPos(), m_firstPos(), m_thicknessChange(0) {
912   TVectorImageP vi = tool->getImage(false);
913   assert(vi);
914 
915   setStrokesThickness(*vi);
916 
917   TXshSimpleLevel *level =
918       TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
919   m_undo.reset(new UndoChangeStrokes(level, tool->getCurrentFid(), tool,
920                                      tool->strokeSelection()));
921 }
922 
923 //-----------------------------------------------------------------------------
924 
~VectorChangeThicknessTool()925 VectorChangeThicknessTool::~VectorChangeThicknessTool() {}
926 
927 //-----------------------------------------------------------------------------
928 
929 namespace {
930 namespace SetStrokeThickness {
931 
932 using namespace DragSelectionTool;
933 
934 struct Data {
935   VectorChangeThicknessTool &m_tool;
936   const TVectorImage &m_vi;
937 };
938 }  // namespace SetStrokeThickness
939 }  // namespace
940 
setStrokesThickness(TVectorImage & vi)941 void DragSelectionTool::VectorChangeThicknessTool::setStrokesThickness(
942     TVectorImage &vi) {
943   struct locals {
944     static void setThickness(const SetStrokeThickness::Data &data, int s) {
945       const TStroke &stroke = *data.m_vi.getStroke(s);
946 
947       std::vector<double> strokeThickness;
948 
949       int cp, cpCount = stroke.getControlPointCount();
950       strokeThickness.reserve(cpCount);
951 
952       for (cp = 0; cp != cpCount; ++cp)
953         strokeThickness.push_back(stroke.getControlPoint(cp).thick);
954 
955       data.m_tool.m_strokesThickness[s] = strokeThickness;
956     }
957 
958   };  // locals
959 
960   SetStrokeThickness::Data data = {*this, vi};
961 
962   VectorSelectionTool *vsTool = static_cast<VectorSelectionTool *>(m_tool);
963   const LevelSelection &levelSelection = vsTool->levelSelection();
964 
965   if (levelSelection.isEmpty()) {
966     StrokeSelection *strokeSelection =
967         static_cast<StrokeSelection *>(m_tool->getSelection());
968     const std::set<int> &selectedStrokeIdxs = strokeSelection->getSelection();
969 
970     std::for_each(selectedStrokeIdxs.begin(), selectedStrokeIdxs.end(),
971                   boost::bind(locals::setThickness, boost::cref(data), _1));
972   } else {
973     std::vector<int> strokeIdxs = getSelectedStrokes(vi, levelSelection);
974 
975     std::for_each(strokeIdxs.begin(), strokeIdxs.end(),
976                   boost::bind(locals::setThickness, boost::cref(data), _1));
977   }
978 }
979 
980 //-----------------------------------------------------------------------------
981 
982 namespace {
983 namespace ChangeImageThickness {
984 
985 using namespace DragSelectionTool;
986 
987 struct Data {
988   VectorChangeThicknessTool &m_tool;
989   TVectorImage &m_vi;
990   double m_newThickness;
991 };
992 }  // namespace ChangeImageThickness
993 }  // namespace
994 
changeImageThickness(TVectorImage & vi,double newThickness)995 void DragSelectionTool::VectorChangeThicknessTool::changeImageThickness(
996     TVectorImage &vi, double newThickness) {
997   struct locals {
998     static void changeThickness(const ChangeImageThickness::Data &data, int s) {
999       TStroke &stroke = *data.m_vi.getStroke(s);
1000 
1001       for (int cp = 0; cp < (int)stroke.getControlPointCount(); ++cp) {
1002         double thickness =
1003             tcrop(data.m_tool.m_strokesThickness[s][cp] + data.m_newThickness,
1004                   0.0, 255.0);
1005 
1006         TThickPoint point(TPointD(stroke.getControlPoint(cp)), thickness);
1007 
1008         stroke.setControlPoint(cp, point);
1009       }
1010     }
1011 
1012   };  // locals
1013 
1014   ChangeImageThickness::Data data = {*this, vi, newThickness};
1015 
1016   VectorSelectionTool *vsTool = static_cast<VectorSelectionTool *>(getTool());
1017   const LevelSelection &levelSelection = vsTool->levelSelection();
1018 
1019   if (levelSelection.isEmpty()) {
1020     StrokeSelection *strokeSelection =
1021         static_cast<StrokeSelection *>(m_tool->getSelection());
1022     const std::set<int> &selectedStrokeIdxs = strokeSelection->getSelection();
1023 
1024     std::for_each(selectedStrokeIdxs.begin(), selectedStrokeIdxs.end(),
1025                   boost::bind(locals::changeThickness, boost::ref(data), _1));
1026   } else {
1027     std::vector<int> strokeIdxs = getSelectedStrokes(vi, levelSelection);
1028 
1029     std::for_each(strokeIdxs.begin(), strokeIdxs.end(),
1030                   boost::bind(locals::changeThickness, boost::ref(data), _1));
1031   }
1032 }
1033 
1034 //-----------------------------------------------------------------------------
1035 
addUndo()1036 void DragSelectionTool::VectorChangeThicknessTool::addUndo() {
1037   TVectorImageP curVi = getTool()->getImage(true);
1038   if (!curVi) return;
1039 
1040   m_undo->registerStrokes();
1041 
1042   SelectionTool *tool = getTool();
1043   if (tool->isLevelType() || tool->isSelectedFramesType()) {
1044     VectorSelectionTool *vtool = dynamic_cast<VectorSelectionTool *>(tool);
1045     assert(vtool && !vtool->levelSelection().isEmpty());
1046 
1047     TXshSimpleLevel *level =
1048         TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
1049 
1050     // Retrieve frames available in the level
1051     std::vector<TFrameId> fids;
1052     level->getFids(fids);
1053 
1054     // Remove unwanted frames
1055     fids.erase(std::remove_if(fids.begin(), fids.end(),
1056                               boost::bind(::currentOrNotSelected,
1057                                           boost::cref(*vtool), _1)),
1058                fids.end());
1059 
1060     TUndoManager::manager()->beginBlock();
1061     {
1062       // Current frame added separately
1063       TUndoManager::manager()->add(m_undo.release());  // Inside an undo block
1064 
1065       // Iterate remaining ones
1066       int f, fCount = int(fids.size());
1067       for (f = 0; f != fCount; ++f) {
1068         const TFrameId &fid = fids[f];
1069 
1070         TVectorImageP vi = level->getFrame(fid, true);
1071         if (!vi) {
1072           assert(vi);
1073           continue;
1074         }
1075 
1076         // Transform fid's selection
1077         std::unique_ptr<UndoChangeStrokes> undo(
1078             new UndoChangeStrokes(level, fid, vtool, vtool->levelSelection()));
1079 
1080         setStrokesThickness(*vi);
1081         changeImageThickness(*vi, m_thicknessChange);
1082 
1083         m_strokesThickness.clear();
1084         undo->registerStrokes();
1085 
1086         TUndoManager::manager()->add(undo.release());
1087       }
1088     }
1089     TUndoManager::manager()->endBlock();
1090 
1091     // Finally, notify changed frames
1092     std::for_each(fids.begin(), fids.end(),
1093                   boost::bind(  // NOTE: current frame is not here - it was
1094                       &TTool::notifyImageChanged, m_tool,
1095                       _1));  //       aldready notified
1096   } else
1097     TUndoManager::manager()->add(m_undo.release());  // Outside any undo block
1098 }
1099 
1100 //-----------------------------------------------------------------------------
1101 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)1102 void DragSelectionTool::VectorChangeThicknessTool::leftButtonDown(
1103     const TPointD &pos, const TMouseEvent &e) {
1104   m_curPos   = pos;
1105   m_firstPos = pos;
1106 }
1107 
1108 //-----------------------------------------------------------------------------
1109 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)1110 void DragSelectionTool::VectorChangeThicknessTool::leftButtonDrag(
1111     const TPointD &pos, const TMouseEvent &e) {
1112   TAffine aff;
1113   TPointD delta    = pos - m_curPos;
1114   TVectorImageP vi = getTool()->getImage(true);
1115   if (!vi) return;
1116   VectorSelectionTool *tool = dynamic_cast<VectorSelectionTool *>(m_tool);
1117   tool->setResetCenter(false);
1118   m_thicknessChange = (pos.y - m_firstPos.y) * 0.2;
1119   changeImageThickness(*vi, m_thicknessChange);
1120   getTool()->m_deformValues.m_maxSelectionThickness = m_thicknessChange;
1121   getTool()->computeBBox();
1122   getTool()->invalidate();
1123   m_curPos = pos;
1124   getTool()->notifyImageChanged();
1125   TTool::getApplication()->getCurrentTool()->notifyToolChanged();
1126 }
1127 
1128 //-----------------------------------------------------------------------------
1129 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)1130 void DragSelectionTool::VectorChangeThicknessTool::leftButtonUp(
1131     const TPointD &pos, const TMouseEvent &e) {
1132   TVectorImageP curVi = getTool()->getImage(true);
1133   if (!curVi) return;
1134   addUndo();
1135   m_strokesThickness.clear();
1136 }
1137 
1138 //=============================================================================
1139 namespace {
1140 //-----------------------------------------------------------------------------
1141 
getGroupBBox(const TVectorImage & vi,int strokeIndex,TRectD & gBox)1142 bool getGroupBBox(const TVectorImage &vi, int strokeIndex, TRectD &gBox) {
1143   if (!vi.isStrokeGrouped(strokeIndex)) return false;
1144 
1145   gBox = vi.getStroke(strokeIndex)->getBBox();
1146 
1147   int s, sCount = int(vi.getStrokeCount());
1148   for (s = 0; s != sCount; ++s) {
1149     if (vi.sameGroup(s, strokeIndex)) gBox += vi.getStroke(s)->getBBox();
1150   }
1151 
1152   return true;
1153 }
1154 
1155 //=============================================================================
1156 // UndoEnterGroup
1157 //-----------------------------------------------------------------------------
1158 
1159 class UndoEnterGroup final : public TUndo {
1160   int m_strokeIndex;
1161   TVectorImageP m_vi;
1162 
1163 public:
UndoEnterGroup(TVectorImageP vi,int strokeIndex)1164   UndoEnterGroup(TVectorImageP vi, int strokeIndex)
1165       : m_vi(vi), m_strokeIndex(strokeIndex) {}
undo() const1166   void undo() const override {
1167     m_vi->exitGroup();
1168     TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
1169   }
redo() const1170   void redo() const override {
1171     m_vi->enterGroup(m_strokeIndex);
1172     TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
1173   }
getSize() const1174   int getSize() const override { return sizeof(*this); }
1175 };
1176 
1177 //=============================================================================
1178 // UndoExitGroup
1179 //-----------------------------------------------------------------------------
1180 
1181 class UndoExitGroup final : public TUndo {
1182   int m_strokeIndex;
1183   TVectorImageP m_vi;
1184 
1185 public:
UndoExitGroup(TVectorImageP vi,int strokeIndex)1186   UndoExitGroup(TVectorImageP vi, int strokeIndex)
1187       : m_vi(vi), m_strokeIndex(strokeIndex) {}
undo() const1188   void undo() const override {
1189     m_vi->enterGroup(m_strokeIndex);
1190     TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
1191   }
redo() const1192   void redo() const override {
1193     m_vi->exitGroup();
1194     TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
1195   }
1196 
getSize() const1197   int getSize() const override { return sizeof(*this); }
1198 };
1199 
1200 }  // namespace
1201 
1202 //=============================================================================
1203 // VectorSelectionTool
1204 //-----------------------------------------------------------------------------
1205 
VectorSelectionTool(int targetType)1206 VectorSelectionTool::VectorSelectionTool(int targetType)
1207     : SelectionTool(targetType)
1208     , m_selectionTarget("Mode:")
1209     , m_includeIntersection("Include Intersection", false)
1210     , m_constantThickness("Preserve Thickness", false)
1211     , m_levelSelection(m_strokeSelection)
1212     , m_capStyle("Cap")
1213     , m_joinStyle("Join")
1214     , m_miterJoinLimit("Miter:", 0, 100, 4)
1215     , m_selectionCount(0)
1216     , m_canEnterGroup(true)
1217     , m_resetCenter(true) {
1218   assert(targetType == TTool::Vectors);
1219   m_prop.bind(m_selectionTarget);
1220   m_prop.bind(m_includeIntersection);
1221   m_prop.bind(m_constantThickness);
1222 
1223   m_selectionTarget.addValue(NORMAL_TYPE);
1224   m_selectionTarget.addValue(SELECTED_FRAMES_TYPE);
1225   m_selectionTarget.addValue(ALL_LEVEL_TYPE);
1226   m_selectionTarget.addValue(SAME_STYLE_TYPE);
1227   m_selectionTarget.addValue(STYLE_SELECTED_FRAMES_TYPE);
1228   m_selectionTarget.addValue(STYLE_LEVEL_TYPE);
1229   m_selectionTarget.addValue(BOUNDARY_TYPE);
1230   m_selectionTarget.addValue(BOUNDARY_SELECTED_FRAMES_TYPE);
1231   m_selectionTarget.addValue(BOUNDARY_LEVEL_TYPE);
1232 
1233   m_strokeSelection.setView(this);
1234 
1235   m_includeIntersection.setId("IncludeIntersection");
1236   m_constantThickness.setId("PreserveThickness");
1237   m_selectionTarget.setId("SelectionMode");
1238 
1239   m_capStyle.addValue(BUTT_WSTR, QString::fromStdWString(BUTT_WSTR));
1240   m_capStyle.addValue(ROUNDC_WSTR, QString::fromStdWString(ROUNDC_WSTR));
1241   m_capStyle.addValue(PROJECTING_WSTR,
1242                       QString::fromStdWString(PROJECTING_WSTR));
1243   m_capStyle.setId("Cap");
1244 
1245   m_joinStyle.addValue(MITER_WSTR, QString::fromStdWString(MITER_WSTR));
1246   m_joinStyle.addValue(ROUNDJ_WSTR, QString::fromStdWString(ROUNDJ_WSTR));
1247   m_joinStyle.addValue(BEVEL_WSTR, QString::fromStdWString(BEVEL_WSTR));
1248   m_joinStyle.setId("Join");
1249 
1250   m_miterJoinLimit.setId("Miter");
1251 
1252   m_outlineProps.bind(m_capStyle);
1253   m_outlineProps.bind(m_joinStyle);
1254   m_outlineProps.bind(m_miterJoinLimit);
1255 }
1256 
1257 //------------------------------------------------------------------------------
1258 
setNewFreeDeformer()1259 void VectorSelectionTool::setNewFreeDeformer() {
1260   clearDeformers();
1261 
1262   TVectorImageP vi =
1263       getImage(true);  // BAD: Should not be done at tool creation...
1264   if (!vi) return;
1265 
1266   // Current freeDeformer always at index 0
1267   m_freeDeformers.push_back(
1268       new VectorFreeDeformer(vi, m_strokeSelection.getSelection()));
1269 
1270   if (isLevelType() || isSelectedFramesType()) {
1271     TXshSimpleLevel *level =
1272         TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
1273 
1274     // All SELECTED frames' subsequent freeDeformers are stored sequentially
1275     // after that
1276     std::vector<TFrameId> fids;
1277     level->getFids(fids);
1278 
1279     fids.erase(std::remove_if(
1280                    fids.begin(), fids.end(),
1281                    boost::bind(::currentOrNotSelected, boost::cref(*this), _1)),
1282                fids.end());
1283 
1284     std::vector<TFrameId>::iterator ft, fEnd = fids.end();
1285     for (ft = fids.begin(); ft != fEnd; ++ft) {
1286       if (TVectorImageP levelVi = level->getFrame(*ft, false)) {
1287         const std::vector<int> &selectedStrokeIdxs =
1288             ::getSelectedStrokes(*levelVi, m_levelSelection);
1289         std::set<int> strokesIndices(selectedStrokeIdxs.begin(),
1290                                      selectedStrokeIdxs.end());
1291 
1292         m_freeDeformers.push_back(
1293             new VectorFreeDeformer(levelVi, strokesIndices));
1294       }
1295     }
1296   }
1297 }
1298 
1299 //-----------------------------------------------------------------------------
1300 
isLevelType() const1301 bool VectorSelectionTool::isLevelType() const {
1302   return m_levelSelection.framesMode() == LevelSelection::FRAMES_ALL;
1303 }
1304 
1305 //-----------------------------------------------------------------------------
1306 
isSelectedFramesType() const1307 bool VectorSelectionTool::isSelectedFramesType() const {
1308   return m_levelSelection.framesMode() == LevelSelection::FRAMES_SELECTED;
1309 }
1310 
1311 //-----------------------------------------------------------------------------
1312 
isSameStyleType() const1313 bool VectorSelectionTool::isSameStyleType() const {
1314   return m_levelSelection.filter() == LevelSelection::SELECTED_STYLES;
1315 }
1316 
1317 //-----------------------------------------------------------------------------
1318 
isModifiableSelectionType() const1319 bool VectorSelectionTool::isModifiableSelectionType() const {
1320   return (m_levelSelection.isEmpty() ||
1321           m_levelSelection.filter() == LevelSelection::SELECTED_STYLES);
1322 }
1323 
1324 //-----------------------------------------------------------------------------
1325 
updateTranslation()1326 void VectorSelectionTool::updateTranslation() {
1327   m_selectionTarget.setQStringName(tr("Mode:"));
1328   m_selectionTarget.setItemUIName(NORMAL_TYPE, tr("Standard"));
1329   m_selectionTarget.setItemUIName(SELECTED_FRAMES_TYPE, tr("Selected Frames"));
1330   m_selectionTarget.setItemUIName(ALL_LEVEL_TYPE, tr("Whole Level"));
1331   m_selectionTarget.setItemUIName(SAME_STYLE_TYPE, tr("Same Style"));
1332   m_selectionTarget.setItemUIName(STYLE_SELECTED_FRAMES_TYPE,
1333                                   tr("Same Style on Selected Frames"));
1334   m_selectionTarget.setItemUIName(STYLE_LEVEL_TYPE,
1335                                   tr("Same Style on Whole Level"));
1336   m_selectionTarget.setItemUIName(BOUNDARY_TYPE, tr("Boundary Strokes"));
1337   m_selectionTarget.setItemUIName(BOUNDARY_SELECTED_FRAMES_TYPE,
1338                                   tr("Boundaries on Selected Frames"));
1339   m_selectionTarget.setItemUIName(BOUNDARY_LEVEL_TYPE,
1340                                   tr("Boundaries on Whole Level"));
1341 
1342   m_includeIntersection.setQStringName(tr("Include Intersection"));
1343   m_constantThickness.setQStringName(tr("Preserve Thickness"));
1344 
1345   m_capStyle.setQStringName(tr("Cap"));
1346   m_capStyle.setItemUIName(BUTT_WSTR, tr("Butt cap"));
1347   m_capStyle.setItemUIName(ROUNDC_WSTR, tr("Round cap"));
1348   m_capStyle.setItemUIName(PROJECTING_WSTR, tr("Projecting cap"));
1349 
1350   m_joinStyle.setQStringName(tr("Join"));
1351   m_joinStyle.setItemUIName(MITER_WSTR, tr("Miter join"));
1352   m_joinStyle.setItemUIName(ROUNDJ_WSTR, tr("Round join"));
1353   m_joinStyle.setItemUIName(BEVEL_WSTR, tr("Bevel join"));
1354 
1355   m_miterJoinLimit.setQStringName(tr("Miter:"));
1356   SelectionTool::updateTranslation();
1357 }
1358 
1359 //-----------------------------------------------------------------------------
1360 
updateSelectionTarget()1361 void VectorSelectionTool::updateSelectionTarget() {
1362   // Make the correct selection current
1363   if (m_selectionTarget.getIndex() == NORMAL_TYPE_IDX) {
1364     std::set<int> selectedStrokes;  // Retain previously selected strokes across
1365     selectedStrokes.swap(
1366         m_strokeSelection.getSelection());  // current selection change
1367 
1368     m_strokeSelection.makeCurrent();  // Empties any (different) previously
1369                                       // current selection on its own
1370     selectedStrokes.swap(m_strokeSelection.getSelection());
1371     return;
1372   }
1373 
1374   m_levelSelection.makeCurrent();  // Same here
1375 
1376   // Choose frames mode
1377   LevelSelection::FramesMode framesMode;
1378   switch (m_selectionTarget.getIndex()) {
1379   case SAME_STYLE_TYPE_IDX:
1380   case BOUNDARY_TYPE_IDX:
1381     framesMode = LevelSelection::FRAMES_CURRENT;
1382     break;
1383 
1384   case ALL_LEVEL_TYPE_IDX:
1385   case STYLE_LEVEL_TYPE_IDX:
1386   case BOUNDARY_LEVEL_TYPE_IDX:
1387     framesMode = LevelSelection::FRAMES_ALL;
1388     break;
1389 
1390   case SELECTED_FRAMES_TYPE_IDX:
1391   case STYLE_SELECTED_FRAMES_TYPE_IDX:
1392   case BOUNDARY_SELECTED_FRAMES_TYPE_IDX:
1393     framesMode = LevelSelection::FRAMES_SELECTED;
1394     break;
1395   }
1396 
1397   if (framesMode != m_levelSelection.framesMode()) clearSelectedStrokes();
1398 
1399   m_levelSelection.framesMode() = framesMode;
1400 
1401   // Choose filter
1402   LevelSelection::Filter filter;
1403   switch (m_selectionTarget.getIndex()) {
1404   case SELECTED_FRAMES_TYPE_IDX:
1405   case ALL_LEVEL_TYPE_IDX:
1406     filter = LevelSelection::WHOLE;
1407     selectedStyles().clear();
1408     break;
1409 
1410   case SAME_STYLE_TYPE_IDX:
1411   case STYLE_SELECTED_FRAMES_TYPE_IDX:
1412   case STYLE_LEVEL_TYPE_IDX:
1413     filter = LevelSelection::SELECTED_STYLES;
1414     break;
1415 
1416   case BOUNDARY_TYPE_IDX:
1417   case BOUNDARY_SELECTED_FRAMES_TYPE_IDX:
1418   case BOUNDARY_LEVEL_TYPE_IDX:
1419     filter = LevelSelection::BOUNDARY_STROKES;
1420     selectedStyles().clear();
1421     break;
1422   }
1423 
1424   if (filter != m_levelSelection.filter()) clearSelectedStrokes();
1425 
1426   m_levelSelection.filter() = filter;
1427 }
1428 
1429 //-----------------------------------------------------------------------------
1430 
finalizeSelection()1431 void VectorSelectionTool::finalizeSelection() {
1432   TVectorImageP vi = getImage(false);
1433   if (vi && !m_levelSelection.isEmpty()) {
1434     std::set<int> &selection = m_strokeSelection.getSelection();
1435     selection.clear();
1436 
1437     // Apply base additive selection
1438     if (!isSelectedFramesType() || m_selectedFrames.count(getCurrentFid())) {
1439       // Apply filters
1440       std::vector<int> selectedStrokes =
1441           getSelectedStrokes(*vi, m_levelSelection);
1442       std::set<int>(selectedStrokes.begin(), selectedStrokes.end())
1443           .swap(selection);
1444     }
1445   }
1446 
1447   computeBBox();
1448 
1449   TTool::getApplication()
1450       ->getCurrentTool()
1451       ->notifyToolChanged();  // Refreshes toolbar values
1452 }
1453 
1454 //-----------------------------------------------------------------------------
1455 
clearSelectedStrokes()1456 void VectorSelectionTool::clearSelectedStrokes() {
1457   m_strokeSelection.selectNone();
1458   m_levelSelection.styles().clear();
1459   m_deformValues.reset();
1460 }
1461 
1462 //-----------------------------------------------------------------------------
1463 
modifySelectionOnClick(TImageP image,const TPointD & pos,const TMouseEvent & e)1464 void VectorSelectionTool::modifySelectionOnClick(TImageP image,
1465                                                  const TPointD &pos,
1466                                                  const TMouseEvent &e) {
1467   TVectorImageP vi = TVectorImageP(image);
1468   assert(m_strokeSelection.getImage() == vi);
1469 
1470   if (!vi) return;
1471 
1472   updateSelectionTarget();  // Make selection current. This is necessary in case
1473   // some other selection context made another selection current.
1474   m_firstPos = m_curPos = pos;
1475   m_selectingRect       = FourPoints();
1476   m_selecting           = false;
1477   m_justSelected        = false;
1478 
1479   updateAction(pos, e);
1480   if (m_what != Inside && m_what != Outside && m_what != ADD_SELECTION) return;
1481 
1482   UINT index         = 0;
1483   bool modifiableSel = isModifiableSelectionType(),
1484        strokeAtPos   = getStrokeIndexFromPos(index, vi, pos, getPixelSize(),
1485                                            getViewer()->getViewMatrix()),
1486        addStroke     = strokeAtPos && !m_strokeSelection.isSelected(index),
1487        toggleStroke  = strokeAtPos && e.isShiftPressed();
1488 
1489   m_selecting =
1490       (modifiableSel && !strokeAtPos  // There must be no stroke under cursor
1491        && (e.isShiftPressed()  // and either the user is explicitly performing
1492                                // additional selection
1493            || (m_strokeSelectionType.getIndex() !=
1494                POLYLINE_SELECTION_IDX)  // or the tool support immediate
1495                                         // selection on clear
1496            || m_strokeSelection
1497                   .isEmpty()));  // or the strokes list was already cleared
1498 
1499   bool clearTargets     = !(strokeAtPos || e.isShiftPressed() || m_selecting),
1500        clearSelection   = (addStroke || !strokeAtPos) && !e.isShiftPressed(),
1501        selectionChanged = clearSelection;
1502 
1503   assert(clearTargets ? clearSelection : true);
1504 
1505   if (clearTargets) m_levelSelection.selectNone();
1506 
1507   if (clearSelection) {
1508     m_strokeSelection.selectNone();
1509     selectedStyles().clear();  // Targets are preserved here
1510   }
1511 
1512   if (strokeAtPos)
1513     selectionChanged = m_justSelected = selectStroke(index, toggleStroke);
1514 
1515   if (selectionChanged) {
1516     m_deformValues.reset();  // Resets selection values shown in the toolbar
1517 
1518     finalizeSelection();
1519     notifySelectionChanged();
1520 
1521     invalidate();
1522   }
1523 }
1524 
1525 //-----------------------------------------------------------------------------
1526 
leftButtonDoubleClick(const TPointD & pos,const TMouseEvent & e)1527 void VectorSelectionTool::leftButtonDoubleClick(const TPointD &pos,
1528                                                 const TMouseEvent &e) {
1529   TVectorImageP vi = getImage(false);
1530   if (!vi) return;
1531 
1532   if (m_strokeSelectionType.getIndex() == POLYLINE_SELECTION_IDX &&
1533       !m_polyline.empty()) {
1534     closePolyline(pos);
1535     selectRegionVectorImage(m_includeIntersection.getValue());
1536 
1537     m_selecting = false;
1538     invalidate();
1539 
1540     return;
1541   }
1542 
1543   int strokeIndex;
1544   if ((strokeIndex = vi->pickGroup(pos)) >= 0) {
1545     if (vi->canEnterGroup(strokeIndex) && m_canEnterGroup) {
1546       if (vi->enterGroup(strokeIndex)) {
1547         clearSelectedStrokes();
1548         TUndoManager::manager()->add(new UndoEnterGroup(vi, strokeIndex));
1549       }
1550     }
1551   } else if ((strokeIndex = vi->exitGroup()) >= 0)
1552     TUndoManager::manager()->add(new UndoExitGroup(vi, strokeIndex));
1553 
1554   finalizeSelection();
1555   invalidate();
1556 }
1557 
1558 //-----------------------------------------------------------------------------
1559 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)1560 void VectorSelectionTool::leftButtonDrag(const TPointD &pos,
1561                                          const TMouseEvent &e) {
1562   if (m_dragTool) {
1563     if (!m_strokeSelection.isEditable()) return;
1564 
1565     m_dragTool->leftButtonDrag(pos, e);
1566     return;
1567   }
1568 
1569   TVectorImageP vi = getImage(false);
1570   if (!vi) return;
1571 
1572   double pixelSize        = getPixelSize();
1573   TTool::Application *app = TTool::getApplication();
1574   if (!app || m_justSelected || !m_selecting ||
1575       tdistance2(pos, m_curPos) < 9.0 * pixelSize * pixelSize)
1576     return;
1577 
1578   m_curPos = pos;
1579 
1580   if (m_strokeSelectionType.getIndex() == FREEHAND_SELECTION_IDX) {
1581     freehandDrag(pos);
1582     invalidate();
1583   } else if (m_strokeSelectionType.getIndex() == RECT_SELECTION_IDX) {
1584     bool selectOverlappingStroke = (m_firstPos.x > pos.x);
1585 
1586     TRectD rect(m_firstPos, pos);
1587     m_selectingRect = rect;
1588 
1589     std::set<int> oldSelection;
1590     if (m_shiftPressed) oldSelection = m_strokeSelection.getSelection();
1591 
1592     clearSelectedStrokes();
1593 
1594     QMutexLocker lock(vi->getMutex());
1595 
1596     m_strokeSelection.setImage(vi);  // >_<  Shouldn't be done here...?
1597 
1598     bool selectionChanged = false;
1599 
1600     int s, sCount = vi->getStrokeCount();
1601     for (s = 0; s != sCount; ++s) {
1602       if (!vi->isEnteredGroupStroke(s)) continue;
1603 
1604       TStroke *stroke = vi->getStroke(s);
1605 
1606       if (m_strokeSelection.isSelected(s)) continue;
1607 
1608       bool inSelection = selectOverlappingStroke
1609                              ? rect.overlaps(stroke->getBBox())
1610                              : rect.contains(stroke->getBBox());
1611 
1612       if (inSelection || (m_shiftPressed && oldSelection.count(s)))
1613         selectionChanged = (selectStroke(s, false) || selectionChanged);
1614     }
1615 
1616     if (selectionChanged) finalizeSelection();
1617 
1618     invalidate();
1619   }
1620 }
1621 
1622 //-----------------------------------------------------------------------------
1623 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)1624 void VectorSelectionTool::leftButtonDown(const TPointD &pos,
1625                                          const TMouseEvent &e) {
1626   if (getViewer() && getViewer()->getGuidedStrokePickerMode()) {
1627     getViewer()->doPickGuideStroke(pos);
1628     return;
1629   }
1630 
1631   SelectionTool::leftButtonDown(pos, e);
1632 }
1633 
1634 //-----------------------------------------------------------------------------
1635 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)1636 void VectorSelectionTool::leftButtonUp(const TPointD &pos,
1637                                        const TMouseEvent &e) {
1638   m_leftButtonMousePressed = false;
1639   m_shiftPressed           = false;
1640 
1641   if (m_dragTool) {
1642     if (!m_strokeSelection.isEditable()) {
1643       delete m_dragTool;
1644       m_dragTool = 0;
1645       return;
1646     }
1647 
1648     m_dragTool->leftButtonUp(pos, e);
1649     delete m_dragTool;
1650     m_dragTool = 0;
1651     invalidate();
1652     return;
1653   }
1654 
1655   if (!m_selecting) return;
1656 
1657   // Complete selection
1658   TVectorImageP vi = getImage(false);
1659 
1660   if (vi) {
1661     if (m_strokeSelectionType.getIndex() == RECT_SELECTION_IDX)
1662       notifySelectionChanged();
1663     else if (m_strokeSelectionType.getIndex() == FREEHAND_SELECTION_IDX) {
1664       QMutexLocker lock(vi->getMutex());
1665 
1666       closeFreehand(pos);
1667 
1668       if (m_stroke->getControlPointCount() > 3)
1669         selectRegionVectorImage(m_includeIntersection.getValue());
1670 
1671       delete m_stroke;  // >:(
1672       m_stroke = 0;
1673       m_track.clear();
1674     }
1675   }
1676 
1677   m_selecting    = false;
1678   m_justSelected = false;
1679   invalidate();
1680 }
1681 
1682 //-----------------------------------------------------------------------------
1683 
addContextMenuItems(QMenu * menu)1684 void VectorSelectionTool::addContextMenuItems(QMenu *menu) {
1685   menu->addAction(CommandManager::instance()->getAction(MI_RemoveEndpoints));
1686   menu->addSeparator();
1687 
1688   m_strokeSelection.getGroupCommand()->addMenuItems(menu);
1689 }
1690 
1691 //-----------------------------------------------------------------------------
1692 
drawInLevelType(const TVectorImage & vi)1693 void VectorSelectionTool::drawInLevelType(const TVectorImage &vi) {
1694   glPushMatrix();
1695 
1696   FourPoints bbox = getBBox();
1697   if (!bbox.isEmpty()) {
1698     TPixel32 frameColor(127, 127, 127);
1699     double pixelSize = getPixelSize();
1700 
1701     drawFourPoints(bbox, TPixel32::Black, 0x5555, true);
1702     drawFourPoints(bbox.enlarge(pixelSize * (-4)), frameColor, 0xffff, true);
1703     drawFourPoints(bbox.enlarge(pixelSize * (-2)), frameColor, 0x8888, true);
1704 
1705     drawCommandHandle(&vi);
1706   }
1707 
1708   drawSelectedStrokes(vi);
1709 
1710   if (m_selecting && !m_selectingRect.isEmpty()) drawRectSelection(&vi);
1711 
1712   if (m_strokeSelectionType.getIndex() == POLYLINE_SELECTION_IDX)
1713     drawPolylineSelection();
1714 
1715   glPopMatrix();
1716 }
1717 
1718 //-----------------------------------------------------------------------------
1719 
drawSelectedStrokes(const TVectorImage & vi)1720 void VectorSelectionTool::drawSelectedStrokes(const TVectorImage &vi) {
1721   glEnable(GL_LINE_STIPPLE);
1722 
1723   double pixelSize = getPixelSize();
1724 
1725   int s, sCount = vi.getStrokeCount();
1726   for (s = 0; s != sCount; ++s) {
1727     if (m_strokeSelection.isSelected(s)) {
1728       TStroke *stroke = vi.getStroke(s);
1729 
1730       glLineStipple(1, 0xF0F0);
1731       tglColor(TPixel32::Black);
1732       drawStrokeCenterline(*stroke, pixelSize);
1733 
1734       glLineStipple(1, 0x0F0F);
1735       tglColor(TPixel32::White);
1736       drawStrokeCenterline(*stroke, pixelSize);
1737     }
1738   }
1739 
1740   glDisable(GL_LINE_STIPPLE);
1741 }
1742 
1743 //-----------------------------------------------------------------------------
1744 
drawGroup(const TVectorImage & vi)1745 void VectorSelectionTool::drawGroup(const TVectorImage &vi) {
1746   int s, sCount = vi.getStrokeCount();
1747   for (s = 0; s != sCount; ++s) {
1748     if (m_strokeSelection.isSelected(s)) {
1749       TRectD gBox;
1750       if (getGroupBBox(vi, s, gBox)) drawRect(gBox, TPixel::Blue, 0xffff);
1751     }
1752   }
1753 }
1754 
1755 //-----------------------------------------------------------------------------
1756 
draw()1757 void VectorSelectionTool::draw() {
1758   TVectorImageP vi = getImage(false);
1759   if (!vi) return;
1760 
1761   if (isLevelType() || isSelectedFramesType()) {
1762     drawInLevelType(*vi);
1763     return;
1764   }
1765 
1766   glPushMatrix();
1767 
1768   if (m_strokeSelection.isEmpty())  // o_o  WTF!?
1769     m_bboxs.clear();                //
1770 
1771   // common draw
1772   if (getBBoxsCount() > 0) drawCommandHandle(vi.getPointer());
1773 
1774   if (m_selecting && !m_selectingRect.isEmpty())
1775     drawRectSelection(vi.getPointer());
1776 
1777   TRectD bbox = vi->getBBox();
1778   TPixel32 frameColor(140, 140, 140);
1779   tglColor(frameColor);
1780   drawRect(bbox, frameColor, 0x5555, true);
1781 
1782   drawSelectedStrokes(*vi);
1783 
1784   if (m_strokeSelectionType.getIndex() == POLYLINE_SELECTION_IDX)
1785     drawPolylineSelection();
1786   else if (m_strokeSelectionType.getIndex() == FREEHAND_SELECTION_IDX)
1787     drawFreehandSelection();
1788 
1789   if (m_levelSelection.isEmpty()) drawGroup(*vi);
1790 
1791   glPopMatrix();
1792 }
1793 
1794 //-----------------------------------------------------------------------------
1795 
getSelection()1796 TSelection *VectorSelectionTool::getSelection() {
1797   TImage *image    = getImage(false);
1798   TVectorImageP vi = image;
1799   if (!vi) return 0;
1800 
1801   return &m_strokeSelection;
1802 }
1803 
1804 //-----------------------------------------------------------------------------
1805 
isSelectionEmpty()1806 bool VectorSelectionTool::isSelectionEmpty() {
1807   TVectorImageP vi =
1808       getImage(false);  // We want at least current image to be visible.
1809   if (!vi)              // This is somewhat in line to preventing tool actions
1810     return true;        // on non-visible levels.
1811 
1812   return m_strokeSelection
1813       .isEmpty();  // Same here, something should be visibly selected.
1814 }
1815 
1816 //-----------------------------------------------------------------------------
1817 
computeBBox()1818 void VectorSelectionTool::computeBBox() {
1819   m_bboxs.clear();
1820   if (canResetCenter()) m_centers.clear();
1821 
1822   TVectorImageP vi = getImage(false);
1823   if (!vi) return;
1824 
1825   if (isLevelType() || isSelectedFramesType()) {
1826     double maxThickness = 0;
1827 
1828     // Calculate current image's bbox - it is always the first one (index 0)
1829     if (vi && (isLevelType() || m_selectedFrames.count(getCurrentFid()) > 0)) {
1830       FourPoints bbox =
1831           getFourPointsFromVectorImage(vi, selectedStyles(), maxThickness);
1832 
1833       m_bboxs.push_back(bbox);
1834       m_centers.push_back((bbox.getP00() + bbox.getP11()) * 0.5);
1835     }
1836 
1837     // All subsequent SELECTED frames' bboxes come sequentially starting from 1
1838     if (TXshSimpleLevel *level =
1839             TTool::getApplication()->getCurrentLevel()->getSimpleLevel()) {
1840       std::vector<TFrameId> fids;
1841       level->getFids(fids);
1842 
1843       for (int i = 0; i < (int)fids.size(); ++i) {
1844         if (getCurrentFid() == fids[i] ||
1845             (isSelectedFramesType() && m_selectedFrames.count(fids[i]) == 0))
1846           continue;
1847 
1848         TVectorImageP vi = level->getFrame(fids[i], false);
1849         if (!vi) continue;
1850 
1851         FourPoints p =
1852             getFourPointsFromVectorImage(vi, selectedStyles(), maxThickness);
1853 
1854         m_bboxs.push_back(p);
1855         m_centers.push_back(0.5 * (p.getP00() + p.getP11()));
1856         m_deformValues.m_maxSelectionThickness = maxThickness;
1857       }
1858     }
1859   } else if (vi) {
1860     TRectD newBbox;
1861     double maxThickness = 0;
1862 
1863     for (int i = 0; i < (int)vi->getStrokeCount(); i++) {
1864       TStroke *stroke = vi->getStroke(i);
1865 
1866       if (m_strokeSelection.isSelected(i)) {
1867         newBbox += stroke->getBBox();
1868 
1869         for (int j = 0; j < stroke->getControlPointCount(); j++) {
1870           double thick = stroke->getControlPoint(j).thick;
1871           if (maxThickness < thick) maxThickness = thick;
1872         }
1873       }
1874     }
1875 
1876     m_deformValues.m_maxSelectionThickness = maxThickness;
1877     FourPoints bbox;
1878     bbox = newBbox;
1879     m_bboxs.push_back(bbox);
1880     if (canResetCenter())
1881       m_centers.push_back(0.5 * (bbox.getP11() + bbox.getP00()));
1882   }
1883 
1884   ++m_selectionCount;
1885 }
1886 
1887 //-----------------------------------------------------------------------------
1888 
selectStroke(int index,bool toggle)1889 bool VectorSelectionTool::selectStroke(int index, bool toggle) {
1890   TVectorImageP vi = getImage(false);
1891   assert(vi);
1892   assert(m_strokeSelection.getImage() == vi);
1893 
1894   if (!vi->isEnteredGroupStroke(index))  // If index is not in current group
1895     return false;
1896 
1897   if (index < 0 || index >= int(vi->getStrokeCount()))  // Should be asserted...
1898     return false;
1899 
1900   bool wasSelected = m_strokeSelection.isSelected(index),
1901        selectState = !(wasSelected && toggle);
1902 
1903   // (De)Select additional strokes related to ours
1904   std::set<int> &selectedStyles = this->selectedStyles();
1905 
1906   if (isSameStyleType())  // Color selection
1907   {
1908     TStroke *refStroke = vi->getStroke(index);
1909     assert(refStroke);
1910 
1911     int style = refStroke->getStyle();
1912 
1913     if (selectState)
1914       selectedStyles.insert(style);
1915     else
1916       selectedStyles.erase(style);
1917   } else if (vi->isStrokeGrouped(index) &&
1918              vi->selectable(index))  // Group selection
1919   {
1920     int s, sCount = vi->getStrokeCount();
1921     for (s = 0; s != sCount; ++s) {
1922       if (vi->selectable(s) && vi->sameSubGroup(index, s))
1923         m_strokeSelection.select(s, selectState);
1924     }
1925   } else  // Single stroke selection
1926     m_strokeSelection.select(index, selectState);
1927 
1928   return (selectState != wasSelected);
1929 }
1930 
1931 //-----------------------------------------------------------------------------
1932 
onActivate()1933 void VectorSelectionTool::onActivate() {
1934   if (m_firstTime) {
1935     m_includeIntersection.setValue(l_strokeSelectIncludeIntersection ? 1 : 0);
1936     m_constantThickness.setValue(l_strokeSelectConstantThickness ? 1 : 0);
1937     m_strokeSelection.setSceneHandle(
1938         TTool::getApplication()->getCurrentScene());
1939   }
1940 
1941   SelectionTool::onActivate();
1942 }
1943 
1944 //-----------------------------------------------------------------------------
1945 
onDeactivate()1946 void VectorSelectionTool::onDeactivate() {
1947   if (isLevelType()) return;
1948 
1949   SelectionTool::onDeactivate();
1950 }
1951 
1952 //-----------------------------------------------------------------------------
1953 
doOnActivate()1954 void VectorSelectionTool::doOnActivate() {
1955   TVectorImageP vi = getImage(false);
1956   m_strokeSelection.setImage(vi);
1957 
1958   updateSelectionTarget();
1959 
1960   finalizeSelection();
1961   invalidate();
1962 }
1963 
1964 //-----------------------------------------------------------------------------
1965 
onImageChanged()1966 void VectorSelectionTool::onImageChanged() {
1967   TVectorImageP vi          = getImage(false);
1968   TVectorImageP selectedImg = m_strokeSelection.getImage();
1969 
1970   if (vi != selectedImg) {
1971     m_strokeSelection.selectNone();
1972     m_strokeSelection.setImage(vi);
1973 
1974     if (!(vi && selectedImg)  // Retain the styles selection ONLY
1975         || vi->getPalette() !=
1976                selectedImg->getPalette())  // if palettes still match
1977       selectedStyles().clear();
1978   } else {
1979     // Remove any eventual stroke index outside the valid range
1980     if (!m_strokeSelection.isEmpty()) {
1981       const std::set<int> &indexes = m_strokeSelection.getSelection();
1982       int strokesCount             = selectedImg->getStrokeCount();
1983 
1984       std::set<int>::const_iterator it;
1985       for (it = indexes.begin(); it != indexes.end(); ++it) {
1986         int index = *it;
1987 
1988         if (index >= strokesCount) m_strokeSelection.select(index, false);
1989       }
1990     }
1991   }
1992 
1993   finalizeSelection();
1994 }
1995 
1996 //-----------------------------------------------------------------------------
1997 
isDragging() const1998 bool VectorSelectionTool::isDragging() const {
1999   return m_enabled && m_leftButtonMousePressed;
2000 }
2001 
2002 //-----------------------------------------------------------------------------
2003 
doOnDeactivate()2004 void VectorSelectionTool::doOnDeactivate() {
2005   m_strokeSelection.selectNone();
2006   m_levelSelection.selectNone();
2007   m_deformValues.reset();
2008 
2009   m_polyline.clear();
2010 
2011   TTool::getApplication()->getCurrentSelection()->setSelection(0);
2012 
2013   invalidate();
2014 }
2015 
2016 //-----------------------------------------------------------------------------
2017 
getProperties(int idx)2018 TPropertyGroup *VectorSelectionTool::getProperties(int idx) {
2019   switch (idx) {
2020   case 0:
2021     return &m_prop;
2022   case 1:
2023     return &m_outlineProps;
2024   default:
2025     return 0;
2026   };
2027 }
2028 
2029 //-----------------------------------------------------------------------------
2030 
onPropertyChanged(std::string propertyName)2031 bool VectorSelectionTool::onPropertyChanged(std::string propertyName) {
2032   if (!m_strokeSelection.isEditable()) return false;
2033 
2034   if (SelectionTool::onPropertyChanged(propertyName)) return true;
2035 
2036   if (propertyName == m_includeIntersection.getName())
2037     l_strokeSelectIncludeIntersection = (int)(m_includeIntersection.getValue());
2038   if (propertyName == m_constantThickness.getName())
2039     l_strokeSelectConstantThickness = (int)(m_constantThickness.getValue());
2040   else if (propertyName == m_selectionTarget.getName())
2041     doOnActivate();
2042   else if (propertyName == m_capStyle.getName()) {
2043     if (m_strokeSelection.isEmpty()) return true;
2044 
2045     TXshSimpleLevel *level =
2046         TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
2047     UndoChangeOutlineStyle *undo =
2048         new UndoChangeOutlineStyle(level, getCurrentFid(), this);
2049 
2050     int newCapStyle = m_capStyle.getIndex();
2051 
2052     TVectorImageP vi = m_strokeSelection.getImage();
2053 
2054     const std::set<int> &indices = m_strokeSelection.getSelection();
2055 
2056     std::set<int>::iterator it;
2057     for (it = indices.begin(); it != indices.end(); ++it) {
2058       TStroke *stroke = vi->getStroke(*it);
2059 
2060       stroke->outlineOptions().m_capStyle =
2061           (TStroke::OutlineOptions::CapStyle)newCapStyle;
2062       stroke->invalidate();
2063     }
2064 
2065     computeBBox();
2066     invalidate();
2067 
2068     level->setDirtyFlag(true);
2069 
2070     undo->registerStrokes();
2071     TUndoManager::manager()->add(undo);
2072 
2073     notifyImageChanged();
2074   } else if (propertyName == m_joinStyle.getName()) {
2075     if (m_strokeSelection.isEmpty()) return true;
2076 
2077     TXshSimpleLevel *level =
2078         TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
2079     UndoChangeOutlineStyle *undo =
2080         new UndoChangeOutlineStyle(level, getCurrentFid(), this);
2081 
2082     int newJoinStyle = m_joinStyle.getIndex();
2083 
2084     TVectorImageP vi = m_strokeSelection.getImage();
2085 
2086     const std::set<int> &indices = m_strokeSelection.getSelection();
2087 
2088     std::set<int>::iterator it;
2089     for (it = indices.begin(); it != indices.end(); ++it) {
2090       TStroke *stroke = vi->getStroke(*it);
2091 
2092       stroke->outlineOptions().m_joinStyle =
2093           (TStroke::OutlineOptions::JoinStyle)newJoinStyle;
2094       stroke->invalidate();
2095     }
2096 
2097     computeBBox();
2098     invalidate();
2099 
2100     level->setDirtyFlag(true);
2101 
2102     undo->registerStrokes();
2103     TUndoManager::manager()->add(undo);
2104 
2105     notifyImageChanged();
2106   } else if (propertyName == m_miterJoinLimit.getName()) {
2107     if (m_strokeSelection.isEmpty()) return true;
2108 
2109     TXshSimpleLevel *level =
2110         TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
2111     UndoChangeOutlineStyle *undo =
2112         new UndoChangeOutlineStyle(level, getCurrentFid(), this);
2113 
2114     int upper = m_miterJoinLimit.getValue();
2115 
2116     TVectorImageP vi = m_strokeSelection.getImage();
2117 
2118     const std::set<int> &indices = m_strokeSelection.getSelection();
2119 
2120     std::set<int>::iterator it;
2121     for (it = indices.begin(); it != indices.end(); ++it) {
2122       TStroke *stroke = vi->getStroke(*it);
2123 
2124       stroke->outlineOptions().m_miterUpper = upper;
2125       stroke->invalidate();
2126     }
2127 
2128     computeBBox();
2129     invalidate();
2130 
2131     level->setDirtyFlag(true);
2132 
2133     undo->registerStrokes();
2134     TUndoManager::manager()->add(undo);
2135 
2136     notifyImageChanged();
2137   } else
2138     return false;
2139 
2140   return true;
2141 }
2142 
2143 //-----------------------------------------------------------------------------
2144 
selectionOutlineStyle(int & capStyle,int & joinStyle)2145 void VectorSelectionTool::selectionOutlineStyle(int &capStyle, int &joinStyle) {
2146   // Check the selection's outline style group validity
2147   const std::set<int> &selection = m_strokeSelection.getSelection();
2148   if (selection.empty()) {
2149     capStyle = joinStyle = -1;
2150     return;
2151   }
2152 
2153   TVectorImageP vi = m_strokeSelection.getImage();
2154   const TStroke::OutlineOptions &beginOptions =
2155       vi->getStroke(*selection.begin())->outlineOptions();
2156 
2157   capStyle  = beginOptions.m_capStyle;
2158   joinStyle = beginOptions.m_joinStyle;
2159 
2160   std::set<int>::const_iterator it;
2161   for (it = selection.begin(); it != selection.end(); ++it) {
2162     const TStroke::OutlineOptions &options =
2163         vi->getStroke(*it)->outlineOptions();
2164 
2165     if (capStyle != options.m_capStyle) capStyle = -1;
2166     if (joinStyle != options.m_joinStyle) joinStyle = -1;
2167     if (capStyle < 0 && joinStyle < 0) return;
2168   }
2169 }
2170 
2171 //-----------------------------------------------------------------------------
2172 
selectRegionVectorImage(bool includeIntersect)2173 void VectorSelectionTool::selectRegionVectorImage(bool includeIntersect) {
2174   if (!m_stroke) return;
2175 
2176   TVectorImageP vi(getImage(false));
2177   if (!vi) return;
2178 
2179   m_strokeSelection.setImage(vi);
2180 
2181   TVectorImage selectImg;
2182   selectImg.addStroke(new TStroke(*m_stroke));
2183   selectImg.findRegions();
2184 
2185   int sCount = int(vi->getStrokeCount()),
2186       rCount = int(selectImg.getRegionCount());
2187 
2188   bool selectionChanged = false;
2189 
2190   for (int s = 0; s != sCount; ++s) {
2191     TStroke *currentStroke = vi->getStroke(s);
2192 
2193     for (int r = 0; r != rCount; ++r) {
2194       TRegion *region = selectImg.getRegion(r);
2195 
2196       if (region->contains(*currentStroke, true))
2197         selectionChanged = selectStroke(s, false) || selectionChanged;
2198     }
2199 
2200     if (includeIntersect) {
2201       std::vector<DoublePair> intersections;
2202       intersect(m_stroke, currentStroke, intersections, false);
2203       if (intersections.size() > 0) {
2204         selectionChanged = selectStroke(s, false) || selectionChanged;
2205       }
2206     }
2207   }
2208 
2209   if (selectionChanged) {
2210     finalizeSelection();
2211     notifySelectionChanged();
2212     invalidate();
2213   }
2214 }
2215 
2216 //-----------------------------------------------------------------------------
2217 
updateAction(TPointD pos,const TMouseEvent & e)2218 void VectorSelectionTool::updateAction(TPointD pos, const TMouseEvent &e) {
2219   TImageP image    = getImage(false);
2220   TVectorImageP vi = (TVectorImageP)image;
2221   if (!vi) return;
2222 
2223   SelectionTool::updateAction(pos, e);
2224   if (m_what != Outside || m_cursorId != ToolCursor::StrokeSelectCursor) return;
2225 
2226   if (!m_strokeSelection.isEditable()) return;
2227 
2228   FourPoints bbox = getBBox();
2229   UINT index      = 0;
2230 
2231   if ((isLevelType() &&
2232        bbox.contains(pos))  // What about isSelectedFramesType()??
2233       || (getStrokeIndexFromPos(index, vi, pos, getPixelSize(),
2234                                 getViewer()->getViewMatrix()) &&
2235           m_strokeSelection.isSelected(index))) {
2236     m_what = Inside;
2237     m_cursorId =
2238         isLevelType() ? ToolCursor::LevelSelectCursor : ToolCursor::MoveCursor;
2239   }
2240 }
2241 
2242 //-----------------------------------------------------------------------------
2243 
onSelectedFramesChanged()2244 void VectorSelectionTool::onSelectedFramesChanged() {
2245   if (isSelectedFramesType())  // False also in case m_levelSelection is not
2246                                // current
2247     finalizeSelection();
2248 }
2249