2014-08-15 01:19:41 +04:00
|
|
|
#include "colormap.h"
|
2014-09-17 01:27:54 +04:00
|
|
|
#include "boosty.h"
|
2014-08-15 01:19:41 +04:00
|
|
|
#include "printutils.h"
|
2014-09-17 01:27:54 +04:00
|
|
|
#include "PlatformUtils.h"
|
|
|
|
|
2014-12-05 01:29:18 +03:00
|
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
|
|
|
2014-11-03 01:36:23 +03:00
|
|
|
static const char *DEFAULT_COLOR_SCHEME_NAME = "Cornfield";
|
|
|
|
|
2014-11-30 04:29:29 +03:00
|
|
|
// See http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
|
|
|
|
static void rgbtohsv(float r, float g, float b, float &h, float &s, float &v)
|
|
|
|
{
|
|
|
|
float K = 0.f;
|
|
|
|
|
|
|
|
if (g < b)
|
|
|
|
{
|
|
|
|
std::swap(g, b);
|
|
|
|
K = -1.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r < g)
|
|
|
|
{
|
|
|
|
std::swap(r, g);
|
|
|
|
K = -2.f / 6.f - K;
|
|
|
|
}
|
|
|
|
|
|
|
|
float chroma = r - std::min(g, b);
|
|
|
|
h = fabs(K + (g - b) / (6.f * chroma + 1e-20f));
|
|
|
|
s = chroma / (r + 1e-20f);
|
|
|
|
v = r;
|
|
|
|
}
|
|
|
|
|
2014-11-15 20:39:17 +03:00
|
|
|
RenderColorScheme::RenderColorScheme() : _path("")
|
2014-11-03 01:36:23 +03:00
|
|
|
{
|
|
|
|
_name = DEFAULT_COLOR_SCHEME_NAME;
|
|
|
|
_index = 1000;
|
|
|
|
_show_in_gui = true;
|
|
|
|
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(BACKGROUND_COLOR, Color4f(0xff, 0xff, 0xe5)));
|
2015-05-14 22:48:12 +03:00
|
|
|
_color_scheme.insert(ColorScheme::value_type(AXES_COLOR, Color4f(0x00, 0x00, 0x00)));
|
2014-11-03 01:36:23 +03:00
|
|
|
_color_scheme.insert(ColorScheme::value_type(OPENCSG_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(OPENCSG_FACE_BACK_COLOR, Color4f(0x9d, 0xcb, 0x51)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CGAL_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CGAL_FACE_2D_COLOR, Color4f(0x00, 0xbf, 0x99)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CGAL_FACE_BACK_COLOR, Color4f(0x9d, 0xcb, 0x51)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CGAL_EDGE_FRONT_COLOR, Color4f(0xff, 0xec, 0x5e)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CGAL_EDGE_BACK_COLOR, Color4f(0xab, 0xd8, 0x56)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CGAL_EDGE_2D_COLOR, Color4f(0xff, 0x00, 0x00)));
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(CROSSHAIR_COLOR, Color4f(0x80, 0x00, 0x00)));
|
|
|
|
}
|
|
|
|
|
2014-11-15 20:39:17 +03:00
|
|
|
RenderColorScheme::RenderColorScheme(fs::path path) : _path(path)
|
2014-09-17 01:27:54 +04:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
boost::property_tree::read_json(boosty::stringy(path).c_str(), pt);
|
|
|
|
_name = pt.get<std::string>("name");
|
|
|
|
_index = pt.get<int>("index");
|
|
|
|
_show_in_gui = pt.get<bool>("show-in-gui");
|
|
|
|
|
|
|
|
addColor(BACKGROUND_COLOR, "background");
|
2015-05-12 23:26:08 +03:00
|
|
|
addColor(AXES_COLOR, "axes-color");
|
2014-09-17 01:27:54 +04:00
|
|
|
addColor(OPENCSG_FACE_FRONT_COLOR, "opencsg-face-front");
|
|
|
|
addColor(OPENCSG_FACE_BACK_COLOR, "opencsg-face-back");
|
|
|
|
addColor(CGAL_FACE_FRONT_COLOR, "cgal-face-front");
|
|
|
|
addColor(CGAL_FACE_2D_COLOR, "cgal-face-2d");
|
|
|
|
addColor(CGAL_FACE_BACK_COLOR, "cgal-face-back");
|
|
|
|
addColor(CGAL_EDGE_FRONT_COLOR, "cgal-edge-front");
|
|
|
|
addColor(CGAL_EDGE_BACK_COLOR, "cgal-edge-back");
|
|
|
|
addColor(CGAL_EDGE_2D_COLOR, "cgal-edge-2d");
|
|
|
|
addColor(CROSSHAIR_COLOR, "crosshair");
|
|
|
|
} catch (const std::exception & e) {
|
2015-04-26 23:11:51 +03:00
|
|
|
PRINTB("Error reading color scheme file '%s': %s", boosty::stringy(path).c_str() % e.what());
|
2014-11-15 20:39:17 +03:00
|
|
|
_error = e.what();
|
2014-09-17 01:27:54 +04:00
|
|
|
_name = "";
|
|
|
|
_index = 0;
|
|
|
|
_show_in_gui = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RenderColorScheme::~RenderColorScheme()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RenderColorScheme::valid() const
|
|
|
|
{
|
|
|
|
return !_name.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string & RenderColorScheme::name() const
|
|
|
|
{
|
|
|
|
return _name;
|
|
|
|
}
|
|
|
|
|
|
|
|
int RenderColorScheme::index() const
|
|
|
|
{
|
|
|
|
return _index;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RenderColorScheme::showInGui() const
|
|
|
|
{
|
|
|
|
return _show_in_gui;
|
|
|
|
}
|
|
|
|
|
2014-11-15 20:39:17 +03:00
|
|
|
std::string RenderColorScheme::path() const
|
|
|
|
{
|
|
|
|
return _path.string();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string RenderColorScheme::error() const
|
|
|
|
{
|
|
|
|
return _error;
|
|
|
|
}
|
|
|
|
|
2014-09-17 01:27:54 +04:00
|
|
|
ColorScheme & RenderColorScheme::colorScheme()
|
|
|
|
{
|
|
|
|
return _color_scheme;
|
|
|
|
}
|
|
|
|
|
|
|
|
const boost::property_tree::ptree & RenderColorScheme::propertyTree() const
|
|
|
|
{
|
|
|
|
return pt;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderColorScheme::addColor(RenderColor colorKey, std::string key)
|
|
|
|
{
|
|
|
|
const boost::property_tree::ptree& colors = pt.get_child("colors");
|
|
|
|
std::string color = colors.get<std::string>(key);
|
|
|
|
if ((color.length() == 7) && (color.at(0) == '#')) {
|
|
|
|
char *endptr;
|
|
|
|
unsigned int val = strtol(color.substr(1).c_str(), &endptr, 16);
|
|
|
|
int r = (val >> 16) & 0xff;
|
|
|
|
int g = (val >> 8) & 0xff;
|
|
|
|
int b = val & 0xff;
|
|
|
|
_color_scheme.insert(ColorScheme::value_type(colorKey, Color4f(r, g, b)));
|
|
|
|
} else {
|
|
|
|
throw std::invalid_argument(std::string("invalid color value for key '") + key + "': '" + color + "'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-15 01:19:41 +04:00
|
|
|
ColorMap *ColorMap::inst(bool erase)
|
|
|
|
{
|
|
|
|
static ColorMap *instance = new ColorMap;
|
|
|
|
if (erase) {
|
|
|
|
delete instance;
|
|
|
|
instance = NULL;
|
|
|
|
}
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2014-11-01 01:20:12 +03:00
|
|
|
ColorMap::ColorMap()
|
|
|
|
{
|
|
|
|
colorSchemeSet = enumerateColorSchemes();
|
2014-11-15 20:39:17 +03:00
|
|
|
|
|
|
|
dump();
|
2014-11-01 01:20:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ColorMap::~ColorMap()
|
|
|
|
{
|
2014-08-15 01:19:41 +04:00
|
|
|
}
|
|
|
|
|
2014-11-15 22:59:39 +03:00
|
|
|
const char * ColorMap::defaultColorSchemeName() const
|
|
|
|
{
|
|
|
|
return DEFAULT_COLOR_SCHEME_NAME;
|
|
|
|
}
|
|
|
|
|
2014-08-15 01:19:41 +04:00
|
|
|
const ColorScheme &ColorMap::defaultColorScheme() const
|
|
|
|
{
|
2014-11-03 01:36:23 +03:00
|
|
|
return *findColorScheme(DEFAULT_COLOR_SCHEME_NAME);
|
2014-08-15 01:19:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
const ColorScheme *ColorMap::findColorScheme(const std::string &name) const
|
|
|
|
{
|
2014-11-01 01:20:12 +03:00
|
|
|
for (colorscheme_set_t::const_iterator it = colorSchemeSet.begin();it != colorSchemeSet.end();it++) {
|
2014-09-17 01:27:54 +04:00
|
|
|
RenderColorScheme *scheme = (*it).second.get();
|
|
|
|
if (name == scheme->name()) {
|
|
|
|
return &scheme->colorScheme();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2014-08-15 01:19:41 +04:00
|
|
|
}
|
|
|
|
|
2014-11-15 20:39:17 +03:00
|
|
|
void ColorMap::dump() const
|
|
|
|
{
|
|
|
|
PRINTD("Listing available color schemes...");
|
|
|
|
|
|
|
|
std::list<std::string> names = colorSchemeNames();
|
|
|
|
unsigned int length = 0;
|
|
|
|
for (std::list<std::string>::const_iterator it = names.begin();it != names.end();it++) {
|
|
|
|
length = (*it).length() > length ? (*it).length() : length;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (colorscheme_set_t::const_iterator it = colorSchemeSet.begin();it != colorSchemeSet.end();it++) {
|
|
|
|
const RenderColorScheme *cs = (*it).second.get();
|
|
|
|
const char gui = cs->showInGui() ? 'G' : '-';
|
|
|
|
if (cs->path().empty()) {
|
|
|
|
PRINTDB("%6d:%c: %s (built-in)", cs->index() % gui % boost::io::group(std::setw(length), cs->name()));
|
|
|
|
} else {
|
|
|
|
PRINTDB("%6d:%c: %s from %s", cs->index() % gui % boost::io::group(std::setw(length), cs->name()) % cs->path());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PRINTD("done.");
|
|
|
|
}
|
|
|
|
|
2014-09-17 01:27:54 +04:00
|
|
|
std::list<std::string> ColorMap::colorSchemeNames(bool guiOnly) const
|
2014-08-15 01:19:41 +04:00
|
|
|
{
|
2014-11-01 01:20:12 +03:00
|
|
|
std::list<std::string> colorSchemeNames;
|
|
|
|
for (colorscheme_set_t::const_iterator it = colorSchemeSet.begin();it != colorSchemeSet.end();it++) {
|
2014-11-15 20:39:17 +03:00
|
|
|
const RenderColorScheme *scheme = (*it).second.get();
|
2014-09-17 01:27:54 +04:00
|
|
|
if (guiOnly && !scheme->showInGui()) {
|
|
|
|
continue;
|
2014-08-15 01:19:41 +04:00
|
|
|
}
|
2014-11-01 01:20:12 +03:00
|
|
|
colorSchemeNames.push_back(scheme->name());
|
2014-09-17 01:27:54 +04:00
|
|
|
}
|
|
|
|
|
2014-11-01 01:20:12 +03:00
|
|
|
return colorSchemeNames;
|
2014-08-15 01:19:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Color4f ColorMap::getColor(const ColorScheme &cs, const RenderColor rc)
|
|
|
|
{
|
|
|
|
if (cs.count(rc)) return cs.at(rc);
|
|
|
|
if (ColorMap::inst()->defaultColorScheme().count(rc)) return ColorMap::inst()->defaultColorScheme().at(rc);
|
|
|
|
return Color4f(0, 0, 0, 127);
|
|
|
|
}
|
|
|
|
|
2014-11-30 04:29:29 +03:00
|
|
|
Color4f ColorMap::getColorHSV(const Color4f &col)
|
|
|
|
{
|
|
|
|
float h, s, v;
|
|
|
|
rgbtohsv(col[0], col[1], col[2], h, s, v);
|
|
|
|
return Color4f(h, s, v, col[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate contrast color. Based on the article
|
|
|
|
* http://gamedev.stackexchange.com/questions/38536/given-a-rgb-color-x-how-to-find-the-most-contrasting-color-y
|
|
|
|
*
|
|
|
|
* @param col the input color
|
|
|
|
* @return a color with high contrast to the input color
|
|
|
|
*/
|
|
|
|
Color4f ColorMap::getContrastColor(const Color4f &col)
|
|
|
|
{
|
|
|
|
Color4f hsv = ColorMap::getColorHSV(col);
|
|
|
|
float Y = 0.2126 * col[0] + 0.7152 * col[1] + 0.0722 * col[2];
|
|
|
|
float S = hsv[1];
|
|
|
|
|
|
|
|
if (S < 0.5) {
|
|
|
|
// low saturation, choose between black / white based on luminance Y
|
|
|
|
float val = Y > 0.5 ? 0.0f : 1.0f;
|
|
|
|
return Color4f(val, val, val, 1.0f);
|
|
|
|
} else {
|
|
|
|
float H = 360 * hsv[0];
|
|
|
|
if ((H < 60) || (H > 300)) {
|
|
|
|
return Color4f(0.0f, 1.0f, 1.0f, 1.0f); // red -> cyan
|
|
|
|
} else if (H < 180) {
|
|
|
|
return Color4f(1.0f, 0.0f, 1.0f, 1.0f); // green -> magenta
|
|
|
|
} else {
|
|
|
|
return Color4f(1.0f, 1.0f, 0.0f, 1.0f); // blue -> yellow
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-15 20:39:17 +03:00
|
|
|
void ColorMap::enumerateColorSchemesInPath(colorscheme_set_t &result_set, const fs::path basePath)
|
2014-09-17 01:27:54 +04:00
|
|
|
{
|
2014-11-15 20:39:17 +03:00
|
|
|
const fs::path color_schemes = basePath / "color-schemes" / "render";
|
|
|
|
|
2015-04-26 23:11:51 +03:00
|
|
|
PRINTDB("Enumerating color schemes from '%s'", boosty::stringy(color_schemes).c_str());
|
2014-11-02 01:06:36 +03:00
|
|
|
|
2014-09-17 01:27:54 +04:00
|
|
|
fs::directory_iterator end_iter;
|
|
|
|
|
|
|
|
if (fs::exists(color_schemes) && fs::is_directory(color_schemes)) {
|
|
|
|
for (fs::directory_iterator dir_iter(color_schemes); dir_iter != end_iter; ++dir_iter) {
|
|
|
|
if (!fs::is_regular_file(dir_iter->status())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const fs::path path = (*dir_iter).path();
|
2015-04-26 23:11:51 +03:00
|
|
|
if (!(path.extension() == ".json")) {
|
2014-09-17 01:27:54 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
RenderColorScheme *colorScheme = new RenderColorScheme(path);
|
2014-11-02 01:06:36 +03:00
|
|
|
if (colorScheme->valid() && (findColorScheme(colorScheme->name()) == 0)) {
|
2014-09-17 01:27:54 +04:00
|
|
|
result_set.insert(colorscheme_set_t::value_type(colorScheme->index(), boost::shared_ptr<RenderColorScheme>(colorScheme)));
|
2014-11-15 20:39:17 +03:00
|
|
|
PRINTDB("Found file '%s' with color scheme '%s' and index %d",
|
|
|
|
colorScheme->path() % colorScheme->name() % colorScheme->index());
|
2014-09-17 01:27:54 +04:00
|
|
|
} else {
|
2014-11-15 20:39:17 +03:00
|
|
|
PRINTDB("Invalid file '%s': %s", colorScheme->path() % colorScheme->error());
|
2014-09-17 01:27:54 +04:00
|
|
|
delete colorScheme;
|
|
|
|
}
|
2014-08-15 01:19:41 +04:00
|
|
|
}
|
2014-09-17 01:27:54 +04:00
|
|
|
}
|
2014-11-02 01:06:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ColorMap::colorscheme_set_t ColorMap::enumerateColorSchemes()
|
|
|
|
{
|
|
|
|
colorscheme_set_t result_set;
|
|
|
|
|
2014-11-03 01:36:23 +03:00
|
|
|
RenderColorScheme *defaultColorScheme = new RenderColorScheme();
|
|
|
|
result_set.insert(colorscheme_set_t::value_type(defaultColorScheme->index(),
|
|
|
|
boost::shared_ptr<RenderColorScheme>(defaultColorScheme)));
|
2014-11-13 02:20:01 +03:00
|
|
|
enumerateColorSchemesInPath(result_set, PlatformUtils::resourceBasePath());
|
2014-11-02 01:06:36 +03:00
|
|
|
enumerateColorSchemesInPath(result_set, PlatformUtils::userConfigPath());
|
2014-09-17 01:27:54 +04:00
|
|
|
|
|
|
|
return result_set;
|
2014-12-05 01:29:18 +03:00
|
|
|
}
|