1 
2 
3 #include "tools/levelselection.h"
4 
5 // TnzTools includes
6 #include "tools/tool.h"
7 #include "tools/toolhandle.h"
8 
9 // TnzCore includes
10 #include "tfilepath.h"
11 #include "tvectorimage.h"
12 #include "tstroke.h"
13 #include "tregion.h"
14 
15 // Boost includes
16 #include <boost/iterator/counting_iterator.hpp>
17 #include <boost/bind.hpp>
18 
19 //*******************************************************************************
20 //    Local namespace  stuff
21 //*******************************************************************************
22 
23 namespace {
24 
tool()25 TTool *tool() { return TTool::getApplication()->getCurrentTool()->getTool(); }
26 
27 //========================================================================
28 
29 struct StrokeData {
30   UCHAR m_hasColor, m_hasRegion;
31 };
32 
getBoundaries(TVectorImage & vi,std::vector<int> & strokes)33 void getBoundaries(TVectorImage &vi, std::vector<int> &strokes) {
34   enum { FORWARD = 0x1, BACKWARD = 0x2, INTERNAL = FORWARD | BACKWARD };
35 
36   struct locals {
37     static inline bool isBoundary(const std::vector<StrokeData> &sData,
38                                   UINT s) {
39       return (sData[s].m_hasColor != INTERNAL);
40     }
41 
42     static void markEdges(const TRegion &region, std::vector<StrokeData> &sData,
43                           bool parentRegionHasColor) {
44       bool regionHasColor = (region.getStyle() != 0);
45 
46       // Traverse region edges, marking associated strokes accordingly
47       UINT e, eCount = region.getEdgeCount();
48       for (e = 0; e != eCount; ++e) {
49         const TEdge &ed = *region.getEdge(e);
50         assert(ed.m_s);
51 
52         int strokeIdx = ed.m_index;
53         if (strokeIdx >= 0)  // Could be <0 in case the corresponding
54         {                    // stroke is a region 'closure' (autoclose)
55           assert(0 <= strokeIdx && strokeIdx < sData.size());
56 
57           StrokeData &sd = sData[strokeIdx];
58 
59           UCHAR side = (ed.m_w1 > ed.m_w0) ? FORWARD : BACKWARD;
60 
61           sd.m_hasRegion |= side;
62           if (regionHasColor) sd.m_hasColor |= side;
63         }
64       }
65 
66       if (parentRegionHasColor) {
67         // Mark non-region edge sides with color
68         for (e = 0; e != eCount; ++e) {
69           const TEdge &ed = *region.getEdge(e);
70           assert(ed.m_s);
71 
72           int strokeIdx = ed.m_index;
73           if (strokeIdx >= 0) {
74             StrokeData &sd = sData[strokeIdx];
75             sd.m_hasColor |= (INTERNAL & ~sd.m_hasRegion);
76           }
77         }
78       }
79 
80       // Mark recursively on sub-regions
81       UINT sr, srCount = region.getSubregionCount();
82       for (sr = 0; sr != srCount; ++sr)
83         markEdges(*region.getSubregion(sr), sData, regionHasColor);
84     }
85   };  // locals
86 
87   std::vector<StrokeData> sData(vi.getStrokeCount());
88 
89   // Traverse regions, mark each stroke edge with the side a COLORED region is
90   // on
91   UINT r, rCount = vi.getRegionCount();
92   for (r = 0; r != rCount; ++r)
93     locals::markEdges(*vi.getRegion(r), sData, false);
94 
95   // Strokes not appearing as region edges must be checked for region inclusion
96   // separately
97   UINT s, sCount = vi.getStrokeCount();
98   for (s = 0; s != sCount; ++s) {
99     if (!sData[s].m_hasRegion) {
100       TRegion *parentRegion = vi.getRegion(vi.getStroke(s)->getPoint(0.5));
101 
102       if (parentRegion && parentRegion->getStyle())
103         sData[s].m_hasColor = INTERNAL;
104     }
105   }
106 
107   // Output all not internal regions
108   std::copy_if(boost::make_counting_iterator(0u),
109                boost::make_counting_iterator(vi.getStrokeCount()),
110                std::back_inserter(strokes),
111                boost::bind(locals::isBoundary, sData, _1));
112 }
113 
114 }  // namespace
115 
116 //*******************************************************************************
117 //    VectorLevelSelection  implementation
118 //*******************************************************************************
119 
LevelSelection()120 LevelSelection::LevelSelection() : m_framesMode(FRAMES_NONE), m_filter(EMPTY) {}
121 
122 //---------------------------------------------------------------------
123 
isEmpty() const124 bool LevelSelection::isEmpty() const {
125   return (m_framesMode == FRAMES_NONE || m_filter == EMPTY);
126 }
127 
128 //---------------------------------------------------------------------
129 
selectNone()130 void LevelSelection::selectNone() {
131   m_framesMode = FRAMES_NONE;
132   m_filter     = EMPTY;
133 
134   m_styles.clear();
135 }
136 
137 //*******************************************************************************
138 //    Related standalone functions
139 //*******************************************************************************
140 
getBoundaryStrokes(TVectorImage & vi)141 std::vector<int> getBoundaryStrokes(TVectorImage &vi) {
142   std::vector<int> result;
143   getBoundaries(vi, result);
144 
145   return result;
146 }
147 
148 //------------------------------------------------------------------------
149 
getSelectedStrokes(TVectorImage & vi,const LevelSelection & levelSelection)150 std::vector<int> getSelectedStrokes(TVectorImage &vi,
151                                     const LevelSelection &levelSelection) {
152   struct locals {
153     static void selectStyles(const TVectorImage &vi,
154                              const std::set<int> &styles,
155                              std::vector<int> &strokes) {
156       UINT s, sCount = vi.getStrokeCount();
157       for (s = 0; s != sCount; ++s) {
158         if (styles.count(vi.getStroke(s)->getStyle())) strokes.push_back(s);
159       }
160     }
161   };  // locals
162 
163   std::vector<int> strokes;
164 
165   switch (levelSelection.filter()) {
166   case LevelSelection::EMPTY:
167     break;
168   case LevelSelection::WHOLE:
169     strokes.assign(boost::make_counting_iterator(0u),
170                    boost::make_counting_iterator(vi.getStrokeCount()));
171     break;
172   case LevelSelection::SELECTED_STYLES:
173     locals::selectStyles(vi, levelSelection.styles(), strokes);
174     break;
175   case LevelSelection::BOUNDARY_STROKES:
176     getBoundaries(vi, strokes);
177     break;
178   }
179 
180   return strokes;
181 }
182