Developer Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Histogram.hh
1 /*===========================================================================*\
2  * *
3  * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40 \*===========================================================================*/
41 
42 // Author: Martin Heistermann, <martin.heistermann()rwth-aachen.de>
43 
44 #ifndef ACG_HISTOGRAM_HH
45 #define ACG_HISTOGRAM_HH
46 
47 #include <vector>
48 #include <cassert>
49 #include <memory>
50 #include <exception>
51 #include <algorithm>
52 #include <type_traits>
53 
54 #include <QString>
55 
56 #include "../Config/ACGDefines.hh"
57 
58 namespace ACG {
59 class ACGDLLEXPORT Histogram {
60 public:
61  enum class LabelType {
62  PerBin,
63  PerBoundary,
64  };
65 
66  virtual ~Histogram() = default;
67  const std::vector<size_t> &getBins() const { return bins_; }
68  const std::vector<double> &getBinWidths() const { return bin_widths_; }
69  virtual double getTotalWidth() const = 0;
70 
71  virtual LabelType getLabelType() const = 0;
72  virtual QString getBoundaryLabel(size_t /*idx*/) const { assert(false); return QString();}
73  virtual QString getBinLabel (size_t /*idx*/) const { assert(false); return QString();}
74 
75 protected:
76  std::vector<size_t> bins_;
77  std::vector<double> bin_widths_;
78 };
79 
80 
81 // we need to be careful with ranges, some sums (e.g. INT_MAX - INT_MIN) do not fit into a signed int,
82 // so we store bin sizes as doubles. With specialization or some tricks we
83 // could probably use the next-biggest integer type, but if we're using
84 // the biggest integer type already, we should to fall back to double anyways.
85 
86 template<typename T>
87 class HistogramT : public Histogram {
88 public:
89  HistogramT(const std::vector<int> &histogram,
90  const std::vector<T> &bin_boundaries,
91  const std::vector<double> &bin_widths)
92  {
93  if (bins_.size() != bin_widths_.size()
94  || bins_.size() + 1 != bin_boundaries_.size()) {
95  throw std::runtime_error("Histogram constructor sizes don't match.");
96  }
97  bins_ = histogram;
98  bin_boundaries_ = bin_boundaries;
99  bin_widths_ = bin_widths;
100  double range = bin_boundaries.back() - bin_boundaries.front();
101  avg_bin_size_ = range / bins_.size();
102  }
103 
104  template<typename IterT>
105  HistogramT(IterT begin, IterT end, size_t max_bins)
106  {
107  static_assert(std::is_assignable<T&, typename IterT::value_type>::value, "IterT incompatible with T.");
108  static_assert(std::is_floating_point<typename IterT::value_type>::value, "HistogramT currently only supports floating point values.");
109  assert(max_bins > 0);
110  const size_t n = std::distance(begin, end);
111  if (n == 0) return;
112 
113  const auto minmax = std::minmax_element(begin, end);
114  const T min = *minmax.first;
115  const T max = *minmax.second;
116  const double min_dbl = static_cast<double>(min);
117  const double range = static_cast<double>(max) - min_dbl;
118 
119  const size_t n_bins_max = std::min(max_bins, n);
120  bin_boundaries_.reserve(n_bins_max + 1);
121 
122  T last_boundary = min;
123  bin_boundaries_.push_back(min);
124  for (size_t i = 1; i < n_bins_max; ++i) {
125  // Adding range/n_bins to a accumulator might seem more efficient/elegant,
126  // but might cause numeric issues.
127 
128  // This multiplication order is bad for huge ranges that cause overflows,
129  // however I assume tiny ranges are more common than huge values and more
130  // important to get right. If you disagree, add a case distinction or something better.
131 
132  T boundary = static_cast<T>(min + (i * range) / n_bins_max);
133  // avoid zero-sized bins (happens for many ints with values in a small range)
134  if (boundary != last_boundary || i == 0) {
135  bin_boundaries_.push_back(boundary);
136  bin_widths_.push_back(boundary - last_boundary);
137  }
138  last_boundary = boundary;
139  }
140  bin_boundaries_.push_back(max); // avoid rounding issues etc by explicitly picking max.
141  bin_widths_.push_back(max - last_boundary);
142 
143  bin_boundaries_.shrink_to_fit();
144  size_t n_bins = bin_boundaries_.size() - 1;
145  bins_.resize(n_bins);
146 
147  // note that due to rounding, our bins may have differing sizes, which matters
148  // if we handle integral types (relative size difference worst case: bin width 1 vs 2).
149  // Be careful to select the right bin.
150  std::for_each(begin, end, [&](const T &val) {
151  auto it = std::upper_bound(bin_boundaries_.begin(), bin_boundaries_.end(), val);
152  if (it == bin_boundaries_.end()) --it; // the last value is exactly max!
153  size_t idx = std::distance(bin_boundaries_.begin(), it);
154  assert(idx > 0);
155  ++bins_[idx - 1];
156  });
157  avg_bin_size_ = range / n_bins;
158  }
159 
160  const std::vector<T> &getBinBoundaries() const {
161  return bin_boundaries_;
162  }
163 
164  double getTotalWidth() const override
165  {
166  return bin_boundaries_.back() - bin_boundaries_.front();
167  }
168 
169  LabelType getLabelType() const override
170  {
171  return LabelType::PerBoundary;
172  }
173 
174  QString getBoundaryLabel (size_t idx) const override;
175 
176 
177 private:
178  std::vector<T> bin_boundaries_;
179  double avg_bin_size_ = 0.0;
180 };
181 
182 
183 template<typename T>
184 QString HistogramT<T>::getBoundaryLabel(size_t idx) const {
185  return QString::number(bin_boundaries_[idx]);
186 }
187 
188 } // namespace ACG
189 
190 #endif // ACG_HISTOGRAM_HH
Namespace providing different geometric functions concerning angles.
Definition: DBSCANT.cc:51