Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RPalette.cxx
Go to the documentation of this file.
1 /*************************************************************************
2  * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
3  * All rights reserved. *
4  * *
5  * For the licensing terms see $ROOTSYS/LICENSE. *
6  * For the list of contributors see $ROOTSYS/README/CREDITS. *
7  *************************************************************************/
8 
9 #include "ROOT/RPalette.hxx"
10 
11 #include "ROOT/RLogger.hxx"
12 
13 #include <algorithm>
14 #include <cmath>
15 #include <exception>
16 #include <unordered_map>
17 
18 using namespace ROOT::Experimental;
19 
20 RPalette::RPalette(bool interpolate, bool knownNormalized, const std::vector<RPalette::OrdinalAndColor> &points)
21  : fColors(points), fInterpolate(interpolate), fNormalized(knownNormalized)
22 {
23  if (points.size() < 2)
24  throw std::runtime_error("Must have at least two points to build a palette!");
25 
26  std::sort(fColors.begin(), fColors.end());
27 
28  if (!knownNormalized) {
29  // Is this a normalized palette? I.e. are the first and last ordinals 0 and 1?
30  double high = fColors.back().fOrdinal;
31  double low = fColors.front().fOrdinal;
32  double prec = (high - low) * 1E-6;
33 
34  auto reasonablyEqual = [&](double val, double expected) -> bool { return std::fabs(val - expected) < prec; };
35  fNormalized = reasonablyEqual(low, 0.) && reasonablyEqual(high, 1.);
36  }
37 }
38 
39 namespace {
40 static std::vector<RPalette::OrdinalAndColor> AddOrdinals(const std::vector<RColor> &points)
41 {
42  std::vector<RPalette::OrdinalAndColor> ret(points.size());
43  auto addOneOrdinal = [&](const RColor &col) -> RPalette::OrdinalAndColor {
44  return {1. / (points.size() - 1) * (&col - points.data()), col};
45  };
46  std::transform(points.begin(), points.end(), ret.begin(), addOneOrdinal);
47  return ret;
48 }
49 } // unnamed namespace
50 
51 RPalette::RPalette(bool interpolate, const std::vector<RColor> &points)
52  : RPalette(interpolate, true, AddOrdinals(points))
53 {}
54 
55 RColor RPalette::GetColor(double ordinal)
56 {
57  if (fInterpolate) {
58  R__ERROR_HERE("Gpad") << "Not yet implemented!";
59  } else {
60  auto iColor = std::lower_bound(fColors.begin(), fColors.end(), ordinal);
61  if (iColor == fColors.end())
62  return fColors.back().fColor;
63  // Is iColor-1 closer to ordinal than iColor?
64  if (iColor != fColors.begin() && (iColor - 1)->fOrdinal - ordinal < ordinal - iColor->fOrdinal)
65  return (iColor - 1)->fColor;
66  return iColor->fColor;
67  }
68  return RColor{};
69 }
70 
71 namespace {
72 using GlobalPalettes_t = std::unordered_map<std::string, RPalette>;
73 static GlobalPalettes_t CreateDefaultPalettes()
74 {
75  GlobalPalettes_t ret;
76  ret["default"] = RPalette({RColor::kRed, RColor::kBlue});
77  ret["bw"] = RPalette({RColor::kBlack, RColor::kWhite});
78  return ret;
79 }
80 
81 static GlobalPalettes_t &GetGlobalPalettes()
82 {
83  static GlobalPalettes_t globalPalettes = CreateDefaultPalettes();
84  return globalPalettes;
85 }
86 } // unnamed namespace
87 
88 void RPalette::RegisterPalette(std::string_view name, const RPalette &palette)
89 {
90  GetGlobalPalettes()[std::string(name)] = palette;
91 }
92 
93 const RPalette &RPalette::GetPalette(std::string_view name)
94 {
95  static const RPalette sNoPaletteWithThatName;
96  auto iGlobalPalette = GetGlobalPalettes().find(std::string(name));
97  if (iGlobalPalette == GetGlobalPalettes().end())
98  return sNoPaletteWithThatName;
99  return iGlobalPalette->second;
100 }