// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2005 Werner Mayer * * * * 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 #include #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(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(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 ColorGradient::getColorModelNames() const { std::vector 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(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; }