Gnash  0.8.11dev
snappingrange.h
Go to the documentation of this file.
1 //
2 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
3 // Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 //
19 
20 #ifndef GNASH_SNAPPINGRANGE_H
21 #define GNASH_SNAPPINGRANGE_H
22 
23 #include "Range2d.h"
24 
25 #include <vector>
26 #include <iterator>
27 #include <algorithm>
28 #include <ostream>
29 #include <cstdint>
30 
31 namespace gnash {
32 
33 namespace geometry {
34 
39 //
69 
70 // Forward declarations.
71 namespace {
72 
74  template<typename T> inline bool snaptest(
75  const geometry::Range2d<T>& range1,
76  const geometry::Range2d<T>& range2, const float snapFactor);
77 }
78 
79 template <typename T>
81 {
82 public:
84  typedef std::vector<RangeType> RangeList;
85  typedef typename RangeList::size_type size_type;
86 
87  template<typename U> friend std::ostream& operator<<(std::ostream& os,
88  const SnappingRanges2d<U>& r);
89 
91  :
92  _snapFactor(1.3f),
93  _singleMode(false),
94  _rangesLimit(50),
95  _combineCounter(0)
96  {
97  }
98 
100  template <typename U>
102  :
103  _snapFactor(from.getSnapFactor()),
104  _singleMode(from.getSingleMode()),
105  _rangesLimit(from.getRangeCountLimit()),
106  _combineCounter(0)
107  {
108  if (from.isWorld()) setWorld();
109  else if (from.isNull()) setNull();
110  else {
111  // TODO: can we safely assume that the 'from' parameter was
112  // finalized ?
113 
114  // TODO: use visitor pattern !
115  for (size_type i = 0, e = from.size(); i != e; ++i) {
116  const Range2d<U>& r = from.getRange(i);
117  RangeType rc(r);
118  add(rc);
119  }
120  }
121  }
122 
125  void setSnapFactor(const float factor) {
126  assert(factor > 1.0f);
127  _snapFactor = factor;
128  }
129 
130  float getSnapFactor() const {
131  return _snapFactor;
132  }
133 
135  void setSingleMode(const bool mode) {
136  _singleMode = mode;
137  }
138 
139  bool getSingleMode() const {
140  return _singleMode;
141  }
142 
145  void setRangeCountLimit(const size_type limit) {
146  _rangesLimit = limit;
147  }
148 
149  size_type getRangeCountLimit() const {
150  return _rangesLimit;
151  }
152 
155  void inheritConfig(const SnappingRanges2d<T>& from) {
156  _snapFactor = from._snapFactor;
157  _singleMode = from._singleMode;
158  }
159 
162  {
163  public:
164  ExpandToIfSnap(const RangeType& rt, const float snapFactor)
165  :
166  _rt(rt),
167  _snapFactor(snapFactor)
168  {}
169 
170  bool operator()(RangeType& r) {
171  if (snaptest(r, _rt, _snapFactor)) {
172  r.expandTo(_rt);
173  return false;
174  }
175  return true;
176  }
177  private:
178  const RangeType& _rt;
179  const float _snapFactor;
180  };
181 
182  class Scale
183  {
184  public:
185  Scale(const float scale) : _scale(scale) {}
186  void operator()(RangeType& r) {
187  r.scale(_scale);
188  }
189  private:
190  const float _scale;
191  };
192 
193  class GrowBy
194  {
195  public:
196  GrowBy(const float factor) : _factor(factor) {}
197  void operator()(RangeType& r) {
198  r.growBy(_factor);
199  }
200  private:
201  const float _factor;
202  };
203 
204  class AddTo
205  {
206  public:
207  AddTo(SnappingRanges2d<T>& us) : _this(us) {}
208  void operator()(const RangeType& r) {
209  _this.add(r);
210  }
211  private:
212  SnappingRanges2d<T>& _this;
213  };
214 
216  {
217  public:
218  IntersectsRange(const RangeType& range) : _range(range) {}
219  bool operator()(const RangeType& us) {
220  return us.intersects(_range);
221  }
222  private:
223  const RangeType& _range;
224  };
225 
227  {
228  public:
229  ContainsPoint(const T x, const T y) : _x(x), _y(y) {}
230  bool operator()(const RangeType& us) {
231  return us.contains(_x, _y);
232  }
233  private:
234  const T _x, _y;
235  };
236 
238  {
239  public:
240  ContainsRange(const RangeType& range) : _range(range) {}
241  bool operator()(const RangeType& us) {
242  return us.contains(_range);
243  }
244  private:
245  const RangeType& _range;
246  };
247 
248 
250  void add(const RangeType& range) {
251  if (range.isWorld()) {
252  setWorld();
253  return;
254  }
255 
256  if (range.isNull()) return;
257 
258  if (_singleMode) {
259  if (_ranges.empty()) _ranges.resize(1);
260  _ranges[0].expandTo(range);
261  return;
262  }
263 
264  ExpandToIfSnap exp(range, _snapFactor);
265  if (visit(exp)) return;
266 
267  // reached this point we need a new range
268  _ranges.push_back(range);
269 
270  combineRangesLazy();
271  }
272 
274  void add(const SnappingRanges2d<T>& other) {
275  const RangeList& rl = other._ranges;
276  std::for_each(rl.begin(), rl.end(), AddTo(*this));
277  }
278 
280  void growBy(const T amount) {
281 
282  if (isWorld() || isNull()) return;
283 
284  std::for_each(_ranges.begin(), _ranges.end(), GrowBy(amount));
285  combineRangesLazy();
286  }
287 
289  void scale(const float factor) {
290 
291  if (isWorld() || isNull()) return;
292 
293  std::for_each(_ranges.begin(), _ranges.end(), Scale(factor));
294  combineRangesLazy();
295  }
296 
298  void setNull() {
299  _ranges.clear();
300  }
301 
303  void setWorld() {
304  if (isWorld()) return;
305  _ranges.resize(1);
306  _ranges[0].setWorld();
307  }
308 
310  bool isWorld() const {
311  return ((size()==1) && (_ranges.front().isWorld()));
312  }
313 
315  bool isNull() const {
316  return _ranges.empty();
317  }
318 
320  size_type size() const {
321  finalize();
322  return _ranges.size();
323  }
324 
326  const RangeType& getRange(size_type index) const {
327  finalize();
328  assert(index<size());
329  return _ranges[index];
330  }
331 
334  RangeType getFullArea() const {
335  RangeType range;
336 
337  range.setNull();
338 
339  int rcount = _ranges.size();
340 
341  for (int rno=0; rno<rcount; rno++)
342  range.expandTo(_ranges[rno]);
343 
344  return range;
345  }
346 
347 
349  //
353  bool intersects(const RangeType& r) const {
354 
355  finalize();
356  return std::find_if(_ranges.begin(), _ranges.end(), IntersectsRange(r))
357  != _ranges.end();
358  }
359 
361  bool contains(T x, T y) const {
362 
363  finalize();
364  return std::find_if(_ranges.begin(), _ranges.end(), ContainsPoint(x, y))
365  != _ranges.end();
366  }
367 
369  //
373  bool contains(const RangeType& r) const {
374 
375  finalize();
376  return std::find_if(_ranges.begin(), _ranges.end(), ContainsRange(r))
377  != _ranges.end();
378  }
379 
388  bool contains(const SnappingRanges2d<T>& o) const
389  {
390 
391  finalize();
392  // o.finalize(); // should I finalize the other range too ?
393 
394  // Null range set doesn't contain and isn't contained by anything
395  if ( isNull() ) return false;
396  if ( o.isNull() ) return false;
397 
398  // World range contains everything (except null ranges)
399  if ( isWorld() ) return true;
400 
401  // This snappingrange is neither NULL nor WORLD
402  // The other can still be WORLD, but in that case the
403  // first iteration would return false
404  //
406  for (unsigned rno=0, rcount=o.size(); rno<rcount; rno++)
407  {
408  RangeType r = o.getRange(rno);
409  if ( ! contains(r) )
410  {
411  return false;
412  }
413  }
414 
415  return true;
416 
417  }
418 
419 
426  {
427  if (o.isNull()) {
428  setNull();
429  return;
430  }
431 
432  if (o.isWorld()) return;
433 
434  // We create a new ranges set for each range in "o" and
435  // then update ourselves with the *union* of these ranges.
436  // Anybody knows a better method (in terms of efficieny) ?
437 
438  std::vector<SnappingRanges2d<T> > list;
439  list.reserve(o.size());
440 
441  //TODO: use a visitor !
442  for (unsigned rno=0, rcount=o.size(); rno<rcount; rno++) {
443 
444  // add a copy of ourselves to the list
445  list.push_back(*this);
446 
447  // intersect that copy with the single range
448  list.back().intersect(o.getRange(rno));
449 
450  }
451 
452  // update ourselves with the union of the "list"
453  setNull();
454  for (auto& range : list) {
455  add(range);
456  }
457 
458  }
459 
460 
463  void intersect(const RangeType& r)
464  {
465 
466  finalize();
467 
468  if (isWorld()) { // world intersection with X = X
469  setNull();
470  add(r);
471  return;
472  }
473 
474  if (isNull()) return; // NULL will always remain NULL
475 
476  if (r.isNull()) { // X intersection with NULL = NULL
477  setNull();
478  return;
479  }
480 
481  if (r.isWorld()) return; // X intersection with WORLD = X
482 
483  // TODO: use a vector (remember to walk in reverse dir.)
484  for (int rno=_ranges.size()-1; rno>=0; rno--) {
485 
486  RangeType newrange = Intersection(_ranges[rno], r);
487 
488  if (newrange.isNull())
489  _ranges.erase(_ranges.begin() + rno);
490  else
491  _ranges[rno] = newrange;
492  }
493  }
494 
497  void combineRanges() const {
498 
499  // makes no sense in single mode
500  if (_singleMode) return;
501 
502  bool restart = true;
503 
504  _combineCounter = 0;
505 
506  while (restart) {
507 
508  int rcount = _ranges.size();
509 
510  restart=false;
511 
512  for (int i=0; i<rcount; i++) {
513 
514  for (int j=i+1; j<rcount; j++) {
515 
516  if (snaptest(_ranges[i], _ranges[j], _snapFactor)) {
517  // merge i + j
518  _ranges[i].expandTo(_ranges[j]);
519 
520  _ranges.erase(_ranges.begin() + j);
521 
522  restart=true; // restart from beginning
523  break;
524 
525  }
526  }
527 
528  if (restart) break;
529  }
530  }
531 
532  // limit number of ranges
533  if (_ranges.size() > _rangesLimit) {
534 
535  // We found way too much ranges, so reduce to just one single range.
536  // We could also double the factor and try again, but that probably
537  // won't make much difference, so we avoid the trouble...
538 
539  RangeType single = getFullArea();
540  _ranges.resize(1);
541  _ranges[0] = single;
542 
543  }
544 
545  }
546 
548  //
557  template<class V> inline bool visit(V& visitor) const
558  {
559  typename RangeList::iterator it, e;
560  for (it = _ranges.begin(), e = _ranges.end(); it != e; ++it) {
561  if (!visitor(*it)) break;
562  }
563  return it != _ranges.end();
564  }
565 
567  //
573  template<class V> inline void visitAll(V& visitor) const
574  {
575  for_each(_ranges.begin(), _ranges.end(), visitor);
576  }
577 
578 private:
579 
580 
583  void combineRangesLazy() const {
584  const size_type max = 5;
585  ++_combineCounter;
586  if (_combineCounter > max) combineRanges();
587  }
588 
589  void finalize() const {
590  if (_combineCounter > 0) combineRanges();
591  }
592 
594  //
597  mutable RangeList _ranges;
598 
600  float _snapFactor;
601 
603  bool _singleMode;
604 
606  size_type _rangesLimit;
607 
609  mutable size_type _combineCounter;
610 
611 };
612 
613 template <class T>
614 std::ostream&
615 operator<< (std::ostream& os, const SnappingRanges2d<T>& r)
616 {
617  if ( r.isNull() ) return os << "NULL";
618  if ( r.isWorld() ) return os << "WORLD";
619 
620  typedef typename SnappingRanges2d<T>::RangeList R;
621 
622  const R& ranges = r._ranges;
623 
624  std::copy(ranges.begin(), ranges.end(),
625  std::ostream_iterator<typename R::value_type>(os, ","));
626 
627  return os;
628 }
629 
630 namespace {
631 
632 template<typename T>
633 inline bool snaptest(const geometry::Range2d<T>& range1,
634  const geometry::Range2d<T>& range2, const float snapFactor)
635 {
636 
637  // when they intersect anyway, they should of course be merged!
638  // TODO: not really, a "+" style ranges list might be worth to
639  // remain unmerged (but needs special handling, i.e. create three
640  // ranges out of two)...
641  if (range1.intersects(range2)) return true;
642 
643  geometry::Range2d<T> temp = range1;
644  temp.expandTo(range2);
645 
646  return (range1.getArea() + range2.getArea()) * snapFactor >
647  temp.getArea();
648 
649 }
650 
651 } // anonymous namespace
652 } // namespace geometry
653 
656 
657 } //namespace gnash
658 
659 #endif
void for_each(C &container, R(T::*pmf)(const A &), const A &arg)
Definition: Renderer_ogl.cpp:690
size_type size() const
Returns the number of ranges in the list.
Definition: snappingrange.h:320
bool isNull() const
Returns true if this is the NULL Range2d.
Definition: Range2d.h:181
void combineRanges() const
Definition: snappingrange.h:497
detail::Promote< T >::type getArea() const
Get area (width*height)
Definition: Range2d.h:643
Range2d< T > & setNull()
Set the Range2d to the NULL value.
Definition: Range2d.h:190
AddTo(SnappingRanges2d< T > &us)
Definition: snappingrange.h:207
void add(const RangeType &range)
Add a Range to the set, merging when possible and appropriate.
Definition: snappingrange.h:250
void add(const SnappingRanges2d< T > &other)
combines two snapping ranges
Definition: snappingrange.h:274
Definition: snappingrange.h:182
bool operator()(const RangeType &us)
Definition: snappingrange.h:219
float getSnapFactor() const
Definition: snappingrange.h:130
SnappingRanges2d(const SnappingRanges2d< U > &from)
Templated copy constructor, for casting between range types.
Definition: snappingrange.h:101
void operator()(RangeType &r)
Definition: snappingrange.h:186
Anonymous namespace for callbacks, local functions, event handlers etc.
Definition: dbus_ext.cpp:40
GrowBy(const float factor)
Definition: snappingrange.h:196
Definition: GnashKey.h:152
size_type getRangeCountLimit() const
Definition: snappingrange.h:149
const RangeType & getRange(size_type index) const
Returns the range at the specified index.
Definition: snappingrange.h:326
Definition: GnashKey.h:161
2d Range template class
Definition: Range2d.h:77
void setRangeCountLimit(const size_type limit)
Definition: snappingrange.h:145
Definition: GnashKey.h:156
RangeList::size_type size_type
Definition: snappingrange.h:85
Definition: GnashKey.h:164
void setSingleMode(const bool mode)
if mode==true, then the snapping ranges will act like a normal Range2d
Definition: snappingrange.h:135
Range2d< T > Intersection(const Range2d< T > &r1, const Range2d< T > &r2)
Return a rectangle being the intersetion of the two rectangles.
Definition: Range2d.h:762
Scale(const float scale)
Definition: snappingrange.h:185
Range2d< T > & scale(float xfactor, float yfactor)
Scale this Range2d.
Definition: Range2d.h:471
ContainsPoint(const T x, const T y)
Definition: snappingrange.h:229
void intersect(const SnappingRanges2d< T > &o)
Definition: snappingrange.h:425
std::vector< RangeType > RangeList
Definition: snappingrange.h:84
bool intersects(const RangeType &r) const
Returns true if any of the ranges intersect the given range.
Definition: snappingrange.h:353
Definition: snappingrange.h:80
geometry::Range2d< T > RangeType
Definition: snappingrange.h:83
Definition: GnashKey.h:134
std::int32_t x
Definition: BitmapData_as.cpp:434
void setSnapFactor(const float factor)
Definition: snappingrange.h:125
void setWorld()
Resets to one range with world flags.
Definition: snappingrange.h:303
geometry::SnappingRanges2d< std::int32_t > InvalidatedRanges
Standard snapping 2d ranges type for invalidated bounds calculation.
Definition: snappingrange.h:655
Definition: GnashKey.h:130
Range2d< T > & expandTo(T x, T y)
Expand this Range2d to enclose the given point.
Definition: Range2d.h:297
bool operator()(const RangeType &us)
Definition: snappingrange.h:241
void visitAll(V &visitor) const
Visit the current Ranges set.
Definition: snappingrange.h:573
bool operator()(RangeType &r)
Definition: snappingrange.h:170
bool isWorld() const
Returns true if this is the WORLD Range2d.
Definition: Range2d.h:200
bool isWorld() const
Returns true, when the ranges equal world range.
Definition: snappingrange.h:310
bool contains(const RangeType &r) const
Returns true if any of the ranges contains the range.
Definition: snappingrange.h:373
void inheritConfig(const SnappingRanges2d< T > &from)
Definition: snappingrange.h:155
bool isNull() const
Returns true, when there is no range.
Definition: snappingrange.h:315
void scale(const float factor)
Scale all ranges by the specified factor.
Definition: snappingrange.h:289
Definition: GnashKey.h:132
Range2d< T > & growBy(T amount)
Grow this range by the given amout in all directions.
Definition: Range2d.h:520
Definition: snappingrange.h:204
std::int32_t y
Definition: BitmapData_as.cpp:435
void growBy(const T amount)
Grows all ranges by the specified amount.
Definition: snappingrange.h:280
Definition: GnashKey.h:155
bool contains(T x, T y) const
Returns true if any of the ranges contains the point.
Definition: snappingrange.h:361
Definition: GnashKey.h:151
bool operator()(const RangeType &us)
Definition: snappingrange.h:230
SnappingRanges2d()
Definition: snappingrange.h:90
ContainsRange(const RangeType &range)
Definition: snappingrange.h:240
bool contains(const SnappingRanges2d< T > &o) const
Returns true if all ranges in the given SnappingRanges2d are contained in at least one of the ranges ...
Definition: snappingrange.h:388
void operator()(const RangeType &r)
Definition: snappingrange.h:208
RangeType getFullArea() const
Definition: snappingrange.h:334
IntersectsRange(const RangeType &range)
Definition: snappingrange.h:218
Definition: snappingrange.h:193
bool contains(U x, U y) const
Return true if this rectangle contains the point with given coordinates (boundaries are inclusive)...
Definition: Range2d.h:241
void setNull()
Resets to NULL range.
Definition: snappingrange.h:298
Merge two ranges based on snaptest.
Definition: snappingrange.h:161
friend std::ostream & operator<<(std::ostream &os, const SnappingRanges2d< U > &r)
bool intersects(const Range2d< T > &other) const
Return true if this rectangle intersects the point with given coordinates (boundaries are inclusive)...
Definition: Range2d.h:281
bool getSingleMode() const
Definition: snappingrange.h:139
ExpandToIfSnap(const RangeType &rt, const float snapFactor)
Definition: snappingrange.h:164
void operator()(RangeType &r)
Definition: snappingrange.h:197
bool visit(V &visitor) const
Visit the current Ranges set.
Definition: snappingrange.h:557
void intersect(const RangeType &r)
Definition: snappingrange.h:463