void ccRenderingTools::DrawColorRamp(const CC_DRAW_CONTEXT& context, const ccScalarField* sf, ccGLWindow* win, int glW, int glH, float renderZoom/*=1.0f*/) { if (!sf || !sf->getColorScale() || !win) { return; } //get the set of OpenGL functions (version 2.1) QOpenGLFunctions_2_1 *glFunc = context.glFunctions<QOpenGLFunctions_2_1>(); assert(glFunc != nullptr); if (glFunc == nullptr) return; bool logScale = sf->logScale(); bool symmetricalScale = sf->symmetricalScale(); bool alwaysShowZero = sf->isZeroAlwaysShown(); //set of particular values //DGM: we work with doubles for maximum accuracy ccColorScale::LabelSet keyValues; bool customLabels = false; try { ccColorScale::Shared colorScale = sf->getColorScale(); if (colorScale && colorScale->customLabels().size() >= 2) { keyValues = colorScale->customLabels(); if (alwaysShowZero) { keyValues.insert(0.0); } customLabels = true; } else if (!logScale) { keyValues.insert(sf->displayRange().min()); keyValues.insert(sf->displayRange().start()); keyValues.insert(sf->displayRange().stop()); keyValues.insert(sf->displayRange().max()); keyValues.insert(sf->saturationRange().min()); keyValues.insert(sf->saturationRange().start()); keyValues.insert(sf->saturationRange().stop()); keyValues.insert(sf->saturationRange().max()); if (symmetricalScale) keyValues.insert(-sf->saturationRange().max()); if (alwaysShowZero) keyValues.insert(0.0); } else { ScalarType minDisp = sf->displayRange().min(); ScalarType maxDisp = sf->displayRange().max(); ConvertToLogScale(minDisp, maxDisp); keyValues.insert(minDisp); keyValues.insert(maxDisp); ScalarType startDisp = sf->displayRange().start(); ScalarType stopDisp = sf->displayRange().stop(); ConvertToLogScale(startDisp, stopDisp); keyValues.insert(startDisp); keyValues.insert(stopDisp); keyValues.insert(sf->saturationRange().min()); keyValues.insert(sf->saturationRange().start()); keyValues.insert(sf->saturationRange().stop()); keyValues.insert(sf->saturationRange().max()); } } catch (const std::bad_alloc&) { //not enough memory return; } //magic fix (for infinite values!) { for (ccColorScale::LabelSet::iterator it = keyValues.begin(); it != keyValues.end(); ++it) { #if defined(CC_WINDOWS) && defined(_MSC_VER) if (!_finite(*it)) #else if (!std::isfinite(*it)) #endif { bool minusInf = (*it < 0); keyValues.erase(it); if (minusInf) keyValues.insert(-std::numeric_limits<ScalarType>::max()); else keyValues.insert(std::numeric_limits<ScalarType>::max()); it = keyValues.begin(); //restart the process (easier than trying to be intelligent here ;) } } } //Internally, the elements in a set are already sorted //std::sort(keyValues.begin(), keyValues.end()); if (!customLabels && !sf->areNaNValuesShownInGrey()) { //remove 'hidden' values if (!logScale) { for (ccColorScale::LabelSet::iterator it = keyValues.begin(); it != keyValues.end(); ) { if (!sf->displayRange().isInRange(static_cast<ScalarType>(*it)) && (!alwaysShowZero || *it != 0)) //we keep zero if the user has explicitely asked for it! { ccColorScale::LabelSet::iterator toDelete = it; ++it; keyValues.erase(toDelete); } else { ++it; } } } else { //convert actual display range to log scale //(we can't do the opposite, otherwise we get accuracy/round-off issues!) ScalarType dispMin = sf->displayRange().start(); ScalarType dispMax = sf->displayRange().stop(); ConvertToLogScale(dispMin, dispMax); for (ccColorScale::LabelSet::iterator it = keyValues.begin(); it != keyValues.end(); ) { if (*it >= dispMin && *it <= dispMax) { ++it; } else { ccColorScale::LabelSet::iterator toDelete = it; ++it; keyValues.erase(toDelete); } } } } const ccGui::ParamStruct& displayParams = win->getDisplayParameters(); //default color: text color const ccColor::Rgbub& textColor = displayParams.textDefaultCol; //histogram? const ccScalarField::Histogram histogram = sf->getHistogram(); bool showHistogram = (displayParams.colorScaleShowHistogram && !logScale && histogram.maxValue != 0 && histogram.size() > 1); //display area QFont font = win->getTextDisplayFont(); //takes rendering zoom into account! const int strHeight = static_cast<int>(displayParams.defaultFontSize * renderZoom); //QFontMetrics(font).height() --> always returns the same value?! const int scaleWidth = static_cast<int>(displayParams.colorScaleRampWidth * renderZoom); const int scaleMaxHeight = (keyValues.size() > 1 ? std::max(glH - static_cast<int>(140 * renderZoom), 2 * strHeight) : scaleWidth); //if 1 value --> we draw a cube //centered orthoprojective view (-halfW,-halfH,halfW,halfH) int halfW = (glW >> 1); int halfH = (glH >> 1); //top-right corner of the scale ramp const int xShift = static_cast<int>(20 * renderZoom) + (showHistogram ? scaleWidth / 2 : 0); const int yShift = halfH - scaleMaxHeight / 2 - static_cast<int>(10 * renderZoom); glFunc->glPushAttrib(GL_DEPTH_BUFFER_BIT); glFunc->glDisable(GL_DEPTH_TEST); std::vector<double> sortedKeyValues(keyValues.begin(),keyValues.end()); double maxRange = sortedKeyValues.back()-sortedKeyValues.front(); //display color ramp { glFunc->glPushAttrib(GL_LINE_BIT); glFunc->glLineWidth(renderZoom); //(x,y): current display area coordinates (top-left corner) int x = halfW-xShift-scaleWidth; int y = halfH-yShift-scaleMaxHeight; if (keyValues.size() > 1) { int histoStart = x + scaleWidth + std::min(std::max(scaleWidth / 8, 3), static_cast<int>(15 * renderZoom)); glFunc->glBegin(GL_LINES); for (int j=0; j<scaleMaxHeight; ++j) { double baseValue = sortedKeyValues.front() + (j * maxRange) / scaleMaxHeight; double value = baseValue; if (logScale) { value = exp(value*c_log10); } const ColorCompType* col = sf->getColor(static_cast<ScalarType>(value)); if (!col) { //special case: if we have user-defined labels, we want all the labels to be displayed with their associated color if (customLabels) { assert(sf->getColorScale() && !sf->getColorScale()->isRelative()); col = sf->getColorScale()->getColorByValue(value, ccColor::lightGrey.rgba); } else { col = ccColor::lightGrey.rgba; } } assert(col); glFunc->glColor3ubv(col); glFunc->glVertex2i(x,y+j); glFunc->glVertex2i(x+scaleWidth,y+j); if (showHistogram) { double bind = (value - sf->displayRange().min())*(histogram.size() - 1) / sf->displayRange().maxRange(); int bin = static_cast<int>(floor(bind)); double hVal = 0.0; if (bin >= 0 && bin < static_cast<int>(histogram.size())) //in symmetrical case we can get values outside of the real SF range { hVal = histogram[bin]; if (bin+1 < static_cast<int>(histogram.size())) { //linear interpolation double alpha = bind-static_cast<double>(bin); hVal = (1.0-alpha) * hVal + alpha * histogram[bin+1]; } } int xSpan = std::max(static_cast<int>(hVal / histogram.maxValue * (scaleWidth/2)),1); glFunc->glVertex2i(histoStart,y+j); glFunc->glVertex2i(histoStart+xSpan,y+j); } } glFunc->glEnd(); } else { //if there's a unique (visible) scalar value, we only draw a square! double value = sortedKeyValues.front(); if (logScale) value = exp(value*c_log10); const ColorCompType* col = sf->getColor(static_cast<ScalarType>(value)); glFunc->glColor3ubv(col ? col : ccColor::lightGrey.rgba); glFunc->glBegin(GL_POLYGON); glFunc->glVertex2i(x,y); glFunc->glVertex2i(x+scaleWidth,y); glFunc->glVertex2i(x+scaleWidth,y+scaleMaxHeight-1); glFunc->glVertex2i(x,y+scaleMaxHeight-1); glFunc->glEnd(); } //scale border const ccColor::Rgbub& lineColor = textColor; glFunc->glColor3ubv(lineColor.rgb); glFunc->glLineWidth(2.0f * renderZoom); glFunc->glEnable(GL_LINE_SMOOTH); glFunc->glBegin(GL_LINE_LOOP); glFunc->glVertex2i(x,y); glFunc->glVertex2i(x+scaleWidth,y); glFunc->glVertex2i(x+scaleWidth,y+scaleMaxHeight); glFunc->glVertex2i(x,y+scaleMaxHeight); glFunc->glEnd(); glFunc->glPopAttrib(); } //display labels { //list of labels to draw vlabelSet drawnLabels; //add first label drawnLabels.push_back(vlabel(0, 0, strHeight, sortedKeyValues.front())); if (keyValues.size() > 1) { //add last label drawnLabels.push_back(vlabel(scaleMaxHeight, scaleMaxHeight - strHeight, scaleMaxHeight, sortedKeyValues.back())); } //we try to display the other keyPoints (if any) if (keyValues.size() > 2) { assert(maxRange > 0.0); const int minGap = strHeight; for (size_t i=1; i<keyValues.size()-1; ++i) { int yScale = static_cast<int>((sortedKeyValues[i]-sortedKeyValues[0]) * scaleMaxHeight / maxRange); vlabelPair nLabels = GetVLabelsAround(yScale,drawnLabels); assert(nLabels.first != drawnLabels.end() && nLabels.second != drawnLabels.end()); if ( (nLabels.first == drawnLabels.end() || nLabels.first->yMax <= yScale - minGap) && (nLabels.second == drawnLabels.end() || nLabels.second->yMin >= yScale + minGap)) { //insert it at the right place (so as to keep a sorted list!) drawnLabels.insert(nLabels.second,vlabel(yScale,yScale-strHeight/2,yScale+strHeight/2,sortedKeyValues[i])); } } } //now we recursively display labels for which we have some room left if (!customLabels && drawnLabels.size() > 1) { const int minGap = strHeight*2; size_t drawnLabelsBefore = 0; //just to init the loop size_t drawnLabelsAfter = drawnLabels.size(); //proceed until no more label can be inserted while (drawnLabelsAfter > drawnLabelsBefore) { drawnLabelsBefore = drawnLabelsAfter; vlabelSet::iterator it1 = drawnLabels.begin(); vlabelSet::iterator it2 = it1; ++it2; for (; it2 != drawnLabels.end(); ++it2) { if (it1->yMax + 2*minGap < it2->yMin) { //insert label double val = (it1->val + it2->val)/2.0; int yScale = static_cast<int>((val-sortedKeyValues[0]) * scaleMaxHeight / maxRange); //insert it at the right place (so as to keep a sorted list!) drawnLabels.insert(it2,vlabel(yScale,yScale-strHeight/2,yScale+strHeight/2,val)); } it1 = it2; } drawnLabelsAfter = drawnLabels.size(); } } //display labels //Some versions of Qt seem to need glColorf instead of glColorub! (see https://bugreports.qt-project.org/browse/QTBUG-6217) glFunc->glColor3f(textColor.r / 255.0f, textColor.g / 255.0f, textColor.b / 255.0f); //Scalar field name const char* sfName = sf->getName(); if (sfName) { //QString sfTitle = QString("[%1]").arg(sfName); QString sfTitle(sfName); if (sf->getGlobalShift() != 0) sfTitle += QString("[Shifted]"); if (logScale) sfTitle += QString("[Log scale]"); //we leave some (vertical) space for the top-most label! win->displayText(sfTitle, glW-xShift, glH-yShift+strHeight, ccGLWindow::ALIGN_HRIGHT | ccGLWindow::ALIGN_VTOP, 0, 0, &font); } //precision (same as color scale) const unsigned precision = displayParams.displayedNumPrecision; //format const char format = (sf->logScale() ? 'E' : 'f'); //tick const int tickSize = static_cast<int>(4 * renderZoom); //for labels const int x = glW-xShift-scaleWidth-2*tickSize-1; const int y = glH-yShift-scaleMaxHeight; //for ticks const int xTick = halfW-xShift-scaleWidth-tickSize-1; const int yTick = halfH-yShift-scaleMaxHeight; for (vlabelSet::iterator it = drawnLabels.begin(); it != drawnLabels.end(); ++it) { vlabelSet::iterator itNext = it; ++itNext; //position unsigned char align = ccGLWindow::ALIGN_HRIGHT; if (it == drawnLabels.begin()) align |= ccGLWindow::ALIGN_VTOP; else if (itNext == drawnLabels.end()) align |= ccGLWindow::ALIGN_VBOTTOM; else align |= ccGLWindow::ALIGN_VMIDDLE; double value = it->val; if (logScale) value = exp(value*c_log10); win->displayText(QString::number(value,format,precision), x, y+it->yPos, align, 0, 0, &font); glFunc->glBegin(GL_LINES); glFunc->glVertex2i(xTick,yTick+it->yPos); glFunc->glVertex2i(xTick+tickSize,yTick+it->yPos); glFunc->glEnd(); } } glFunc->glPopAttrib(); }
void ccRenderingTools::DrawColorRamp(const CC_DRAW_CONTEXT& context) { const ccScalarField* sf = context.sfColorScaleToDisplay; if (!sf || !sf->getColorScale()) return; //#define USE_OLD_SCALE_RENDERING #ifndef USE_OLD_SCALE_RENDERING ccGLWindow* win = static_cast<ccGLWindow*>(context._win); if (!win) return; bool logScale = sf->logScale(); bool symmetricalScale = sf->symmetricalScale(); bool alwaysShowZero = sf->isZeroAlwaysShown(); //set of particular values //DGM: we work with doubles for maximum accuracy std::set<double> keyValues; if (!logScale) { keyValues.insert(sf->displayRange().min()); keyValues.insert(sf->displayRange().start()); keyValues.insert(sf->displayRange().stop()); keyValues.insert(sf->displayRange().max()); keyValues.insert(sf->saturationRange().min()); keyValues.insert(sf->saturationRange().start()); keyValues.insert(sf->saturationRange().stop()); keyValues.insert(sf->saturationRange().max()); if (symmetricalScale) keyValues.insert(-sf->saturationRange().max()); if (alwaysShowZero) keyValues.insert(0.0); } else { ScalarType minDisp = sf->displayRange().min(); ScalarType maxDisp = sf->displayRange().max(); ConvertToLogScale(minDisp,maxDisp); keyValues.insert(minDisp); keyValues.insert(maxDisp); ScalarType startDisp = sf->displayRange().start(); ScalarType stopDisp = sf->displayRange().stop(); ConvertToLogScale(startDisp,stopDisp); keyValues.insert(startDisp); keyValues.insert(stopDisp); keyValues.insert(sf->saturationRange().min()); keyValues.insert(sf->saturationRange().start()); keyValues.insert(sf->saturationRange().stop()); keyValues.insert(sf->saturationRange().max()); } //Internally, the elements in a set are already sorted //std::sort(keyValues.begin(),keyValues.end()); if (!sf->areNaNValuesShownInGrey()) { //remove 'hidden' values if (!logScale) { for (std::set<double>::iterator it = keyValues.begin(); it != keyValues.end(); ) { if (!sf->displayRange().isInRange(static_cast<ScalarType>(*it)) && (!alwaysShowZero || *it != 0)) //we keep zero if the user has explicitely asked for it! { std::set<double>::iterator toDelete = it; ++it; keyValues.erase(toDelete); } else { ++it; } } } else { //convert actual display range to log scale //(we can't do the opposite, otherwise we get accuracy/round-off issues!) ScalarType dispMin = sf->displayRange().start(); ScalarType dispMax = sf->displayRange().stop(); ConvertToLogScale(dispMin,dispMax); for (std::set<double>::iterator it = keyValues.begin(); it != keyValues.end(); ) { if (*it >= dispMin && *it <= dispMax) { ++it; } else { std::set<double>::iterator toDelete = it; ++it; keyValues.erase(toDelete); } } } } //Font metrics for proper display of labels! QFontMetrics strMetrics(win->font()); //default color: text color const unsigned char* textColor = ccGui::Parameters().textDefaultCol; //histogram? const::ccScalarField::Histogram histogram = sf->getHistogram(); bool showHistogram = (ccGui::Parameters().colorScaleShowHistogram && !logScale && histogram.maxValue != 0 && histogram.size() > 1); //display area const int strHeight = strMetrics.height(); const int scaleWidth = ccGui::Parameters().colorScaleRampWidth; const int scaleMaxHeight = (keyValues.size() > 1 ? std::max(context.glH-120,2*strHeight) : scaleWidth); //if 1 value --> we draw a cube //centered orthoprojective view (-halfW,-halfH,halfW,halfH) int halfW = (context.glW>>1); int halfH = (context.glH>>1); //top-right corner of the scale ramp const int xShift = 20 + (showHistogram ? scaleWidth/2 : 0); const int yShift = halfH-scaleMaxHeight/2; glPushAttrib(GL_LINE_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_LINE_SMOOTH); glDisable(GL_DEPTH_TEST); std::vector<double> sortedKeyValues(keyValues.begin(),keyValues.end()); double maxRange = sortedKeyValues.back()-sortedKeyValues.front(); //const colorType* lineColor = ccColor::white; ////clear background? //if (ccGui::Parameters().backgroundCol[0] + ccGui::Parameters().backgroundCol[1] + ccGui::Parameters().backgroundCol[2] > 3*128) // lineColor = ccColor::black; const colorType* lineColor = textColor; //display color ramp { //(x,y): current display area coordinates (top-left corner) int x = halfW-xShift-scaleWidth; int y = halfH-yShift-scaleMaxHeight; if (keyValues.size() > 1) { int histoStart = x+scaleWidth+std::min(std::max(scaleWidth/8,3),15); glLineWidth(1.0f); glBegin(GL_LINES); for (int j=0; j<scaleMaxHeight; ++j) { double value = sortedKeyValues.front() + ((double)j * maxRange) / (double)scaleMaxHeight; if (logScale) value = exp(value*c_log10); const colorType* col = sf->getColor(static_cast<ScalarType>(value)); glColor3ubv(col ? col : ccColor::lightGrey); glVertex2i(x,y+j); glVertex2i(x+scaleWidth,y+j); if (showHistogram) { double bind = (value-(double)sf->displayRange().min())*(double)(histogram.size()-1)/(double)sf->displayRange().maxRange(); int bin = static_cast<int>(floor(bind)); double hVal = 0.0; if (bin >= 0 && bin < (int)histogram.size()) //in symmetrical case we can get values outside of the real SF range { hVal = (double)histogram[bin]; if (bin+1 < (int)histogram.size()) { //linear interpolation double alpha = bind-(double)bin; hVal = (1.0-alpha) * hVal + alpha * (double)histogram[bin+1]; } } int xSpan = std::max(static_cast<int>(hVal / (double)histogram.maxValue * (double)(scaleWidth/2)),1); glVertex2i(histoStart,y+j); glVertex2i(histoStart+xSpan,y+j); } } glEnd(); } else { //if there's a unique (visible) scalar value, we only draw a square! double value = sortedKeyValues.front(); if (logScale) value = exp(value*c_log10); const colorType* col = sf->getColor(value); glColor3ubv(col ? col : ccColor::lightGrey); glBegin(GL_POLYGON); glVertex2i(x,y); glVertex2i(x+scaleWidth,y); glVertex2i(x+scaleWidth,y+scaleMaxHeight-1); glVertex2i(x,y+scaleMaxHeight-1); glEnd(); } //scale border glLineWidth(2.0); glColor3ubv(lineColor); glBegin(GL_LINE_LOOP); glVertex2i(x,y); glVertex2i(x+scaleWidth,y); glVertex2i(x+scaleWidth,y+scaleMaxHeight); glVertex2i(x,y+scaleMaxHeight); glEnd(); } //display labels { //list of labels to draw vlabelSet drawnLabels; //add first label drawnLabels.push_back(vlabel(0,0,strHeight,sortedKeyValues.front())); if (keyValues.size() > 1) { //add last label drawnLabels.push_back(vlabel(scaleMaxHeight,scaleMaxHeight-strHeight,scaleMaxHeight,sortedKeyValues.back())); } //we try to display the other keyPoints (if any) if (keyValues.size() > 2) { assert(maxRange > 0.0); const int minGap = strHeight; for (size_t i=1; i<keyValues.size()-1; ++i) { int yScale = static_cast<int>((sortedKeyValues[i]-sortedKeyValues[0]) * (double)scaleMaxHeight / maxRange); vlabelPair nLabels = GetVLabelsAround(yScale,drawnLabels); assert(nLabels.first != drawnLabels.end() && nLabels.second != drawnLabels.end()); if ( (nLabels.first == drawnLabels.end() || nLabels.first->yMax <= yScale - minGap) && (nLabels.second == drawnLabels.end() || nLabels.second->yMin >= yScale + minGap)) { //insert it at the right place (so as to keep a sorted list!) drawnLabels.insert(nLabels.second,vlabel(yScale,yScale-strHeight/2,yScale+strHeight/2,sortedKeyValues[i])); } } } //now we recursively display labels where we have some rool left if (drawnLabels.size() > 1) { const int minGap = strHeight*2; size_t drawnLabelsBefore = 0; //just to init the loop size_t drawnLabelsAfter = drawnLabels.size(); //proceed until no more label can be inserted while (drawnLabelsAfter > drawnLabelsBefore) { drawnLabelsBefore = drawnLabelsAfter; vlabelSet::iterator it1 = drawnLabels.begin(); vlabelSet::iterator it2 = it1; it2++; for (; it2 != drawnLabels.end(); ++it2) { if (it1->yMax + 2*minGap < it2->yMin) { //insert label double val = (it1->val + it2->val)/2.0; int yScale = static_cast<int>((val-sortedKeyValues[0]) * (double)scaleMaxHeight / maxRange); //insert it at the right place (so as to keep a sorted list!) drawnLabels.insert(it2,vlabel(yScale,yScale-strHeight/2,yScale+strHeight/2,val)); } it1 = it2; } drawnLabelsAfter = drawnLabels.size(); } } //display labels //Some versions of Qt seem to need glColorf instead of glColorub! (see https://bugreports.qt-project.org/browse/QTBUG-6217) glColor3f((float)textColor[0]/255.0f,(float)textColor[1]/255.0f,(float)textColor[2]/255.0f); //Scalar field name const char* sfName = sf->getName(); if (sfName) { //QString sfTitle = QString("[%1]").arg(sfName); QString sfTitle(sfName); if (logScale) sfTitle += QString("[Log scale]"); //we leave some (vertical) space for the top-most label! win->displayText(sfTitle, context.glW-xShift, context.glH-yShift+strMetrics.height(), ccGLWindow::ALIGN_HRIGHT | ccGLWindow::ALIGN_VTOP); } //precision (same as color scale) const unsigned precision = ccGui::Parameters().displayedNumPrecision; //format const char format = (sf->logScale() ? 'E' : 'f'); //tick const int tickSize = 4; //for labels const int x = context.glW-xShift-scaleWidth-2*tickSize-1; const int y = context.glH-yShift-scaleMaxHeight; //for ticks const int xTick = halfW-xShift-scaleWidth-tickSize-1; const int yTick = halfH-yShift-scaleMaxHeight; for (vlabelSet::iterator it = drawnLabels.begin(); it != drawnLabels.end(); ++it) { vlabelSet::iterator itNext = it; itNext++; //position unsigned char align = ccGLWindow::ALIGN_HRIGHT; if (it == drawnLabels.begin()) align |= ccGLWindow::ALIGN_VTOP; else if (itNext == drawnLabels.end()) align |= ccGLWindow::ALIGN_VBOTTOM; else align |= ccGLWindow::ALIGN_VMIDDLE; double value = it->val; if (logScale) value = exp(value*c_log10); win->displayText(QString::number(value,format,precision), x, y+it->yPos, align); glBegin(GL_LINES); glVertex2f(xTick,yTick+it->yPos); glVertex2f(xTick+tickSize,yTick+it->yPos); glEnd(); } } glPopAttrib(); #else double minVal = sf->getMin(); double minDisplayed = sf->displayRange().start(); double minSaturation = sf->saturationRange().start(); double maxSaturation = sf->saturationRange().stop(); double maxDisplayed = sf->displayRange().stop(); double maxVal = sf->getMax(); bool strictlyPositive = (minVal >= 0); bool symmetricalScale = sf->symmetricalScale(); bool logScale = sf->logScale(); const int c_cubeSize = ccGui::Parameters().colorScaleRampWidth; const int c_defaultSpace = 4; //this vector stores the values that will be "represented" by the scale //they will be automatically displayed in a regular "pace" std::vector<ScaleElement> theScaleElements; std::vector<double> theCubeEquivalentDist; //to deduce its color! int maxNumberOfCubes = (int)(floor((float)(context.glH-120)/(float)(c_cubeSize+2*c_defaultSpace))); ccColorScale::Shared colorScale = context.sfColorScaleToDisplay->getColorScale(); if (!colorScale) { assert(false); return; } unsigned colorRampSteps = context.sfColorScaleToDisplay->getColorRampSteps(); //first we fill the two vectors below with scale "values" if (strictlyPositive || !symmetricalScale) //only positive values { bool dispZero = minDisplayed>0.0 && strictlyPositive; bool dispMinVal = false;//(minVal<minDisplayed); bool dispMinDispVal = true; bool dispMinSat = (minSaturation>minDisplayed && minSaturation<maxSaturation); bool dispMaxSat = (maxSaturation>=minSaturation && maxSaturation<maxDisplayed); bool dispMaxDispVal = (maxDisplayed>minDisplayed && maxDisplayed<maxVal); bool dispMaxVal = true; int addedCubes = int(dispZero) + int(dispMinVal) + int(dispMinDispVal) + int(dispMinSat) + int(dispMaxSat) + int(dispMaxDispVal) + int(dispMaxVal); //not enough room for display! if (maxNumberOfCubes < addedCubes) return; //number of cubes available for ramp display int numberOfCubes = std::min<int>(maxNumberOfCubes-addedCubes,colorRampSteps); double startValue = minVal; //we want it to be the same color as 'minVal' even if we start at '0' if (dispZero) theScaleElements.push_back(ScaleElement(0.0,true,true)); if (dispMinVal) { //precedent cube color if (!theScaleElements.empty()) theCubeEquivalentDist.push_back(startValue); theScaleElements.push_back(ScaleElement(minVal,true,dispMinDispVal || dispMinSat)); startValue = minVal; } if (dispMinDispVal) { //precedent cube color if (!theScaleElements.empty()) theCubeEquivalentDist.push_back(startValue); theScaleElements.push_back(ScaleElement(minDisplayed,true,dispMinSat)); startValue = minDisplayed; } if (dispMinSat) { //precedent cube color if (!theScaleElements.empty()) theCubeEquivalentDist.push_back(startValue); theScaleElements.push_back(ScaleElement(minSaturation)); startValue = minSaturation; } //the actual color ramp if (numberOfCubes>0 && minSaturation<maxSaturation && minDisplayed<maxDisplayed) { double endValue = (dispMaxSat ? maxSaturation : maxDisplayed); double intervale = (endValue-startValue)/(double)numberOfCubes; double firstValue = startValue; if (logScale) { double endValueLog = log10(std::max<double>(ZERO_TOLERANCE,fabs(endValue))); double startValueLog = log10(std::max<double>(ZERO_TOLERANCE,fabs(startValue))); intervale = (endValueLog-startValueLog)/(double)numberOfCubes; firstValue = startValueLog; } if (intervale < ZERO_TOLERANCE) { //finally, we won't draw this ramp! theScaleElements.back().condensed = true; } else { if (logScale) { for (int i=0;i<numberOfCubes;++i) { double val = firstValue+intervale*static_cast<double>(i); double logVal = val+intervale*0.5; theCubeEquivalentDist.push_back(exp(logVal*log(10.0))); theScaleElements.push_back(ScaleElement(exp((val+intervale)*log(10.0)),true,false)); } } else { for (int i=0;i<numberOfCubes;++i) { double val = firstValue+intervale*static_cast<double>(i); theCubeEquivalentDist.push_back(val+intervale*0.5); theScaleElements.push_back(ScaleElement(val+intervale,true,false)); } } } } if (dispMaxSat && dispMaxDispVal) { theCubeEquivalentDist.push_back(maxSaturation); theScaleElements.back().condensed = true; theScaleElements.push_back(ScaleElement(maxDisplayed, true, true)); } if ((dispMaxSat || dispMaxDispVal) && dispMaxVal) { theCubeEquivalentDist.push_back(maxVal); theScaleElements.back().condensed = true; theScaleElements.push_back(ScaleElement(maxVal)); } } else //both positive and negative values { //TODO FIXME!!! //if the ramp should be symmetrical bool symmetry = ccGui::Parameters().colorScaleAlwaysSymmetrical; if (symmetry) { //we display the color ramp between -maxDisp and +maxDisp double maxDisp = std::max(-minVal,maxVal); bool dispZero = true; bool dispMinSat = (minSaturation>0.0); bool dispMaxSat = (maxSaturation>minSaturation && maxSaturation<maxDisp); bool dispMaxVal = true; int addedCubes = 2 * (int(dispZero && dispMinSat) + int(dispMaxSat && dispMaxVal)); //not enough room for display! if (maxNumberOfCubes < addedCubes) return; //number of cubes available for ramp display int numberOfCubes = std::min<int>((maxNumberOfCubes-addedCubes)/2,colorRampSteps); //1st section: -maxDisp double startValue = -maxDisp; if (dispMaxVal) theScaleElements.push_back(ScaleElement(-maxDisp,true,dispMaxSat)); //2nd section: -maxSaturation if (dispMaxSat) { //precedent cube color if (!theScaleElements.empty()) theCubeEquivalentDist.push_back(startValue); theScaleElements.push_back(ScaleElement(-maxSaturation)); startValue = -maxSaturation; } //3rd section: the real color ramp (negative part) if (numberOfCubes>1) { double endValue = (dispMinSat ? -minSaturation : 0.0); double intervale = (endValue-startValue)/(double)numberOfCubes; double firstValue = startValue; if (logScale) { double endValueLog = log10(std::max<double>(ZERO_TOLERANCE,fabs(-endValue))); double startValueLog = log10(std::max<double>(ZERO_TOLERANCE,fabs(-startValue))); intervale = -(endValueLog-startValueLog)/(double)numberOfCubes; firstValue = startValueLog; } if (intervale < ZERO_TOLERANCE) { //finally, we won't draw this ramp! theScaleElements.back().condensed = true; } else { if (logScale) { for (int i=0;i<numberOfCubes-1;++i) { double logVal = firstValue-intervale*0.5; theCubeEquivalentDist.push_back(-exp(logVal*log(10.0))); firstValue -= intervale; //if (i==0 && firstValue>maxVal) //specific case: all values in the tail // theScaleElements.push_back(ScaleElement(maxVal,true)); //else theScaleElements.push_back(ScaleElement(-exp(firstValue*log(10.0)),true)); } } else { for (int i=0;i<numberOfCubes-1;++i) { theCubeEquivalentDist.push_back(firstValue + intervale*0.5); firstValue += intervale; //if (i==0 && firstValue>maxVal) //specific case: all values in the tail // theScaleElements.push_back(ScaleElement(maxVal,true)); //else theScaleElements.push_back(ScaleElement(firstValue,true)); } } } } //4th section: -minSaturation if (dispMinSat) { theCubeEquivalentDist.push_back(-minSaturation); theScaleElements.push_back(ScaleElement(-minSaturation, true, true)); } //5th section: zero if (dispZero) { theCubeEquivalentDist.push_back(0.5*theCubeEquivalentDist.back()); theScaleElements.push_back(ScaleElement(0, true, dispMinSat)); } //6th section: minSaturation if (dispMinSat) { theCubeEquivalentDist.push_back(0.0); theScaleElements.push_back(ScaleElement(minSaturation)); } //7th section: the real color ramp (positive part) if (numberOfCubes>1) { double intervale = (maxSaturation-minSaturation)/(double)numberOfCubes; double firstValue = minSaturation; if (logScale) { double endValueLog = log10(std::max<double>(ZERO_TOLERANCE,fabs(maxSaturation))); double startValueLog = log10(std::max<double>(ZERO_TOLERANCE,fabs(minSaturation))); intervale = (endValueLog-startValueLog)/(double)numberOfCubes; firstValue = startValueLog; } if (intervale < ZERO_TOLERANCE) { //finally, we won't draw this ramp! theScaleElements.back().condensed = true; } else { if (logScale) { for (int i=0;i<numberOfCubes-1;++i) { double logVal = firstValue+intervale*0.5; theCubeEquivalentDist.push_back(exp(logVal*log(10.0))); firstValue += intervale; //if (i+2==numberOfCubes && firstValue<minVal) //specific case: all values in the head // theScaleElements.push_back(ScaleElement(minVal,true)); //else theScaleElements.push_back(ScaleElement(exp(firstValue*log(10.0)),true)); } } else { for (int i=0;i<numberOfCubes-1;++i) { theCubeEquivalentDist.push_back(firstValue + intervale*0.5); firstValue += intervale; //if (i+2==numberOfCubes && firstValue<minVal) //specific case: all values in the head // theScaleElements.push_back(ScaleElement(minVal,true)); //else theScaleElements.push_back(ScaleElement(firstValue,true)); } } } } //8th section: maxSaturation if (dispMaxSat) { theCubeEquivalentDist.push_back(maxSaturation); theScaleElements.push_back(ScaleElement(maxSaturation,true,true)); } //9th section: maxVal if (dispMaxVal) { theCubeEquivalentDist.push_back(maxDisp); theScaleElements.push_back(ScaleElement(maxDisp)); } } else { //TODO } } if (theScaleElements.empty()) return; //scale height unsigned n = (unsigned)theScaleElements.size(); //assert(theCubeEquivalentDist.size()+(dispZero ? 1 : 0)==n); int scaleHeight = (c_cubeSize+2*c_defaultSpace)*n; const int xShift = c_cubeSize+20; const int yShift = 40; //centered orthoprojective view (-halfW,-halfH,halfW,halfH) int halfW = (context.glW>>1); int halfH = (context.glH>>1); /*** now we can render the scale ***/ //(x,y): current display area coordinates int x = halfW-xShift; int y = yShift-scaleHeight/2; //first horizontal delimiter glBegin(GL_LINES); glVertex2i(x,y); glVertex2i(x+c_cubeSize,y); glEnd(); ccGLWindow* win = (ccGLWindow*)context._win; assert(win); if (theScaleElements[0].textDisplayed) win->displayText(QString::number(theScaleElements[0].value, logScale ? 'E' : 'f', ccGui::Parameters().displayedNumPrecision), halfW+x-5, y+halfH, ccGLWindow::ALIGN_HRIGHT | ccGLWindow::ALIGN_VMIDDLE); const colorType* lineColor = ccColor::white; //clear background? if (ccGui::Parameters().backgroundCol[0] + ccGui::Parameters().backgroundCol[1] + ccGui::Parameters().backgroundCol[2] > 3*128) lineColor = ccColor::black; for (int i=0;i+1<(int)n;++i) { y += c_defaultSpace; //a colored cube //d = 0.5*(theScaleElements[i].value + theScaleElements[i+1].value); double d = theCubeEquivalentDist[i]; const colorType* col = sf->getColor(d); if (i==0 && theScaleElements[i].condensed) { //DOWN ARROW glBegin(GL_LINE_LOOP); glColor3ubv(lineColor); glVertex2i(x,y+c_cubeSize); glVertex2i(x+c_cubeSize,y+c_cubeSize); glVertex2i(x+c_cubeSize/2,y); glEnd(); if (col) { glBegin(GL_POLYGON); glColor3ubv(col); glVertex2i(x,y+c_cubeSize); glVertex2i(x+c_cubeSize,y+c_cubeSize); glVertex2i(x+c_cubeSize/2,y); glEnd(); } } else if (i+2 == (int)n && theScaleElements[i].condensed) { //UP ARROW glBegin(GL_LINE_LOOP); glColor3ubv(lineColor); glVertex2i(x,y); glVertex2i(x+c_cubeSize,y); glVertex2i(x+c_cubeSize/2,y+c_cubeSize); glEnd(); if (col) { glBegin(GL_POLYGON); glColor3ubv(col); glVertex2i(x,y+1); glVertex2i(x+c_cubeSize,y+1); glVertex2i(x+c_cubeSize/2,y+c_cubeSize); glEnd(); } } else //RECTANGLE { if (!theScaleElements[i].condensed) { //simple box if (col) { glBegin(GL_POLYGON); glColor3ubv(col); glVertex2i(x,y); glVertex2i(x+c_cubeSize,y); glVertex2i(x+c_cubeSize,y+c_cubeSize); glVertex2i(x,y+c_cubeSize); glEnd(); } glBegin(GL_LINE_LOOP); glColor3ubv(lineColor); glVertex2i(x,y); glVertex2i(x+c_cubeSize,y); glVertex2i(x+c_cubeSize,y+c_cubeSize); glVertex2i(x,y+c_cubeSize); glEnd(); } else { float third = (float)c_cubeSize *0.8/3.0f; //slashed box if (col) { glColor3ubv(col); glBegin(GL_POLYGON); glVertex2i(x,y); glVertex2f(x,(float)y+third); glVertex2f(x+c_cubeSize,(float)y+2.0f*third); glVertex2i(x+c_cubeSize,y); glEnd(); glBegin(GL_POLYGON); glVertex2i(x,y+c_cubeSize); glVertex2i(x+c_cubeSize,y+c_cubeSize); glVertex2f(x+c_cubeSize,(float)(y+c_cubeSize)-third); glVertex2f(x,(float)(y+c_cubeSize)-2.0*third); glEnd(); } glColor3ubv(lineColor); glBegin(GL_LINE_LOOP); glVertex2i(x,y); glVertex2f(x,(float)y+third); glVertex2f(x+c_cubeSize,(float)y+2.0f*third); glVertex2i(x+c_cubeSize,y); glEnd(); glBegin(GL_LINE_LOOP); glVertex2i(x,y+c_cubeSize); glVertex2i(x+c_cubeSize,y+c_cubeSize); glVertex2f(x+c_cubeSize,(float)(y+c_cubeSize)-third); glVertex2f(x,(float)(y+c_cubeSize)-2.0*third); glEnd(); } } y += c_cubeSize+c_defaultSpace; //separator glColor3ubv(lineColor); glBegin(GL_LINES); glVertex2i(x,y); glVertex2i(x+c_cubeSize,y); glEnd(); if (theScaleElements[i+1].textDisplayed) { double dispValue = theScaleElements[i+1].value; win->displayText(QString::number(dispValue,logScale ? 'E' : 'f',ccGui::Parameters().displayedNumPrecision), halfW+x-5, y+halfH, ccGLWindow::ALIGN_HRIGHT | ccGLWindow::ALIGN_VMIDDLE); } } //Scale title const char* sfName = context.sfColorScaleToDisplay->getName(); if (sfName) { //QString sfTitle = QString("[%1]").arg(sfName); QString sfTitle(sfName); win->displayText(sfTitle, context.glW-c_cubeSize/2, (y+c_cubeSize)+halfH, ccGLWindow::ALIGN_HRIGHT | ccGLWindow::ALIGN_VTOP); } #endif }