Files
create/src/App/ColorModel.cpp
Markus Reitböck d05e2a0431 App: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
should be considered an optimization, not a requirement. In particular, do not explicitly include a
precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
generated precompile header on the compiler command line instead. This is more portable across
the major compilers and is likely to be easier to maintain. It will also avoid warnings being
generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-14 09:47:02 +02:00

505 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 <cstdlib>
#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;
}