KJB
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gui_graph.h
Go to the documentation of this file.
1 /* $Id: gui_graph.h 18278 2014-11-25 01:42:10Z ksimek $ */
2 /* {{{=========================================================================== *
3  |
4  | Copyright (c) 1994-2011 by Kobus Barnard (author)
5  |
6  | Personal and educational use of this code is granted, provided that this
7  | header is kept intact, and that the authorship is not misrepresented, that
8  | its use is acknowledged in publications, and relevant papers are cited.
9  |
10  | For other use contact the author (kobus AT cs DOT arizona DOT edu).
11  |
12  | Please note that the code in this file has not necessarily been adequately
13  | tested. Naturally, there is no guarantee of performance, support, or fitness
14  | for any particular task. Nonetheless, I am interested in hearing about
15  | problems that you encounter.
16  |
17  | Author: Kyle Simek
18  * =========================================================================== }}}*/
19 
20 // vim: tabstop=4 shiftwidth=4 foldmethod=marker
21 
22 #ifndef KJB_CPP_GUI_CPP_GUI_GRAPH_H
23 #define KJB_CPP_GUI_CPP_GUI_GRAPH_H
24 
25 #ifdef KJB_HAVE_OPENGL
26 
27 #include <gui_cpp/gui_overlay.h>
28 #include <m_cpp/m_vector_d.h>
29 
30 #include <map>
31 #include <list>
32 #include <cmath>
33 #include <boost/array.hpp>
34 #include <boost/optional.hpp>
35 
36 namespace kjb
37 {
38 namespace gui
39 {
40 
41 class Plot : public Overlay
42 {
43 typedef Overlay Base;
44 
45 private:
46  // PRIVATE TYPES
47 
48  struct Data_range
49  {
50  Data_range()
51  {
52  reset();
53  }
54 
55  float x_min;
56  float x_max;
57  float y_min;
58  float y_max;
59  bool initialized;
60 
64  void reset();
65 
66  void expand(const Data_range& r);
67 
68  void expand(float x, float y);
69  };
70 
71  typedef std::map<double, double> Data_map;
72 
73  struct Data_set
74  {
75  Data_set() :
76  data(),
77  color(0.0, 0.0, 0.0),
78  thickness(1.0),
79  data_range_()
80  {}
81 
82  template <class Iterator_x, class Iterator_y>
83  void set(Iterator_x begin, Iterator_x end, Iterator_y ybegin)
84  {
85  data.clear();
86  Iterator_y it_y = ybegin;
87  for(Iterator_x it = begin; it != end; ++it, ++it_y)
88  {
89  data[*it] = *it_y;
90  }
91  update_data_range();
92  }
93 
94  template <class Iterator_y>
95  void set(Iterator_y ybegin, Iterator_y yend)
96  {
97  data.clear();
98  size_t i = 0;
99  for(Iterator_y it = ybegin; it != yend; ++it)
100  {
101  data[i] = *it;
102 
103  i++;
104  }
105  update_data_range();
106  }
107 
108  void set(const Data_map& in)
109  {
110  data = in;
111  update_data_range();
112  }
113 
114  template <class Iterator_x, class Iterator_y>
115  void append(Iterator_x begin, Iterator_x end, Iterator_y ybegin)
116  {
117  Data_range append_range;
118  Iterator_y it_y = ybegin;
119  for(Iterator_x it = begin; it != end; ++it, ++it_y)
120  {
121  data[*it] = *it_y;
122  append_range.expand(*it, *it_y);
123  }
124  expand_data_range_(append_range);
125  }
126 
127  template <class Iterator_y>
128  void append(Iterator_y ybegin, Iterator_y yend)
129  {
130  Data_range append_range;
131  Iterator_y it_y = ybegin;
132  size_t i;
133  if(data.size() == 0)
134  i = 0;
135  else
136  i = data.rbegin()->first + 1; // pick up where we left off
137 
138  for(Iterator_y it = ybegin; it != yend; ++it)
139  {
140  data[i] = *it_y;
141  i++;
142  append_range.expand(i, *it_y);
143  }
144  expand_data_range_(append_range);
145  }
146 
147  void append(const Data_map& in)
148  {
149  Data_range append_range;
150  typedef Data_map::const_iterator Iterator;
151  typedef Data_map::value_type Data_pair;
152 
153  for(Iterator it = in.begin(); it != in.end(); ++it)
154  {
155  data.insert(*it);
156  append_range.expand(it->first, it->second);
157  }
158 
159  expand_data_range_(append_range);
160  }
161 
162  void render() const;
163 
164  const Data_range& get_data_range()
165  {
166  return data_range_;
167  }
168 
169  void update_data_range();
170 
171  Data_map data;
172  Vector3 color;
173  float thickness;
174 
175  private:
176  void expand_data_range_(const Data_range& range);
177 
178  Data_range data_range_;
179  };
180 
181 
182  // useful typedefs
183  typedef std::list<Data_set> Data_sets;
184 
191  struct Axis
192  {
193  Axis();
194 
195  // returns whether this is valid
196  operator bool() const
197  {
198  return (y_size > 0 && x_size > 0);
199  }
200 
204  template <class Iterator>
205  void update_data_range(Iterator begin, Iterator end)
206  {
207  data_range_.reset();
208 
209  for(Iterator it = begin; it != end; ++it)
210  {
211  data_range_.expand(*it);
212  }
213 
214  update_real_range();
215  }
216 
220  void set(float x_min_, float x_max_, float y_min_, float y_max_);
221 
228  void auto_set(bool include_x_origin, bool include_y_origin);
229 
233  void fixed()
234  {
235  auto_ = false;
236  }
237 
244  void automatic(bool include_x_origin, bool include_y_origin)
245  {
246  auto_ = true;
247  auto_include_x_ = include_x_origin;
248  auto_include_y_ = include_y_origin;
249 
250  auto_set(auto_include_x_, auto_include_y_);
251  }
252 
261  void set_hash_spacing(float min_width, float min_height)
262  {
263  // in normalized coordinates, width of 0.5 means half the width
264  // of the plot
265  min_dx_normalized_ = min_width;
266  min_dy_normalized_ = min_height;
267 
268  // need at least one subdivision
269  if(min_dy_normalized_ > 1.0)
270  min_dy_normalized_ = 1.0;
271 
272  if(min_dx_normalized_ > 1.0)
273  min_dx_normalized_ = 1.0;
274 
275  update_ideal_range_x();
276  update_ideal_range_y();
277  }
278 
279  void update_real_range()
280  {
281  if(auto_)
282  auto_set(auto_include_x_, auto_include_y_);
283  }
284 
292  void update_ideal_range_y()
293  {
294  if(y_size < SIZE_EPSILON)
295  return;
296 
297  // convert from normalized pane coordinates into axis-space coordinates
298  float min_height = min_dy_normalized_ * y_size;
299 
300  dy = human_quantize_range_(y_min, y_max, auto_, 10, min_height);
301 
302  // range could have been re-quantized, so sizes need updating
303  if(auto_)
304  {
305  y_size = y_max - y_min;
306 
307  assert(y_size >= 0);
308  assert(y_size < DBL_MAX);
309 #ifdef TEST
310  assert(y_size < 1e15);
311 #endif
312  }
313  }
314 
322  void update_ideal_range_x()
323  {
324  // TODO: this sucks because it's literally exactly the same as update dy, just with all the variables changed.
325  // Best solution: refactor so Axis only represents ONE AXIS!
326  // It was a really obviously dumb idea to conflate x and y axis,
327  // not sure what I was thinking...
328  //
329  // ALSO this is the most bug-prone, tightly-coupled code in the entire file.
330  // If there's a weird bug, its _definitely_ here.
331 
332  if(x_size < SIZE_EPSILON)
333  return;
334 
335  // convert from normalized pane coordinates into axis-space coordinates
336  float min_width = min_dx_normalized_ * x_size;
337 
338  // update dx and dy (and optionally *_min and *_max)
339  //
340  // if data range is sufficiently small, the quantize
341  // method will fail, but since dx is ignored in this
342  // case, we can just skip updating dx...
343  dx = human_quantize_range_(x_min, x_max, auto_, 10, min_width);
344 
345  // range could have been re-quantized, so sizes need updating
346  if(auto_)
347  {
348  x_size = x_max - x_min;
349 
350  assert(x_size >= 0);
351  assert(x_size < DBL_MAX);
352  }
353  }
354 
356  static double human_quantize_range_(float& xmin, float& xmax, bool quantize_bounds, size_t max_num_bins, float min_incr);
357 
362  static void quantize_range_(float& x_min, float& x_max, float incr)
363  {
364  x_min = std::floor(x_min / incr) * incr;
365  x_max = std::ceil(x_max / incr) * incr;
366  }
367 
375  void get_view_range(
376  float& left ,
377  float& bottom,
378  float& width,
379  float& height,
380  float& hash_dx,
381  float& hash_dy) const;
382 
383  private:
384  bool auto_;
385  bool auto_include_x_;
386  bool auto_include_y_;
387 
388  Data_range data_range_;
389 
390  float min_dx_normalized_;
391  float min_dy_normalized_;
392 
393  float x_size;
394  float y_size;
395  public:
396  float x_min;
397  float x_max;
398  float y_min;
399  float y_max;
400 
401  float dx;
402  float dy;
403 
404  static const float SIZE_EPSILON;
405  };
406 
407 
408 public:
409  typedef Data_sets::iterator Data_set_iterator;
410  static const Vector3 DEFAULT_COLOR;
411  Plot(float x, float y, float width, float height, const Vector3& bg_color = Vector3(1.0, 1.0, 1.0));
412 
413  void thin_mode()
414  {
415  thin_mode_ = true;
416  update_data_pane_geometry();
417  }
418 
419  void set_transparent_background()
420  {
421  bg_color_.reset();
422  }
423 
424  virtual void set_size(int width, int height)
425  {
426  Base::set_size(width, height);
427  update_geometry_();
428  }
429 
430  virtual void set_position(int x, int y)
431  {
432  Base::set_position(x, y);
433  update_geometry_();
434  }
435 
439  Data_set_iterator add_dataset(const Vector3& line_color = DEFAULT_COLOR);
440 
444  Data_set_iterator add_dataset(const Data_map& data, const Vector3& line_color = DEFAULT_COLOR);
445 
449  void update_dataset(Data_set_iterator data_set, const Data_map& data)
450  {
451  data_set->set(data);
452  update_axis_data_range();
453  }
454 
458  void append_dataset(Data_set_iterator data_set, const Data_map& data)
459  {
460  data_set->append(data);
461  update_axis_data_range();
462  }
463 
464 
468  template <class Iterator_x, class Iterator_y>
469  Data_set_iterator add_dataset(Iterator_x begin, Iterator_x end, Iterator_y ybegin, const Vector3& line_color = DEFAULT_COLOR)
470  {
471  Vector3 color = line_color;
472  if(color == DEFAULT_COLOR)
473  color = COLOR_ORDER_[data_sets_.size() % COLOR_ORDER_.size()];
474 
475  data_sets_.push_front(Data_set());
476  Data_set_iterator data_set = data_sets_.begin();
477 
478  data_set->color = color;
479  update_dataset(data_set, begin, end, ybegin);
480  return data_set;
481  }
482 
486  template <class Iterator_x, class Iterator_y>
487  void update_dataset(Data_set_iterator data_set, Iterator_x begin, Iterator_x end, Iterator_y ybegin)
488  {
489  data_set->set(begin, end, ybegin);
490  update_axis_data_range();
491  }
492 
496  template <class Iterator_x, class Iterator_y>
497  void append_dataset(Data_set_iterator data_set, Iterator_x begin, Iterator_x end, Iterator_y ybegin)
498  {
499  data_set->append(begin, end, ybegin);
500  update_axis_data_range();
501  }
502 
506  template <class Iterator_y>
507  Data_set_iterator add_dataset(Iterator_y ybegin, Iterator_y yend, const Vector3& line_color = DEFAULT_COLOR)
508  {
509  Vector3 color = line_color;
510  if(color == DEFAULT_COLOR)
511  color = COLOR_ORDER_[data_sets_.size() % COLOR_ORDER_.size()];
512 
513  data_sets_.push_front(Data_set());
514  Data_set_iterator data_set = data_sets_.begin();
515 
516  data_set->color = color;
517  update_dataset(data_set, ybegin, yend);
518  return data_sets_.begin();
519  }
520 
524  template <class Iterator_y>
525  void update_dataset(Data_set_iterator data_set, Iterator_y ybegin, Iterator_y yend)
526  {
527  data_set->set(ybegin, yend);
528  update_axis_data_range();
529  }
530 
534  template <class Iterator_y>
535  void append_dataset(Data_set_iterator data_set, Iterator_y ybegin, Iterator_y yend)
536  {
537  data_set->append(ybegin, yend);
538  update_axis_data_range();
539  }
540 
544  Data_set_iterator get_dataset(size_t index);
545 
546  void update_axis_data_range();
547 
549  void set_mark(double x)
550  {
551  mark_ = x;
552  }
553 
555  void unset_mark()
556  {
557  mark_ = boost::none;
558  }
559 
561  void fix_axis()
562  {
563  axis_.fixed();
564  }
565 
568  void axis(float x_min, float x_max, float y_min, float y_max)
569  {
570  axis_.set(x_min, x_max, y_min, y_max);
571  }
572 
574  void auto_axis(bool include_x = false, bool include_y = false)
575  {
576  axis_.automatic(include_x, include_y);
577  }
578 
580  void title(const std::string& title)
581  {
582  title_ = title;
583  render_title_ = true;
584  update_data_pane_geometry();
585  }
586 
588  void xlabel(const std::string& label)
589  {
590  x_axis_label_ = label;
591  render_x_axis_label_ = true;
592  update_data_pane_geometry();
593  }
594 
596  void ylabel(const std::string& label)
597  {
598  y_axis_label_ = label;
599  render_y_axis_label_ = true;
600  update_data_pane_geometry();
601  }
602 
603  virtual void render() const;
604 
611  void render_data_frame() const;
612 
613  void box(bool enabled)
614  {
615  show_box_ = enabled;
616  }
617 
618 private:
619  // PRIVATE METHODS
620  static void draw_mark_(const Data_set& data_set, double x);
621 
622  static void draw_circle_(
623  double x,
624  double y,
625  double radius,
626  const Vector3& fill_color,
627  const Vector3& line_color,
628  size_t subdivisions = 10);
629 
630  void update_hash_mark_spacing();
631 
632  float data_pane_height() const
633  {
634  return data_pane_height_;
635  }
636 
637  float data_pane_width() const
638  {
639  return data_pane_width_;
640  }
641 
642  float data_pane_x_offset() const
643  {
644  return data_pane_x_offset_;
645  }
646 
647  float data_pane_y_offset() const
648  {
649  return data_pane_y_offset_;
650  }
651 
652  void update_geometry_()
653  {
654  update_data_pane_geometry();
655  }
656 
657  void update_data_pane_geometry();
658 
659  static void centered_text_(const std::string& str, float x, float y) ;
660 
661  static void right_text_(const std::string& str, float x, float y) ;
662 
663  static void centered_vertical_text_(const std::string& str, float x, float y) ;
664 
665 
669  void draw_axes( const Axis& axis, bool full_box) const;
670 
671 
672 private:
673  boost::optional<Vector3> bg_color_;
674  std::list<Data_set> data_sets_;
675 
676  boost::optional<double> mark_;
677 
678  Axis axis_;
679  bool auto_axis_;
680 
681  std::string title_;
682  std::string y_axis_label_;
683  std::string x_axis_label_;
684 
685  bool render_title_;
686  bool render_y_axis_label_;
687  bool render_x_axis_label_;
688 
689  float data_pane_x_offset_;
690  float data_pane_y_offset_;
691 
692  float data_pane_height_;
693  float data_pane_width_;
694 
695  bool show_box_;
696  bool thin_mode_;
697 
698  static const float CHARACTER_WIDTH;
699  static const float CHARACTER_HEIGHT;
700  static const float DATA_PANE_PADDING;
701  static const float TEXT_PADDING;
702  static const float TITLE_HEIGHT;
703  static const float X_AXIS_LABEL_HEIGHT;
704  static const float Y_AXIS_LABEL_WIDTH;
705  static const float HASH_LABEL_MAX_CHARS;
706  static const float HASH_LABEL_WIDTH;
707  static const float HASH_LABEL_HEIGHT;
708 
709 #ifdef KJB_HAVE_GLUT
710  static const void* CHARACTER_FONT;
711 #endif
712 
713  static const float MIN_HASH_LABEL_WIDTH;
714  static const float MIN_HASH_LABEL_HEIGHT;
715  static const float HASH_MARK_SIZE;
716 
717  static const boost::array<Vector3, 7> COLOR_ORDER_;
718 };
719 } // namespace gui
720 } // namespace kjb
721 
722 #endif /* have_opengl */
723 #endif
Vector_d< 3 > Vector3
Definition: g_quaternion.h:37
height
Definition: APPgetLargeConnectedEdges.m:33
r
Definition: APPgetLargeConnectedEdges.m:127
REAL xmin
Definition: triangle.c:649
Int_matrix floor(const Matrix &m)
Definition: m_matrix.cpp:2026
x
Definition: APPgetLargeConnectedEdges.m:100
void render(const Cuboid &c)
Definition: psi_weighted_box.cpp:56
get the indices of edges in each direction for i
Definition: APPgetLargeConnectedEdges.m:48
REAL xmax
Definition: triangle.c:649