"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
456 lines
15 KiB
C++
456 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 <sstream>
|
|
# include <QCoreApplication>
|
|
# include <Inventor/fields/SoMFString.h>
|
|
# include <Inventor/nodes/SoBaseColor.h>
|
|
# include <Inventor/nodes/SoCoordinate3.h>
|
|
# include <Inventor/nodes/SoFont.h>
|
|
# include <Inventor/nodes/SoIndexedFaceSet.h>
|
|
# include <Inventor/nodes/SoMaterial.h>
|
|
# include <Inventor/nodes/SoText2.h>
|
|
# include <Inventor/nodes/SoTransform.h>
|
|
# include <Inventor/nodes/SoTransparencyType.h>
|
|
|
|
|
|
#include <Base/Parameter.h>
|
|
#include "SoFCColorGradient.h"
|
|
#include "SoTextLabel.h"
|
|
#include "Dialogs/DlgSettingsColorGradientImp.h"
|
|
#include "MainWindow.h"
|
|
#include "MDIView.h"
|
|
#include "ViewProvider.h"
|
|
|
|
|
|
using namespace Gui;
|
|
|
|
constexpr const float xMin = 5.0F;
|
|
constexpr const float xMax = 5.5F;
|
|
constexpr const float yMin = -4.0F;
|
|
constexpr const float yMax = 4.0F;
|
|
constexpr const float spaceX = 0.1F;
|
|
constexpr const float spaceY = 0.05F;
|
|
constexpr const float defaultMin = -0.5F;
|
|
constexpr const float defaultMax = 0.5F;
|
|
constexpr const float upperLimit = 10000.0F;
|
|
|
|
|
|
SO_NODE_SOURCE(SoFCColorGradient)
|
|
|
|
/*!
|
|
Constructor.
|
|
*/
|
|
SoFCColorGradient::SoFCColorGradient()
|
|
: _bbox(xMin, yMin, xMax, yMax)
|
|
{
|
|
SO_NODE_CONSTRUCTOR(SoFCColorGradient);
|
|
coords = new SoCoordinate3;
|
|
coords->ref();
|
|
labels = new SoSeparator;
|
|
labels->ref();
|
|
|
|
_cColGrad.setStyle(App::ColorBarStyle::FLOW);
|
|
setColorModel(0);
|
|
SoFCColorGradient::setRange(defaultMin, defaultMax, 1);
|
|
}
|
|
|
|
/*!
|
|
Destructor.
|
|
*/
|
|
SoFCColorGradient::~SoFCColorGradient()
|
|
{
|
|
//delete THIS;
|
|
coords->unref();
|
|
labels->unref();
|
|
}
|
|
|
|
// doc from parent
|
|
void SoFCColorGradient::initClass()
|
|
{
|
|
SO_NODE_INIT_CLASS(SoFCColorGradient, SoFCColorBarBase, "Separator");
|
|
}
|
|
|
|
void SoFCColorGradient::finish()
|
|
{
|
|
atexit_cleanup();
|
|
}
|
|
|
|
const char* SoFCColorGradient::getColorBarName() const
|
|
{
|
|
return QT_TRANSLATE_NOOP("QObject", "Color Gradient");
|
|
}
|
|
|
|
void SoFCColorGradient::applyFormat(const SoLabelTextFormat& fmt)
|
|
{
|
|
auto textColor = Base::Color(fmt.textColor);
|
|
|
|
for (int j = 0; j < labels->getNumChildren(); j++) {
|
|
if (labels->getChild(j)->getTypeId() == SoBaseColor::getClassTypeId()) {
|
|
auto baseColor = static_cast<SoBaseColor*>(labels->getChild(j)); // NOLINT
|
|
baseColor->rgb.setValue(textColor.r, textColor.g, textColor.b);
|
|
}
|
|
else if (labels->getChild(j)->getTypeId() == SoFont::getClassTypeId()) {
|
|
auto font = static_cast<SoFont*>(labels->getChild(j)); // NOLINT
|
|
font->size.setValue(static_cast<float>(fmt.textSize));
|
|
}
|
|
}
|
|
}
|
|
|
|
void SoFCColorGradient::setMarkerLabel(const SoMFString& label)
|
|
{
|
|
coinRemoveAllChildren(labels);
|
|
|
|
int num = label.getNum();
|
|
if (num > 1) {
|
|
SbVec2f maxPt = _bbox.getMax();
|
|
SbVec2f minPt = _bbox.getMin();
|
|
float fStep = (maxPt[1] - minPt[1]) / ((float)num - 1);
|
|
auto trans = new SoTransform;
|
|
|
|
SoLabelTextFormat fmt = getFormat();
|
|
auto textColor = Base::Color(fmt.textColor);
|
|
auto textFont = new SoFont;
|
|
auto color = new SoBaseColor;
|
|
textFont->name.setValue("Helvetica,Arial,Times New Roman");
|
|
textFont->size.setValue(static_cast<float>(fmt.textSize));
|
|
trans->translation.setValue(maxPt[0] + spaceX, maxPt[1] - spaceY + fStep, 0.0F);
|
|
color->rgb.setValue(textColor.r, textColor.g, textColor.b);
|
|
labels->addChild(trans);
|
|
labels->addChild(color);
|
|
labels->addChild(textFont);
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
auto trans = new SoTransform;
|
|
auto text2 = new SoColorBarLabel;
|
|
trans->translation.setValue(0, -fStep, 0);
|
|
text2->string.setValue(label[i]);
|
|
labels->addChild(trans);
|
|
labels->addChild(text2);
|
|
}
|
|
}
|
|
|
|
setModified();
|
|
}
|
|
|
|
void SoFCColorGradient::setViewportSize(const SbVec2s& size)
|
|
{
|
|
float fMinX {};
|
|
float fMinY {};
|
|
float fMaxX {};
|
|
float fMaxY {};
|
|
float boxWidth = getBounds(size, fMinX, fMinY, fMaxX, fMaxY);
|
|
|
|
// search for the labels
|
|
int num = 0;
|
|
for (int i = 0; i < labels->getNumChildren(); i++) {
|
|
if (labels->getChild(i)->getTypeId() == SoTransform::getClassTypeId()) {
|
|
num++;
|
|
}
|
|
}
|
|
|
|
if (num > 2) {
|
|
bool first = true;
|
|
float fStep = (fMaxY - fMinY) / ((float)num - 2);
|
|
|
|
for (int j = 0; j < labels->getNumChildren(); j++) {
|
|
if (labels->getChild(j)->getTypeId() == SoTransform::getClassTypeId()) {
|
|
auto transform = static_cast<SoTransform*>(labels->getChild(j)); // NOLINT
|
|
if (first) {
|
|
first = false;
|
|
transform->translation.setValue(fMaxX + spaceX - boxWidth,
|
|
fMaxY - spaceY + fStep,
|
|
0.0F);
|
|
}
|
|
else {
|
|
transform->translation.setValue(0, -fStep, 0.0F);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// gradient bar is shifted to the left by width of the labels to assure that labels are fully visible
|
|
_bbox.setBounds(fMinX - boxWidth, fMinY, fMaxX - boxWidth, fMaxY);
|
|
modifyPoints(_bbox);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool isScientific(float fMin, float fMax, int prec, int numColors)
|
|
{
|
|
const float base10 = 10.0F;
|
|
float eps = std::pow(base10, static_cast<float>(-prec));
|
|
float value_min = std::min<float>(fabs(fMin), fabs(fMax));
|
|
float value_max = std::max<float>(fabs(fMin), fabs(fMax));
|
|
|
|
if (value_min < eps && value_min > 0.0F) {
|
|
return true;
|
|
}
|
|
|
|
if ((value_max - value_min) < eps * static_cast<float>(numColors -1)) {
|
|
return true;
|
|
}
|
|
|
|
return value_max > upperLimit;
|
|
}
|
|
|
|
std::ios::fmtflags getFormatFlags(float fMin, float fMax, int prec, int numColors)
|
|
{
|
|
bool scientific = isScientific(fMin, fMax, prec, numColors);
|
|
std::ios::fmtflags flags = scientific ? (std::ios::scientific | std::ios::showpoint | std::ios::showpos)
|
|
: (std::ios::fixed | std::ios::showpoint | std::ios::showpos);
|
|
return flags;
|
|
}
|
|
|
|
std::string getLabelText(float value, int prec, std::ios::fmtflags flags)
|
|
{
|
|
std::stringstream str;
|
|
str.precision(prec);
|
|
str.setf(flags);
|
|
str << value;
|
|
return str.str();
|
|
}
|
|
}
|
|
|
|
void SoFCColorGradient::setRange(float fMin, float fMax, int prec)
|
|
{
|
|
_cColGrad.setRange(fMin, fMax);
|
|
int numColors = static_cast<int>(_cColGrad.getCountColors());
|
|
|
|
SoMFString label;
|
|
std::ios::fmtflags flags = getFormatFlags(fMin, fMax, prec, numColors);
|
|
|
|
// write the labels
|
|
int i = 0;
|
|
std::vector<float> marks = getMarkerValues(fMin, fMax, numColors);
|
|
for (auto it : marks) {
|
|
std::string text = getLabelText(it, prec, flags);
|
|
label.set1Value(i++, text.c_str());
|
|
}
|
|
|
|
setMarkerLabel(label);
|
|
}
|
|
|
|
bool SoFCColorGradient::isZeroBased(float fMin, float fMax) const
|
|
{
|
|
return (fMin < 0.0F && fMax > 0.0F && _cColGrad.getStyle() == App::ColorBarStyle::ZERO_BASED);
|
|
}
|
|
|
|
std::vector<float> SoFCColorGradient::getMarkerValues(float fMin, float fMax, int count) const
|
|
{
|
|
std::vector<float> labels;
|
|
|
|
// the middle of the bar is zero
|
|
if (isZeroBased(fMin, fMax)) {
|
|
if (count % 2 == 0) {
|
|
count++;
|
|
}
|
|
int half = count / 2;
|
|
for (int j = 0; j < half + 1; j++) {
|
|
float w = (float)j / ((float)half);
|
|
float fValue = (1.0F - w) * fMax;
|
|
labels.push_back(fValue);
|
|
}
|
|
for (int k = half + 1; k < count; k++) {
|
|
float w = (float)(k - half + 1) / ((float)(count - half));
|
|
float fValue = w * fMin;
|
|
labels.push_back(fValue);
|
|
}
|
|
}
|
|
else { // either not zero based or 0 is not in between [fMin,fMax]
|
|
for (int j = 0; j < count; j++) {
|
|
float w = (float)j / ((float)count - 1.0F);
|
|
float fValue = (1.0F - w) * fMax + w * fMin;
|
|
labels.push_back(fValue);
|
|
}
|
|
}
|
|
|
|
return labels;
|
|
}
|
|
|
|
void SoFCColorGradient::modifyPoints(const SbBox2f& box)
|
|
{
|
|
float fMinX = box.getMin()[0];
|
|
float fMinY = box.getMin()[1];
|
|
float fMaxX = box.getMax()[0];
|
|
float fMaxY = box.getMax()[1];
|
|
|
|
// set the vertices spanning the faces for the color gradient
|
|
int intFields = coords->point.getNum() / 2;
|
|
for (int i = 0; i < intFields; i++) {
|
|
float w = static_cast<float>(i) / static_cast<float>(intFields - 1);
|
|
float fPosY = (1.0F - w) * fMaxY + w * fMinY;
|
|
coords->point.set1Value(2 * i, fMinX, fPosY, 0.0F);
|
|
coords->point.set1Value(2 * i + 1, fMaxX, fPosY, 0.0F);
|
|
}
|
|
}
|
|
|
|
void SoFCColorGradient::setColorModel(std::size_t index)
|
|
{
|
|
_cColGrad.setColorModel(index);
|
|
rebuildGradient();
|
|
}
|
|
|
|
void SoFCColorGradient::setColorStyle(App::ColorBarStyle tStyle)
|
|
{
|
|
_cColGrad.setStyle(tStyle);
|
|
rebuildGradient();
|
|
}
|
|
|
|
SoIndexedFaceSet* SoFCColorGradient::createFaceSet(int numFaces) const
|
|
{
|
|
// NOLINTBEGIN
|
|
// for numFaces colors we need 2*(numFaces-1) faces and therefore an array with
|
|
// 8*(numFaces-1) face indices
|
|
auto faceset = new SoIndexedFaceSet;
|
|
faceset->coordIndex.setNum(8 * (numFaces - 1));
|
|
for (int j = 0; j < numFaces - 1; j++) {
|
|
faceset->coordIndex.set1Value(8 * j, 2 * j);
|
|
faceset->coordIndex.set1Value(8 * j + 1, 2 * j + 3);
|
|
faceset->coordIndex.set1Value(8 * j + 2, 2 * j + 1);
|
|
faceset->coordIndex.set1Value(8 * j + 3, SO_END_FACE_INDEX);
|
|
faceset->coordIndex.set1Value(8 * j + 4, 2 * j);
|
|
faceset->coordIndex.set1Value(8 * j + 5, 2 * j + 2);
|
|
faceset->coordIndex.set1Value(8 * j + 6, 2 * j + 3);
|
|
faceset->coordIndex.set1Value(8 * j + 7, SO_END_FACE_INDEX);
|
|
}
|
|
// NOLINTEND
|
|
|
|
return faceset;
|
|
}
|
|
|
|
SoTransparencyType* SoFCColorGradient::createTransparencyType() const
|
|
{
|
|
auto ttype = new SoTransparencyType;
|
|
ttype->value = SoGLRenderAction::DELAYED_BLEND;
|
|
return ttype;
|
|
}
|
|
|
|
SoMaterial* SoFCColorGradient::createMaterial() const
|
|
{
|
|
App::ColorModel model = _cColGrad.getColorModel();
|
|
int numColors = static_cast<int>(model.getCountColors());
|
|
|
|
auto mat = new SoMaterial;
|
|
mat->diffuseColor.setNum(2 * numColors);
|
|
for (int k = 0; k < numColors; k++) {
|
|
Base::Color col = model.colors[numColors - k - 1];
|
|
mat->diffuseColor.set1Value(2 * k, col.r, col.g, col.b);
|
|
mat->diffuseColor.set1Value(2 * k + 1, col.r, col.g, col.b);
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
|
|
SoMaterialBinding* SoFCColorGradient::createMaterialBinding() const
|
|
{
|
|
auto matBinding = new SoMaterialBinding;
|
|
matBinding->value = SoMaterialBinding::PER_VERTEX_INDEXED;
|
|
return matBinding;
|
|
}
|
|
|
|
int SoFCColorGradient::getNumColors() const
|
|
{
|
|
App::ColorModel model = _cColGrad.getColorModel();
|
|
return static_cast<int>(model.getCountColors());
|
|
}
|
|
|
|
void SoFCColorGradient::setCoordSize(int numPoints)
|
|
{
|
|
coords->point.setNum(numPoints);
|
|
}
|
|
|
|
void SoFCColorGradient::rebuildGradient()
|
|
{
|
|
int numColors = getNumColors();
|
|
|
|
setCoordSize(2 * numColors);
|
|
modifyPoints(_bbox);
|
|
|
|
auto faceset = createFaceSet(numColors);
|
|
auto ttype = createTransparencyType();
|
|
auto mat = createMaterial();
|
|
auto matBinding = createMaterialBinding();
|
|
|
|
// first clear the children
|
|
if (getNumChildren() > 0) {
|
|
coinRemoveAllChildren(this);
|
|
}
|
|
|
|
addChild(ttype);
|
|
addChild(labels);
|
|
addChild(coords);
|
|
addChild(mat);
|
|
addChild(matBinding);
|
|
addChild(faceset);
|
|
}
|
|
|
|
bool SoFCColorGradient::isVisible(float fVal) const
|
|
{
|
|
if (_cColGrad.isOutsideInvisible()) {
|
|
return !_cColGrad.isOutOfRange(fVal);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SoFCColorGradient::customize(SoFCColorBarBase* parentNode)
|
|
{
|
|
QWidget* parent = Gui::getMainWindow()->activeWindow();
|
|
Gui::Dialog::DlgSettingsColorGradientImp dlg(_cColGrad, parent);
|
|
App::ColorGradientProfile profile = _cColGrad.getProfile();
|
|
dlg.setNumberOfDecimals(_precision, profile.fMin, profile.fMax);
|
|
|
|
QPoint pos(QCursor::pos());
|
|
pos += QPoint(int(-1.1 * dlg.width()), int(-0.1 * dlg.height())); // NOLINT
|
|
dlg.move(pos);
|
|
|
|
auto applyProfile = [&](const App::ColorGradientProfile& pro, int precision) {
|
|
_cColGrad.setProfile(pro);
|
|
setRange(pro.fMin, pro.fMax, precision);
|
|
rebuildGradient();
|
|
|
|
triggerChange(parentNode);
|
|
};
|
|
QObject::connect(&dlg, &Gui::Dialog::DlgSettingsColorGradientImp::colorModelChanged,
|
|
[&] {
|
|
try {
|
|
applyProfile(dlg.getProfile(), dlg.numberOfDecimals());
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
e.reportException();
|
|
}
|
|
});
|
|
|
|
if (dlg.exec() != QDialog::Accepted) {
|
|
int decimals = dlg.numberOfDecimals();
|
|
if (!profile.isEqual(dlg.getProfile()) || decimals != _precision) {
|
|
applyProfile(profile, _precision);
|
|
}
|
|
}
|
|
else {
|
|
_precision = dlg.numberOfDecimals();
|
|
}
|
|
}
|