GJK_nD
 All Classes Files Functions Typedefs Friends Pages
Segment.hpp
1 /*
2  * Copyright (c) 2012 Laurent Provot <provot.research@gmail.com>,
3  * Yan Gerard <yan.gerard@free.fr> and Fabien Feschet <research@feschet.fr>
4  * All rights reserved.
5  *
6  * This is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef DLL_SEGMENT_HPP
21 #define DLL_SEGMENT_HPP
22 
23 #include <vector>
24 #include <utility>
25 #include <iostream>
26 #include "GJK_nD.hpp"
27 
28 namespace DLL {
29 
56 template <typename DLL_Model>
57 class Segment
58 {
59 public:
61  typedef std::pair<size_t, size_t> Coordinates;
63  typedef std::vector<Coordinates> Curve;
64 
65 public:
69  Segment() : myIsValid(false), myCanGrow(true) {}
70 
77  bool addPoint(const Coordinates & xy);
78 
83  bool isValid() const { return myIsValid; }
84 
88  const Curve & getCurve() const { return myCurve; }
89 
93  template <typename U>
94  friend std::ostream & operator<<(std::ostream & out, const Segment<U> & dll);
95 
97  // Used for test purpose only
98  const Curve & getUpCurve() const { return myUpNeighbors; }
100 
102  // Used for test purpose only
103  const Curve & getDownCurve() const { return myDownNeighbors; }
105 
106 private:
107  template <int N>
108  void updateConnectedUpDownCurves();
109  unsigned int computeFreemanCode(const Coordinates & c1,
110  const Coordinates & c2) const;
111 
112 private:
113  static const int xFromFreeman[];
114  static const int yFromFreeman[];
115 
116 private:
117  bool myIsValid;
118  bool myCanGrow;
119 
120  Curve myCurve;
121  Curve myUpNeighbors;
122  Curve myDownNeighbors;
123 
124  DLL_Model myUnderliyingModel;
125 };
126 
127 
128 template <typename DLL_Model>
129 const int
130 Segment<DLL_Model>::xFromFreeman[] = {1, 1, 0, -1, -1, -1, 0, 1};
131 
132 template <typename DLL_Model>
133 const int
134 Segment<DLL_Model>::yFromFreeman[] = {0, 1 ,1, 1, 0, -1, -1, -1};
135 
136 
137 template <typename DLL_Model>
139 {
140  // we need at least two points in the curve in order to start
141  // the recognition process at the next iteration
142  if (myCurve.size() < 2) {
143  myCurve.push_back(xy);
144  myUnderliyingModel.addInPoint(xy);
145  if (myCurve.size() == 2)
146  myIsValid = true;
147  return true;
148  }
149  else {
150  if (myIsValid && myCanGrow) {
151  myCurve.push_back(xy);
152  myUnderliyingModel.addInPoint(xy);
153  updateConnectedUpDownCurves<4>();
154  if (myUnderliyingModel.stillGrowableAfterUpdate())
155  return true;
156 
157  // we pop the last point we added
158  // we mark this DLL segment as ungrowable to stop the recognition process
159  myCurve.pop_back();
160  myCanGrow = false;
161  }
162  }
163 
164  return false;
165 }
166 
170 template <typename DLL_Model>
171 unsigned int
172 Segment<DLL_Model>::computeFreemanCode(const Coordinates & c1,
173  const Coordinates & c2) const
174 {
175  /* the possible configurations of the vector between point c1 and point c2
176  can be described with the freeman code depicted below
177 
178  3 2 1
179  \ | /
180  \|/
181  4 ---X--- 0
182  /|\
183  / | \
184  5 6 7
185  */
186  int diffX = static_cast<int>(c2.first - c1.first );
187  int diffY = static_cast<int>(c2.second - c1.second);
188 
189  for (size_t i = 0; i < 8; ++i)
190  if (diffX == xFromFreeman[i] && diffY == yFromFreeman[i])
191  return i;
192 
193  // should never go there
194  throw;
195 }
196 
197 
203 template <typename DLL_Model>
204 template <int N>
205 void Segment<DLL_Model>::updateConnectedUpDownCurves()
206 {
207  size_t size = myCurve.size();
208  if (size < 3)
209  //there is not enough points
210  return;
211 
212  // we work with the 3 last points of the curve
213  const Coordinates & lastPoint = myCurve[--size];
214  const Coordinates & currentPoint = myCurve[--size];
215  const Coordinates & previousPoint = myCurve[--size];
216 
217  unsigned int freemanEnter = computeFreemanCode(previousPoint, currentPoint);
218  unsigned int freemanExit = computeFreemanCode(currentPoint, lastPoint);
219 
220  if ((freemanExit + 8 - freemanEnter) % 4 == 0 &&
221  freemanExit != freemanEnter) {
222  // Stationnary point: the best solution is to put a common point in
223  // Up and Down which makes stop the GJK_nD algorithm
224  myDownNeighbors.push_back(lastPoint);
225  myUnderliyingModel.addDownPoint(lastPoint);
226  myUpNeighbors.push_back(lastPoint);
227  myUnderliyingModel.addUpPoint(lastPoint);
228 
229  return;
230  }
231 
232  Coordinates coord;
233  // update myDownNeighbors first
234  size_t setSize = myDownNeighbors.size();
235  for (unsigned int i = (freemanEnter + 5) % 8;
236  i != freemanExit; i = (i + 1) % 8)
237  if (N == 8 || i % 2 == 0) {
238  coord = Coordinates(currentPoint.first + xFromFreeman[i],
239  currentPoint.second + yFromFreeman[i]);
240  if (setSize == 0 ||
241  (coord != myDownNeighbors.back() &&
242  (setSize == 1 || coord != myDownNeighbors[setSize - 2]))) {
243  myDownNeighbors.push_back(coord);
244  ++setSize;
245  myUnderliyingModel.addDownPoint(coord);
246  }
247  }
248 
249  // and now update myUpNeighbors
250  setSize = myUpNeighbors.size();
251  for (unsigned int i = (freemanEnter + 3) % 8;
252  i != freemanExit; i = (i + 7) % 8)
253  if (N == 8 || i % 2 == 0) {
254  coord = Coordinates(currentPoint.first + xFromFreeman[i],
255  currentPoint.second + yFromFreeman[i]);
256  if (setSize == 0 ||
257  (coord != myUpNeighbors.back() &&
258  (setSize == 1 || coord != myUpNeighbors[setSize - 2]))) {
259  myUpNeighbors.push_back(coord);
260  ++setSize;
261  myUnderliyingModel.addUpPoint(coord);
262  }
263  }
264 }
265 
266 
267 template <typename DLL_Model>
268 std::ostream & operator<<(std::ostream & out, const Segment<DLL_Model> & dll)
269 {
270  if (dll.isValid())
271  out << dll.myUnderliyingModel;
272  else
273  out << "Non valid DLL";
274 
275  return out;
276 }
277 
278 } // namespace DLL
279 
280 #endif // DLL_SEGMENT_HPP