CLAM-Development  1.4.0
DiscontinuousSegmentation.hxx
Go to the documentation of this file.
1 #ifndef DiscontinuousSegmentation_hxx
2 #define DiscontinuousSegmentation_hxx
3 
4 #include "Segmentation.hxx"
5 
6 namespace CLAM
7 {
9  {
10  public:
11  class InsertedOutOfBounds : public std::exception
12  {
13  public:
14  const char * what() const throw () { return "Segmentation point inserted out of limits";}
15  };
16  class OffsetMissing : public std::exception
17  {
18  public:
19  const char * what() const throw () { return "Odd number of segmentation points, every segment beggining must be followed by its ending";}
20  };
21  class MissplacedOnset : public std::exception
22  {
23  std::string _message;
24  public:
25  MissplacedOnset(unsigned missplacedOnset,
26  double previousOffsetPosition,
27  double intendedOnsetPosition)
28  {
29  std::ostringstream os;
30  os << "Segment " << missplacedOnset
31  << " starts at " << intendedOnsetPosition
32  << " overlapping previous segment which ends at " << previousOffsetPosition;
33  _message = os.str();
34  }
35  virtual ~MissplacedOnset() throw () {}
36  const char * what() const throw () { return _message.c_str(); }
37  };
38  class MissplacedOffset : public std::exception
39  {
40  std::string _message;
41  public:
42  MissplacedOffset(unsigned missplacedOffset,
43  double onsetPosition,
44  double offsetPosition)
45  {
46  std::ostringstream os;
47  os << "Segment " << missplacedOffset
48  << " starts at " << onsetPosition
49  << " but ends before that, at " << offsetPosition;
50  _message = os.str();
51  }
52  virtual ~MissplacedOffset() throw () {}
53  const char * what() const throw () { return _message.c_str(); }
54  };
55  typedef std::vector<double> TimePositions;
56  public:
59  {
60  }
67  DiscontinuousSegmentation(double maxPosition, const TData * begin, const TData * end)
68  : Segmentation(maxPosition)
69  {
70  takeArray(begin, end);
71  }
75  void takeArray(const TData * begin, const TData * end)
76  {
77  double previousOffset=0.0;
78  unsigned i=0;
79  for (const TData* it=begin; it!=end; i++)
80  {
81  double onset = *it++;
82  std::cout << onset << " " << std::flush;
83  if (onset<previousOffset) throw MissplacedOnset(i,previousOffset,onset);
84  if (it==end) throw OffsetMissing();
85  double offset = *it++;
86  std::cout << offset << " " << std::flush;
87  if (offset<onset) throw MissplacedOffset(i, onset, offset);
88  if (offset>_maxPosition) throw InsertedOutOfBounds(); //_maxPosition
89  _onsets.push_back(onset);
90  _offsets.push_back(offset);
91  _labels.push_back(""); // TODO: a constructor with not empty labels
92  _selection.push_back(false);
93  previousOffset=offset;
94  }
95  }
99  unsigned insert(double timePosition)
100  {
101  if (timePosition<0.0) throw InsertedOutOfBounds();
102  if (timePosition>_maxPosition) throw InsertedOutOfBounds();
103  TimePositions::iterator nextOffset =
104  std::lower_bound(_offsets.begin(), _offsets.end(), timePosition);
105  if (nextOffset == _offsets.end()) // Beyond any existing segment
106  {
107  _onsets.push_back(timePosition);
108  _offsets.push_back(_maxPosition);
109  _labels.push_back("");
110  _selection.push_back(false);
111  return _onsets.size()-1;
112  }
113  // 'nextOffsetPosition' must be computed before the insertion to not invalidate iterators.
114  unsigned nextOffsetPosition = nextOffset - _offsets.begin();
115  if (_onsets[nextOffsetPosition]<=timePosition) // Just in the middle of a segment
116  {
117  _offsets.insert(nextOffset, timePosition);
118  _onsets.insert(_onsets.begin()+nextOffsetPosition+1, timePosition);
119  _labels.insert(_labels.begin()+nextOffsetPosition+1, "");
120  _selection.insert(_selection.begin()+nextOffsetPosition+1, false);
121  if (nextOffsetPosition<_current) _current++;
122  return nextOffsetPosition+1;
123  }
124  else // In a gap before a segment
125  {
126  _offsets.insert(nextOffset, _onsets[nextOffsetPosition]);
127  _onsets.insert(_onsets.begin()+nextOffsetPosition, timePosition);
128  _labels.insert(_labels.begin()+nextOffsetPosition, "");
129  _selection.insert(_selection.begin()+nextOffsetPosition, false);
130  if (_current>=nextOffsetPosition) _current++;
131  return nextOffsetPosition;
132  }
133 
134  }
138  unsigned insert(double timePosition, std::string label)
139  {
140  unsigned segment = insert(timePosition);
141  setLabel(segment, label);
142  return segment;
143  }
150  void remove(unsigned segment)
151  {
152  _offsets.erase(_offsets.begin()+segment);
153  _onsets.erase(_onsets.begin()+segment);
154  _labels.erase(_labels.begin()+segment);
155  _selection.erase(_selection.begin()+segment);
156  if (_current!=0 && segment<=_current) _current--;
157  }
164  unsigned pickOffset(double timePosition, double tolerance) const
165  {
166  return pickPosition(_offsets, timePosition, tolerance);
167  }
174  unsigned pickOnset(double timePosition, double tolerance) const
175  {
176  return pickPosition(_onsets, timePosition, tolerance);
177  }
181  unsigned pickSegmentBody(double timePosition) const
182  {
183  if (timePosition<0) return _offsets.size();
184  TimePositions::const_iterator lowerBound =
185  std::lower_bound(_offsets.begin(), _offsets.end(), timePosition);
186  unsigned index = lowerBound-_offsets.begin();
187  if (index==_offsets.size()) return index;
188  if (_onsets[index]>timePosition) return _offsets.size();
189  return index;
190  }
196  void dragOnset(unsigned segment, double newTimePosition)
197  {
198  // The onset is attached to the previous offset
199  if (segment>=_onsets.size()) return; // Invalid segment
200 
201  // Limit to the left to the previous onset or 0
202  double leftBound = segment ? _offsets[segment-1] : 0;
203  if (newTimePosition<leftBound)
204  newTimePosition=leftBound;
205  // Limit to the right to the own offset
206  double rigthBound = _offsets[segment];
207  if (newTimePosition>rigthBound)
208  newTimePosition=rigthBound;
209 
210  // The offset and the next onset change together
211  _onsets[segment]=newTimePosition;
212  }
218  void dragOffset(unsigned segment, double newTimePosition)
219  {
220  if (segment>=_offsets.size()) return; // Invalid segment
221 
222  // Limit to the right to the next offset or max
223  double rigthBound = segment+1==_offsets.size()? _maxPosition : _onsets[segment+1];
224  if (newTimePosition>rigthBound)
225  newTimePosition=rigthBound;
226  // Limit to the left to the own onset
227  double leftBound = _onsets[segment];
228  if (newTimePosition<leftBound)
229  newTimePosition=leftBound;
230 
231  // The offset and the next onset change together
232  _offsets[segment]=newTimePosition;
233  }
237  void fillArray(DataArray& segmentation) const
238  {
239  unsigned nSegments = _onsets.size();
240  segmentation.Resize(nSegments*2);
241  segmentation.SetSize(nSegments*2);
242  for (unsigned i=0; i<nSegments; i++)
243  {
244  segmentation[i*2] = _onsets[i];
245  segmentation[i*2+1] = _offsets[i];
246  }
247  }
248 
249  const char * GetClassName() const { return "DiscontinuousSegmentation"; }
250 
251 
252  private:
260  unsigned pickPosition(const TimePositions & positions, double timePosition, double tolerance) const
261  {
262  TimePositions::const_iterator lowerBound =
263  std::lower_bound(positions.begin(), positions.end(), timePosition-tolerance);
264  TimePositions::const_iterator upperBound =
265  std::upper_bound(lowerBound, positions.end(), timePosition+tolerance);
266 
267  if (lowerBound==upperBound) return positions.size(); // None found
268 
269  // Pick the closest in range
270  unsigned lowerSegment = lowerBound - positions.begin();
271  unsigned upperSegment = upperBound - positions.begin();
272  double lastDifference = std::fabs(timePosition-positions[lowerSegment]);
273  for (unsigned i=lowerSegment; i<upperSegment; i++)
274  {
275  double newDifference = std::fabs(timePosition-positions[i]);
276  if (newDifference>lastDifference) break;
277  lastDifference = newDifference;
278  lowerSegment = i;
279  }
280  return lowerSegment;
281  }
282  };
283 
284 }
285 
286 
287 
288 #endif//DiscontinuousSegmentation_hxx
289