From 2b8336157bf8eb134c31f00bf20680dad1722cd7 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 6 Feb 2022 23:14:39 +0100 Subject: [PATCH] Gui: implement Inventor node for color legend --- src/Gui/SoFCColorLegend.cpp | 436 +++++++++++++++++++++++------------- src/Gui/SoFCColorLegend.h | 35 +-- 2 files changed, 298 insertions(+), 173 deletions(-) diff --git a/src/Gui/SoFCColorLegend.cpp b/src/Gui/SoFCColorLegend.cpp index fe8a7c428d..14b728ef14 100644 --- a/src/Gui/SoFCColorLegend.cpp +++ b/src/Gui/SoFCColorLegend.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include +# include # include # include # include @@ -45,18 +46,19 @@ SO_NODE_SOURCE(SoFCColorLegend) /*! Constructor. */ -SoFCColorLegend::SoFCColorLegend() : _fPosX(4.0f), _fPosY(4.0f) +SoFCColorLegend::SoFCColorLegend() : _bbox(4.0f, -4.0f, 4.5f, 4.0f) { - SO_NODE_CONSTRUCTOR(SoFCColorLegend); - _cColRamp.setStyle(App::ColorGradient::FLOW); + SO_NODE_CONSTRUCTOR(SoFCColorLegend); + coords = new SoCoordinate3; + coords->ref(); + labelGroup = new SoSeparator; + labelGroup->ref(); - coords = new SoCoordinate3; - coords->ref(); - labels = new SoSeparator; - labels->ref(); + valueGroup = new SoSeparator; + valueGroup->ref(); - setColorModel(0); - setRange(-0.5f,0.5f,1); + setColorLegend(_currentLegend); + setLegendLabels(_currentLegend, 3); } /*! @@ -64,181 +66,297 @@ SoFCColorLegend::SoFCColorLegend() : _fPosX(4.0f), _fPosY(4.0f) */ SoFCColorLegend::~SoFCColorLegend() { - //delete THIS; - coords->unref(); - labels->unref(); + //delete THIS; + coords->unref(); + labelGroup->unref(); + valueGroup->unref(); } // doc from parent void SoFCColorLegend::initClass(void) { - SO_NODE_INIT_CLASS(SoFCColorLegend,SoFCColorBarBase,"Separator"); + SO_NODE_INIT_CLASS(SoFCColorLegend,SoFCColorBarBase,"Separator"); } void SoFCColorLegend::finish() { - atexit_cleanup(); + atexit_cleanup(); } -void SoFCColorLegend::setMarkerLabel( const SoMFString& label ) +namespace { +std::vector getLabelPositions(int num, const SbBox2f& bbox) { - coinRemoveAllChildren(labels); + std::vector pos; + float fMinY = bbox.getMin()[1]; + float fMaxX = bbox.getMax()[0]; + float fMaxY = bbox.getMax()[1] - 0.5f; - int num = label.getNum(); - if ( num > 1 ) - { - float fStep = 8.0f / ((float)num-1); - SoTransform* trans = new SoTransform; - trans->translation.setValue(_fPosX+0.1f,_fPosY-0.05f+fStep,0.0f); - labels->addChild(trans); - - for ( int i=0; itranslation.setValue(0,-fStep,0); - color->rgb.setValue(0,0,0); - text2->string.setValue( label[i] ); - labels->addChild(trans); - labels->addChild(color); - labels->addChild(text2); - } - } -} - -void SoFCColorLegend::setViewportSize( const SbVec2s& size ) -{ - float fRatio = ((float)size[0])/((float)size[1]); - float fMinX= 4.0f, fMaxX=4.5f; - float fMinY= -4.0f, fMaxY=4.0f; - - if ( fRatio > 1.0f ) - { - fMinX = 4.0f * fRatio; - fMaxX = fMinX+0.5f; - } - else if ( fRatio < 1.0f ) - { - fMinY = -4.0f / fRatio; - fMaxY = 4.0f / fRatio; - } - - _fPosX = fMaxX; - _fPosY = fMaxY; - - // search for the labels - int num=0; - for ( int i=0; igetNumChildren(); 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; jgetNumChildren(); j++ ) - { - if ( labels->getChild(j)->getTypeId() == SoTransform::getClassTypeId() ) - { - if ( first ) - { - first = false; - static_cast(labels->getChild(j))->translation.setValue(fMaxX+0.1f,fMaxY-0.05f+fStep,0.0f); + if (num > 1) { + float fStep = (fMaxY-fMinY) / static_cast(num - 1); + pos.emplace_back(fMaxX + 0.1f, fMaxY + 0.20f + fStep, 0.0f); + for (int i=0; i(labels->getChild(j))->translation.setValue(0,-fStep,0.0f); - } - } } - } - // set the vertices spanning the faces for the color gradient - int ct = coords->point.getNum()/2; - for ( int j=0; jpoint.set1Value(2*j, fMinX, fPosY, 0.0f); - coords->point.set1Value(2*j+1, fMaxX, fPosY, 0.0f); - } + return pos; } -void SoFCColorLegend::setRange( float fMin, float fMax, int prec ) +std::vector getValuePositions(int num, const SbBox2f& bbox) { - SoMFString label; - for (int j=0; j<9; j++) - { - std::stringstream s; - s.precision(prec); - s.setf(std::ios::fixed | std::ios::showpoint | std::ios::showpos); - float fValue = (1.0f-0.125f*(float)j)*fMax + (0.125f*(float)j)*fMin; - s << fValue; - label.set1Value(j, s.str().c_str()); - } + std::vector pos; + float fMinY = bbox.getMin()[1]; + float fMaxX = bbox.getMax()[0]; + float fMaxY = bbox.getMax()[1] - 0.5f; - setMarkerLabel( label ); - _cColRamp.setRange(fMin, fMax); + if (num > 2) { + float fStep = (fMaxY-fMinY) / static_cast(num - 2); + float eps = fStep / 4.0f; + + pos.emplace_back(fMaxX + 0.1f, fMaxY + 0.25f + 1.5f * fStep, 0.0f); + for (int i=0; ipoint.setNum(2*uCtColors); - for ( int i=0; ipoint.set1Value(2*i, fMinX, fPosY, 0.0f); - coords->point.set1Value(2*i+1, fMaxX, fPosY, 0.0f); - } + int num = label.getNum(); + if (num > 1) { + std::vector pos = getLabelPositions(num, _bbox); - // for uCtColors colors we need 2*(uCtColors-1) facets and therefore an array with - // 8*(uCtColors-1) face indices - SoIndexedFaceSet * faceset = new SoIndexedFaceSet; - faceset->coordIndex.setNum(8*(uCtColors-1)); - for ( int j=0; jcoordIndex.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); - } + SoTransform* trans = new SoTransform; + trans->translation.setValue(pos[0]); + labelGroup->addChild(trans); - SoMaterial* mat = new SoMaterial; - //mat->transparency = 0.3f; - mat->diffuseColor.setNum(2*uCtColors); - for ( int k=0; kdiffuseColor.set1Value(2*k, col.r, col.g, col.b); - mat->diffuseColor.set1Value(2*k+1, col.r, col.g, col.b); - } + for (int i=0; ivalue = SoMaterialBinding::PER_VERTEX_INDEXED; - - // first clear the children - if ( getNumChildren() > 0 ) - coinRemoveAllChildren(this); - addChild(labels); - addChild(coords); - addChild(mat); - addChild(matBinding); - addChild(faceset); + trans->translation.setValue(pos[i+1]); + color->rgb.setValue(0, 0, 0); + text2->string.setValue(label[i]); + labelGroup->addChild(trans); + labelGroup->addChild(color); + labelGroup->addChild(text2); + } + } +} + +void SoFCColorLegend::setMarkerValue(const SoMFString& value) +{ + coinRemoveAllChildren(valueGroup); + + int num = value.getNum(); + if (num > 1) { + std::vector pos = getValuePositions(num, _bbox); + + SoTransform* trans = new SoTransform; + trans->translation.setValue(pos[0]); + valueGroup->addChild(trans); + + for (int i=0; itranslation.setValue(pos[i+1]); + color->rgb.setValue(0, 0, 0); + text2->string.setValue(value[i]); + valueGroup->addChild(trans); + valueGroup->addChild(color); + valueGroup->addChild(text2); + } + } +} + +void SoFCColorLegend::setViewportSize(const SbVec2s& size) +{ + // don't know why the parameter range isn't between [-1,+1] + float fRatio = static_cast(size[0]) / static_cast(size[1]); + float fMinX = 4.0f, fMaxX = 4.5f; + float fMinY = -4.0f, fMaxY = 4.0f; + + if (fRatio > 1.0f) { + fMinX = 4.0f * fRatio; + fMaxX = fMinX + 0.5f; + } + else if (fRatio < 1.0f) { + fMinY = -4.0f / fRatio; + fMaxY = 4.0f / fRatio; + } + + _bbox.setBounds(fMinX, fMinY, fMaxX, fMaxY); + + arrangeLabels(_bbox); + arrangeValues(_bbox); + modifyPoints(_bbox); +} + +void SoFCColorLegend::setRange(float fMin, float fMax, int prec) +{ + std::size_t numFields = _currentLegend.hasNumberOfFields(); + for (std::size_t i = 0; i <= numFields; i++) { + float factor = static_cast(i) / numFields; + float value = (1 - factor) * fMin + factor * fMax; + _currentLegend.setValue(i, value); + } + + setColorLegend(_currentLegend); + setLegendLabels(_currentLegend, prec); +} + +void SoFCColorLegend::setLegendLabels(const App::ColorLegend& legend, int prec) +{ + float fMin = legend.getMinValue(); + float fMax = legend.getMaxValue(); + + std::size_t numFields = legend.hasNumberOfFields(); + + SoMFString labels, values; + + float eps = std::pow(10.0f, static_cast(-prec)); + float value = std::min(fabs(fMin), fabs(fMax)); + std::ios::fmtflags flags = value < eps ? (std::ios::scientific | std::ios::showpoint | std::ios::showpos) + : (std::ios::fixed | std::ios::showpoint | std::ios::showpos); + + for (std::size_t i=0; i < numFields; i++) { + std::stringstream s; + s << legend.getText(numFields - 1 - i); + labels.set1Value(i, s.str().c_str()); + } + + for (std::size_t i=0; i <= numFields; i++) { + std::stringstream s; + s.precision(prec); + s.setf(flags); + float fValue = legend.getValue(numFields - i); + s << fValue; + values.set1Value(i, s.str().c_str()); + } + + setMarkerLabel(labels); + setMarkerValue(values); +} + +void SoFCColorLegend::modifyPoints(const SbBox2f& box) +{ + float fMinX = box.getMin()[0]; + float fMinY = box.getMin()[1]; + float fMaxX = box.getMax()[0]; + float fMaxY = box.getMax()[1] - 0.5f; + + // set the vertices spanning the faces for the color legend + int intFields = coords->point.getNum() / 4; + for (int i = 0; i < intFields; i++) { + float w = static_cast(i) / (intFields - 1); + float fPosY1 = w * fMaxY + (1.0f - w) * fMinY; + float fPosY2 = fPosY1 + 0.5f; + coords->point.set1Value(4 * i, fMinX, fPosY1, 0.0f); + coords->point.set1Value(4 * i + 1, fMaxX, fPosY1, 0.0f); + coords->point.set1Value(4 * i + 2, fMaxX, fPosY2, 0.0f); + coords->point.set1Value(4 * i + 3, fMinX, fPosY2, 0.0f); + } +} + +void SoFCColorLegend::arrangeLabels(const SbBox2f& box) +{ + // search for the labels + int num=0; + for (int i=0; igetNumChildren(); i++) { + if (labelGroup->getChild(i)->getTypeId() == SoTransform::getClassTypeId()) + num++; + } + + if (num > 2) { + std::vector pos = getLabelPositions(num-1, box); + + int index = 0; + for (int j=0; jgetNumChildren(); j++) { + if (labelGroup->getChild(j)->getTypeId() == SoTransform::getClassTypeId()) { + static_cast(labelGroup->getChild(j))->translation.setValue(pos[index++]); + } + } + } +} + +void SoFCColorLegend::arrangeValues(const SbBox2f& box) +{ + // search for the labels + int num=0; + for (int i=0; igetNumChildren(); i++) { + if (valueGroup->getChild(i)->getTypeId() == SoTransform::getClassTypeId()) + num++; + } + + if (num > 3) { + std::vector pos = getValuePositions(num-1, box); + + int index = 0; + for (int j=0; jgetNumChildren(); j++) { + if (valueGroup->getChild(j)->getTypeId() == SoTransform::getClassTypeId()) { + static_cast(valueGroup->getChild(j))->translation.setValue(pos[index++]); + } + } + } +} + +void SoFCColorLegend::setColorLegend(const App::ColorLegend& legend) +{ + // create top value field + std::size_t numFields = legend.hasNumberOfFields(); + int intFields = static_cast(numFields); + coords->point.setNum(4 * intFields); + modifyPoints(_bbox); + + // for numFields colors we need numFields quads + SoIndexedFaceSet * faceset = new SoIndexedFaceSet; + faceset->coordIndex.setNum(5 * intFields); + for (int j = 0; j < intFields; j++) { + faceset->coordIndex.set1Value(5*j, 4*j); + faceset->coordIndex.set1Value(5*j+1, 4*j+1); + faceset->coordIndex.set1Value(5*j+2, 4*j+2); + faceset->coordIndex.set1Value(5*j+3, 4*j+3); + faceset->coordIndex.set1Value(5*j+4, SO_END_FACE_INDEX); + } + + SoMaterial* mat = new SoMaterial; + mat->diffuseColor.setNum(intFields); + for (std::size_t k = 0; k < numFields; k++) { + App::Color col = legend.getColor(k); + mat->diffuseColor.set1Value(k, col.r, col.g, col.b); + } + + SoMaterialBinding* matBinding = new SoMaterialBinding; + matBinding->value = SoMaterialBinding::PER_FACE; + + // first clear the children + if (getNumChildren() > 0) + coinRemoveAllChildren(this); + addChild(labelGroup); + addChild(valueGroup); + addChild(coords); + addChild(mat); + addChild(matBinding); + addChild(faceset); } diff --git a/src/Gui/SoFCColorLegend.h b/src/Gui/SoFCColorLegend.h index 40aacbdfcd..4d1034a890 100644 --- a/src/Gui/SoFCColorLegend.h +++ b/src/Gui/SoFCColorLegend.h @@ -25,6 +25,7 @@ #define GUI_SOFCCOLORLEGEND_H #include +#include #include "SoFCColorBar.h" #include @@ -44,7 +45,7 @@ public: static void finish(void); SoFCColorLegend(void); - void setMarkerLabel( const SoMFString& label ); + void setLegendLabels(const App::ColorLegend& legend, int prec=3); /** * Sets the range of the colorbar from the maximum \a fMax to the minimum \a fMin. @@ -52,17 +53,17 @@ public: */ void setRange( float fMin, float fMax, int prec=3 ); /** - * Sets the color model of the underlying color ramp to \a index. + * Updates the node with the given color legend. */ - void setColorModel (std::size_t index); + void setColorLegend (const App::ColorLegend& legend); - unsigned short getColorIndex (float fVal) const { return _cColRamp.getColorIndex(fVal); } - App::Color getColor (float fVal) const { return _cColRamp.getColor(fVal); } - void setOutsideGrayed (bool bVal) { _cColRamp.setOutsideGrayed(bVal); } + unsigned short getColorIndex (float fVal) const { return _currentLegend.getColorIndex(fVal); } + App::Color getColor (float fVal) const { return _currentLegend.getColor(fVal); } + void setOutsideGrayed (bool bVal) { _currentLegend.setOutsideGrayed(bVal); } bool isVisible (float) const { return false; } - float getMinValue (void) const { return _cColRamp.getMinValue(); } - float getMaxValue (void) const { return _cColRamp.getMaxValue(); } - unsigned long countColors (void) const { return _cColRamp.getCountColors(); } + float getMinValue (void) const { return _currentLegend.getMinValue(); } + float getMaxValue (void) const { return _currentLegend.getMaxValue(); } + std::size_t countColors (void) const { return _currentLegend.hasNumberOfFields(); } bool customize() { return false; } const char* getColorBarName() const { return "Color Legend"; } @@ -75,13 +76,19 @@ protected: void setViewportSize( const SbVec2s& size ); virtual ~SoFCColorLegend(); // virtual void redrawHighlighted(SoAction * act, SbBool flag); - - SoCoordinate3* coords; - SoSeparator* labels; +private: + void setMarkerLabel(const SoMFString& label); + void setMarkerValue(const SoMFString& value); + void modifyPoints(const SbBox2f&); + void arrangeLabels(const SbBox2f&); + void arrangeValues(const SbBox2f&); private: - float _fPosX, _fPosY; - App::ColorGradient _cColRamp; + SoCoordinate3* coords; + SoSeparator* labelGroup; + SoSeparator* valueGroup; + SbBox2f _bbox; + App::ColorLegend _currentLegend; }; } // namespace Gui