SPH
Palette.cpp
Go to the documentation of this file.
1 #include "gui/objects/Palette.h"
2 #include "io/Path.h"
4 #include <fstream>
5 
7 
8 float Palette::linearToPalette(const float value) const {
9  float palette;
10  switch (scale) {
12  palette = value;
13  break;
15  // we allow calling this function with zero or negative value, it should simply map to the lowest
16  // value on the palette
17  if (value < EPS) {
18  palette = -LARGE;
19  } else {
20  palette = float(log10(value));
21  }
22  break;
24  if (value > 1.f) {
25  palette = 1.f + float(log10(value));
26  } else if (value < -1.f) {
27  palette = -1.f - float(log10(-value));
28  } else {
29  palette = value;
30  }
31  break;
32  default:
34  }
35  SPH_ASSERT(isReal(palette), value);
36  return palette;
37 }
38 
39 float Palette::paletteToLinear(const float value) const {
40  switch (scale) {
42  return value;
44  return float(exp10(value));
46  if (value > 1.f) {
47  return float(exp10(value - 1.f));
48  } else if (value < -1.f) {
49  return float(-exp10(-value - 1.f));
50  } else {
51  return value;
52  }
53  default:
54  NOT_IMPLEMENTED; // in case new scale is added
55  }
56 }
57 
59  : points(other.points.clone())
60  , range(other.range)
61  , scale(other.scale) {}
62 
64  points = copyable(other.points);
65  range = other.range;
66  scale = other.scale;
67  return *this;
68 }
69 
70 Palette::Palette(Array<Point>&& controlPoints, const PaletteScale scale)
71  : points(std::move(controlPoints))
72  , scale(scale) {
73 #ifdef SPH_DEBUG
74  SPH_ASSERT(points.size() >= 2);
75  if (scale == PaletteScale::LOGARITHMIC) {
76  SPH_ASSERT(points[0].value >= 0.f);
77  }
78  // sanity check, points must be sorted
79  for (Size i = 0; i < points.size() - 1; ++i) {
80  SPH_ASSERT(points[i].value < points[i + 1].value);
81  }
82 #endif
83  // save range before converting scale
84  range = Interval(points[0].value, points[points.size() - 1].value);
85  for (Size i = 0; i < points.size(); ++i) {
86  points[i].value = linearToPalette(points[i].value);
87  }
88 }
89 
91  SPH_ASSERT(points.size() >= 2);
92  return range;
93 }
94 
95 void Palette::setInterval(const Interval& newRange) {
96  Interval oldPaletteRange(points.front().value, points.back().value);
97  Interval newPaletteRange(
98  linearToPalette(float(newRange.lower())), linearToPalette(float(newRange.upper())));
99  const float scale = float(newPaletteRange.size() / oldPaletteRange.size());
100  const float offset = float(newPaletteRange.lower() - scale * oldPaletteRange.lower());
101  for (Size i = 0; i < points.size(); ++i) {
102  points[i].value = points[i].value * scale + offset;
103  }
104  range = newRange;
105 }
106 
108  return scale;
109 }
110 
111 Rgba Palette::operator()(const float value) const {
112  SPH_ASSERT(points.size() >= 2);
113  if (scale == PaletteScale::LOGARITHMIC && value <= 0.f) {
114  return points[0].color;
115  }
116  const float palette = linearToPalette(value);
117  if (palette <= points[0].value) {
118  return points[0].color;
119  }
120  if (palette >= points[points.size() - 1].value) {
121  return points[points.size() - 1].color;
122  }
123  for (Size i = 0; i < points.size() - 1; ++i) {
124  if (Interval(points[i].value, points[i + 1].value).contains(palette)) {
125  // interpolate
126  const float x = (points[i + 1].value - palette) / (points[i + 1].value - points[i].value);
127  return points[i].color * x + points[i + 1].color * (1.f - x);
128  }
129  }
130  STOP;
131 }
132 
134  Palette cloned = *this;
135  for (auto& point : cloned.points) {
136  point.color = func(point.color);
137  }
138  return cloned;
139 }
140 
141 float Palette::relativeToPalette(const float value) const {
142  SPH_ASSERT(value >= 0.f && value <= 1.f);
143  const float interpol = points[0].value * (1.f - value) + points.back().value * value;
144  switch (scale) {
146  return interpol;
148  return float(exp10(interpol));
150  if (interpol > 1.f) {
151  return float(exp10(interpol - 1.f));
152  } else if (interpol < -1.f) {
153  return float(-exp10(-interpol - 1.f));
154  } else {
155  return interpol;
156  }
157  default:
158  NOT_IMPLEMENTED; // in case new scale is added
159  }
160 }
161 
162 float Palette::paletteToRelative(const float value) const {
163  const float linear = linearToPalette(value);
164  return (linear - points[0].value) / (points.back().value - points[0].value);
165 }
166 
167 Outcome Palette::loadFromStream(std::istream& ifs) {
168  try {
169  Array<Rgba> colors;
170  std::string line;
171  while (std::getline(ifs, line)) {
172  std::stringstream ss(replaceAll(line, ",", " "));
173  Rgba color;
174  while (!ss.eof()) {
175  ss >> color.r() >> color.g() >> color.b();
176  color.a() = 1.f;
177  colors.push(color);
178  }
179  }
180  if (colors.size() < 2) {
181  return makeFailed("No data loaded");
182  }
183 
184  // preserve the interval of values
186  float from = points.front().value;
187  float to = points.back().value;
188  points.resize(colors.size());
189  for (Size i = 0; i < points.size(); ++i) {
190  points[i].color = colors[i];
191  // yes, do not use linearToPalette, we want to map the palette in linear, not on quantities
192  points[i].value = from + float(i) * (to - from) / (points.size() - 1);
193  }
194  return SUCCESS;
195  } catch (const std::exception& e) {
196  return makeFailed("Cannot load palette: ", e.what());
197  }
198 }
199 
201  std::ifstream ifs(path.native());
202  return loadFromStream(ifs);
203 }
204 
205 Outcome Palette::saveToStream(std::ostream& ofs, const Size lineCnt) const {
206  try {
207  for (Size i = 0; i < lineCnt; ++i) {
208  const float value = this->relativeToPalette(float(i) / (lineCnt - 1));
209  const Rgba color = operator()(value);
210  ofs << color.r() << "," << color.g() << "," << color.b();
211  if (i != lineCnt - 1) {
212  ofs << std::endl;
213  }
214  }
215  return SUCCESS;
216  } catch (const std::exception& e) {
217  return makeFailed("Cannot save palette: ", e.what());
218  }
219 }
220 
221 Outcome Palette::saveToFile(const Path& path, const Size lineCnt) const {
222  std::ofstream os(path.native());
223  return this->saveToStream(os, lineCnt);
224 }
225 
INLINE bool isReal(const AntisymmetricTensor &t)
INLINE CopyableArray< T, TAllocator, TCounter > copyable(const Array< T, TAllocator, TCounter > &array)
Definition: Array.h:558
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
#define STOP
Definition: Assert.h:106
#define NOT_IMPLEMENTED
Helper macro marking missing implementation.
Definition: Assert.h:100
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
constexpr Float EPS
Definition: MathUtils.h:30
INLINE T log10(const T f)
Definition: MathUtils.h:331
INLINE T exp10(const T f)
Definition: MathUtils.h:326
constexpr Float LARGE
Definition: MathUtils.h:34
#define NAMESPACE_SPH_END
Definition: Object.h:12
const SuccessTag SUCCESS
Global constant for successful outcome.
Definition: Outcome.h:141
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
Definition: Outcome.h:157
PaletteScale
Definition: Palette.h:13
@ HYBRID
Logarithic scale for values > 1 and < -1, linear scale on interval <-1, 1>
@ LINEAR
Control points are interpolated on linear scale.
@ LOGARITHMIC
Control points are interpolated on logarithmic scale. All points must be positive!
Object representing a path on a filesystem, similar to std::filesystem::path in c++17.
std::string replaceAll(const std::string &source, const std::string &old, const std::string &s)
Replaces all occurences of string with a new string.
Generic dynamically allocated resizable storage.
Definition: Array.h:43
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
INLINE TCounter size() const noexcept
Definition: Array.h:193
Object representing a 1D interval of real numbers.
Definition: Interval.h:17
INLINE Float lower() const
Returns lower bound of the interval.
Definition: Interval.h:74
INLINE Float upper() const
Returns upper bound of the interval.
Definition: Interval.h:79
INLINE Float size() const
Returns the size of the interval.
Definition: Interval.h:89
Represents a color palette, used for mapping arbitrary number to a color.
Definition: Palette.h:25
void setInterval(const Interval &newRange)
Definition: Palette.cpp:95
Palette()=default
Palette & operator=(const Palette &other)
Definition: Palette.cpp:63
float relativeToPalette(const float value) const
Converts a relative position to an absolute position on a palette.
Definition: Palette.cpp:141
PaletteScale getScale() const
Returns the scale of the palette.
Definition: Palette.cpp:107
Palette transform(Function< Rgba(const Rgba &)> func) const
Returns the palette with colors modified by generic transform.
Definition: Palette.cpp:133
Outcome saveToStream(std::ostream &ofs, const Size lineCnt=256) const
Saves the palettes into given output stream.
Definition: Palette.cpp:205
float paletteToRelative(const float value) const
Inverse transform to relativeToPalette.
Definition: Palette.cpp:162
Outcome loadFromFile(const Path &path)
Loads the palette from given .csv file.
Definition: Palette.cpp:200
Outcome loadFromStream(std::istream &ifs)
Loads the palette from given input stream.
Definition: Palette.cpp:167
Outcome saveToFile(const Path &path, const Size lineCnt=256) const
Saves the palette to a .csv file.
Definition: Palette.cpp:221
Rgba operator()(const float value) const
Returns the color mapped to given number.
Definition: Palette.cpp:111
Interval getInterval() const
Returns the interval for which the palette is defined.
Definition: Palette.cpp:90
Object representing a path on a filesystem.
Definition: Path.h:17
std::string native() const
Returns the native version of the path.
Definition: Path.cpp:71
Definition: Color.h:8
float & a()
Definition: Color.h:63
float & g()
Definition: Color.h:47
float & r()
Definition: Color.h:39
float & b()
Definition: Color.h:55
Overload of std::swap for Sph::Array.
Definition: Array.h:578