Files
create/src/App/ColorModel.cpp
Kacper Donat 13fbab9e42 Base: Move App::Color to Base
Every basic data type is stored in Base module, color is standing out as
one that does not. Moving it to Base opens possibilities to integrate it
better with the rest of FreeCAD.
2025-02-17 21:10:26 +01:00

509 lines
15 KiB
C++

/***************************************************************************
* Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <cstdlib>
#endif
#include <Base/Exception.h>
#include "ColorModel.h"
using namespace App;
ColorModelPack ColorModelPack::createRedGreenBlue()
{
ColorModelPack pack {ColorModelBlueGreenRed(),
ColorModelGreenYellowRed(),
ColorModelBlueCyanGreen(),
"Red-Yellow-Green-Cyan-Blue"};
return pack;
}
ColorModelPack ColorModelPack::createBlueGreenRed()
{
ColorModelPack pack {ColorModelRedGreenBlue(),
ColorModelGreenCyanBlue(),
ColorModelRedYellowGreen(),
"Blue-Cyan-Green-Yellow-Red"};
return pack;
}
ColorModelPack ColorModelPack::createWhiteBlack()
{
ColorModelPack pack {ColorModelBlackWhite(),
ColorModelGrayWhite(),
ColorModelBlackGray(),
"White-Black"};
return pack;
}
ColorModelPack ColorModelPack::createBlackWhite()
{
ColorModelPack pack {ColorModelWhiteBlack(),
ColorModelGrayBlack(),
ColorModelWhiteGray(),
"Black-White"};
return pack;
}
ColorModelPack ColorModelPack::createRedWhiteBlue()
{
ColorModelPack pack {ColorModelBlueWhiteRed(),
ColorModelWhiteRed(),
ColorModelBlueWhite(),
"Red-White-Blue"};
return pack;
}
ColorField::ColorField()
{
set(ColorModelBlueGreenRed(), -1.0f, 1.0f, 13);
}
ColorField::ColorField(const ColorModel& rclModel, float fMin, float fMax, std::size_t usCt)
{
set(rclModel, fMin, fMax, usCt);
}
ColorField& ColorField::operator=(const ColorField& rclCF)
{
colorField = rclCF.colorField;
return *this;
}
void ColorField::set(const ColorModel& rclModel, float fMin, float fMax, std::size_t usCt)
{
auto bounds = std::minmax(fMin, fMax);
if (bounds.second <= bounds.first) {
throw Base::ValueError("Maximum must be higher than minimum");
}
this->fMin = bounds.first;
this->fMax = bounds.second;
colorModel = rclModel;
ctColors = std::max<std::size_t>(usCt, colorModel.getCountColors());
rebuild();
}
void ColorField::setColorModel(const ColorModel& rclModel)
{
colorModel = rclModel;
rebuild();
}
void ColorField::rebuild()
{
colorField.resize(ctColors);
std::size_t usStep =
std::min<std::size_t>(ctColors / (colorModel.getCountColors() - 1), ctColors - 1);
std::size_t usInd1 = 0;
std::size_t usInd2 = usStep;
for (std::size_t i = 0; i < (colorModel.getCountColors() - 1); i++) {
interpolate(colorModel.colors[i], usInd1, colorModel.colors[i + 1], usInd2);
usInd1 = usInd2;
if ((i + 1) == (colorModel.getCountColors() - 2)) {
usInd2 = ctColors - 1;
}
else {
usInd2 += usStep;
}
}
fAscent = float(ctColors) / (fMax - fMin);
fConstant = -fAscent * fMin;
}
// fills the array from color 1, index 1 to color 2, index 2
void ColorField::interpolate(Base::Color clCol1, std::size_t usInd1, Base::Color clCol2, std::size_t usInd2)
{
float fStep = 1.0f, fLen = float(usInd2 - usInd1);
colorField[usInd1] = clCol1;
colorField[usInd2] = clCol2;
float fR = (float(clCol2.r) - float(clCol1.r)) / fLen;
float fG = (float(clCol2.g) - float(clCol1.g)) / fLen;
float fB = (float(clCol2.b) - float(clCol1.b)) / fLen;
for (std::size_t i = (usInd1 + 1); i < usInd2; i++) {
float ucR = clCol1.r + fR * fStep;
float ucG = clCol1.g + fG * fStep;
float ucB = clCol1.b + fB * fStep;
colorField[i] = Base::Color(ucR, ucG, ucB);
fStep += 1.0f;
}
}
ColorGradientProfile::ColorGradientProfile() = default;
bool ColorGradientProfile::isEqual(const ColorGradientProfile& cg) const
{
if (tStyle != cg.tStyle) {
return false;
}
if (fMin != cg.fMin) {
return false;
}
if (fMax != cg.fMax) {
return false;
}
if (!visibility.isEqual(cg.visibility)) {
return false;
}
if (tColorModel != cg.tColorModel) {
return false;
}
return true;
}
ColorGradient::ColorGradient()
{
createStandardPacks();
setColorModel();
set(-1.0f, 1.0f, 13, ColorBarStyle::ZERO_BASED, Visibility::Default);
}
ColorGradient::ColorGradient(float fMin,
float fMax,
std::size_t usCtColors,
ColorBarStyle tS,
VisibilityFlags flags)
{
createStandardPacks();
setColorModel();
set(fMin, fMax, usCtColors, tS, flags);
}
void ColorGradient::createStandardPacks()
{
modelPacks.push_back(ColorModelPack::createRedGreenBlue());
modelPacks.push_back(ColorModelPack::createBlueGreenRed());
modelPacks.push_back(ColorModelPack::createRedWhiteBlue());
modelPacks.push_back(ColorModelPack::createWhiteBlack());
modelPacks.push_back(ColorModelPack::createBlackWhite());
}
std::vector<std::string> ColorGradient::getColorModelNames() const
{
std::vector<std::string> names;
names.reserve(modelPacks.size());
for (const auto& it : modelPacks) {
names.push_back(it.description);
}
return names;
}
void ColorGradient::setProfile(const ColorGradientProfile& pro)
{
profile = pro;
setColorModel();
rebuild();
}
void ColorGradient::set(float fMin,
float fMax,
std::size_t usCt,
ColorBarStyle tS,
VisibilityFlags flags)
{
auto bounds = std::minmax(fMin, fMax);
if (bounds.second <= bounds.first) {
throw Base::ValueError("Maximum must be higher than minimum");
}
profile.fMin = bounds.first;
profile.fMax = bounds.second;
profile.ctColors = std::max<std::size_t>(usCt, getMinColors());
profile.tStyle = tS;
profile.visibility = flags;
rebuild();
}
void ColorGradient::rebuild()
{
switch (profile.tStyle) {
case ColorBarStyle::FLOW: {
colorField1.set(currentModelPack.totalModel,
profile.fMin,
profile.fMax,
profile.ctColors);
break;
}
case ColorBarStyle::ZERO_BASED: {
if ((profile.fMin < 0.0f) && (profile.fMax > 0.0f)) {
colorField1.set(currentModelPack.bottomModel,
profile.fMin,
0.0f,
profile.ctColors / 2);
colorField2.set(currentModelPack.topModel,
0.0f,
profile.fMax,
profile.ctColors / 2);
}
else if (profile.fMin >= 0.0f) {
colorField1.set(currentModelPack.topModel, 0.0f, profile.fMax, profile.ctColors);
}
else {
colorField1.set(currentModelPack.bottomModel, profile.fMin, 0.0f, profile.ctColors);
}
break;
}
}
}
std::size_t ColorGradient::getMinColors() const
{
switch (profile.tStyle) {
case ColorBarStyle::FLOW:
return colorField1.getMinColors();
case ColorBarStyle::ZERO_BASED: {
if ((profile.fMin < 0.0f) && (profile.fMax > 0.0f)) {
return colorField1.getMinColors() + colorField2.getMinColors();
}
else {
return colorField1.getMinColors();
}
}
}
return 2;
}
void ColorGradient::setColorModel(std::size_t tModel)
{
profile.tColorModel = tModel;
setColorModel();
rebuild();
}
void ColorGradient::setColorModel()
{
if (profile.tColorModel < modelPacks.size()) {
currentModelPack = modelPacks[profile.tColorModel];
}
switch (profile.tStyle) {
case ColorBarStyle::FLOW: {
colorField1.setColorModel(currentModelPack.totalModel);
colorField2.setColorModel(currentModelPack.bottomModel);
break;
}
case ColorBarStyle::ZERO_BASED: {
colorField1.setColorModel(currentModelPack.topModel);
colorField2.setColorModel(currentModelPack.bottomModel);
break;
}
}
}
ColorLegend::ColorLegend()
{
// default blue, green, red
colorFields.emplace_back(0, 0, 1);
colorFields.emplace_back(0, 1, 0);
colorFields.emplace_back(1, 0, 0);
names.emplace_back("Min");
names.emplace_back("Mid");
names.emplace_back("Max");
values.push_back(-1.0f);
values.push_back(-0.333f);
values.push_back(0.333f);
values.push_back(1.0f);
}
bool ColorLegend::operator==(const ColorLegend& rclCL) const
{
return (colorFields.size() == rclCL.colorFields.size()) && (names.size() == rclCL.names.size())
&& (values.size() == rclCL.values.size())
&& std::equal(colorFields.begin(), colorFields.end(), rclCL.colorFields.begin())
&& std::equal(names.begin(), names.end(), rclCL.names.begin())
&& std::equal(values.begin(), values.end(), rclCL.values.begin())
&& outsideGrayed == rclCL.outsideGrayed;
}
float ColorLegend::getValue(std::size_t ulPos) const
{
if (ulPos < values.size()) {
return values[ulPos];
}
else {
return 0.0f;
}
}
bool ColorLegend::setValue(std::size_t ulPos, float fVal)
{
if (ulPos < values.size()) {
values[ulPos] = fVal;
return true;
}
else {
return false;
}
}
Base::Color ColorLegend::getColor(std::size_t ulPos) const
{
if (ulPos < colorFields.size()) {
return colorFields[ulPos];
}
else {
return Base::Color();
}
}
// color as: 0x00rrggbb
uint32_t ColorLegend::getPackedColor(std::size_t ulPos) const
{
Base::Color clRGB = getColor(ulPos);
return clRGB.getPackedValue();
}
std::string ColorLegend::getText(std::size_t ulPos) const
{
if (ulPos < names.size()) {
return names[ulPos];
}
else {
return "";
}
}
std::size_t ColorLegend::addMin(const std::string& rclName)
{
names.push_front(rclName);
values.push_front(values.front() - 1.0f);
Base::Color clNewRGB;
clNewRGB.r = ((float)rand() / (float)RAND_MAX);
clNewRGB.g = ((float)rand() / (float)RAND_MAX);
clNewRGB.b = ((float)rand() / (float)RAND_MAX);
colorFields.push_front(clNewRGB);
return colorFields.size() - 1;
}
std::size_t ColorLegend::addMax(const std::string& rclName)
{
names.push_back(rclName);
values.push_back(values.back() + 1.0f);
Base::Color clNewRGB;
clNewRGB.r = ((float)rand() / (float)RAND_MAX);
clNewRGB.g = ((float)rand() / (float)RAND_MAX);
clNewRGB.b = ((float)rand() / (float)RAND_MAX);
colorFields.push_back(clNewRGB);
return colorFields.size() - 1;
}
bool ColorLegend::remove(std::size_t ulPos)
{
if (ulPos < colorFields.size()) {
colorFields.erase(colorFields.begin() + ulPos);
names.erase(names.begin() + ulPos);
values.erase(values.begin() + ulPos);
return true;
}
return false;
}
void ColorLegend::removeFirst()
{
if (!colorFields.empty()) {
colorFields.erase(colorFields.begin());
names.erase(names.begin());
values.erase(values.begin());
}
}
void ColorLegend::removeLast()
{
if (!colorFields.empty()) {
colorFields.erase(colorFields.end() - 1);
names.erase(names.end() - 1);
values.erase(values.end() - 1);
}
}
void ColorLegend::resize(std::size_t ulCt)
{
if ((ulCt < 2) || (ulCt == colorFields.size())) {
return;
}
if (ulCt > colorFields.size()) {
int k = ulCt - colorFields.size();
for (int i = 0; i < k; i++) {
addMin("new");
}
}
else {
int k = colorFields.size() - ulCt;
for (int i = 0; i < k; i++) {
removeLast();
}
}
}
bool ColorLegend::setColor(std::size_t ulPos, float ucRed, float ucGreen, float ucBlue)
{
if (ulPos < names.size()) {
colorFields[ulPos] = Base::Color(ucRed, ucGreen, ucBlue);
return true;
}
return false;
}
// color as 0x00rrggbb
bool ColorLegend::setColor(std::size_t ulPos, unsigned long ulColor)
{
unsigned char ucRed = (unsigned char)((ulColor & 0x00ff0000) >> 16);
unsigned char ucGreen = (unsigned char)((ulColor & 0x0000ff00) >> 8);
unsigned char ucBlue = (unsigned char)(ulColor & 0x000000ff);
return setColor(ulPos, ucRed, ucGreen, ucBlue);
}
bool ColorLegend::setText(std::size_t ulPos, const std::string& rclName)
{
if (ulPos < names.size()) {
names[ulPos] = rclName;
return true;
}
return false;
}