#3698 Scale bar. Support for both horizontal and vertical orientation

This commit is contained in:
Bjørn Erik Jensen 2018-11-16 13:50:15 +01:00
parent 05e04bb588
commit 75c923d102
3 changed files with 229 additions and 111 deletions

View File

@ -54,6 +54,7 @@
#include "cvfFont.h"
#include "cvfOpenGLResourceManager.h"
#include "cvfOverlayAxisCross.h"
#include "cvfOverlayItem.h"
#include "cvfPartRenderHintCollection.h"
#include "cvfRenderQueueSorter.h"
#include "cvfRenderSequence.h"
@ -191,6 +192,7 @@ RiuViewer::RiuViewer(const QGLFormat& format, QWidget* parent)
m_selectionVisualizerManager = new caf::PdmUiSelectionVisualizer3d(this);
m_scaleLegend = new caf::OverlayScaleLegend(standardFont);
m_scaleLegend->setOrientation(caf::OverlayScaleLegend::HORIZONTAL);
}
//--------------------------------------------------------------------------------------------------
@ -630,6 +632,12 @@ void RiuViewer::updateLegendLayout()
{
legend->setRenderSize(cvf::Vec2ui(maxColumnWidht, legend->renderSize().y()));
}
int margin = 5;
auto scaleLegendSize = m_scaleLegend->renderSize();
auto otherItemsHeight = m_versionInfoLabel->size().height();
m_scaleLegend->setLayoutFixedPosition({ width() - (int)scaleLegendSize.x() - margin - edgeAxisBorderWidth,
margin + edgeAxisBorderHeight + margin + otherItemsHeight});
}
//--------------------------------------------------------------------------------------------------
@ -964,7 +972,12 @@ void RiuViewer::showScaleLegend(bool show)
{
if (show)
{
addColorLegendToBottomLeftCorner(m_scaleLegend.p());
if(m_scaleLegend->orientation() == caf::OverlayScaleLegend::HORIZONTAL)
m_scaleLegend->setRenderSize({400, 50});
else
m_scaleLegend->setRenderSize({70, 400});
m_mainRendering->addOverlayItem(m_scaleLegend.p());
}
else
{

View File

@ -91,6 +91,8 @@ OverlayScaleLegend::OverlayScaleLegend(Font* font)
, m_numberFormat(AUTO)
, m_Layout(Vec2ui(200u, 200u))
, m_font(font)
, m_orientation(HORIZONTAL)
, m_currentScale(1.0)
{
CVF_ASSERT(font);
CVF_ASSERT(!font->isEmpty());
@ -145,7 +147,10 @@ void OverlayScaleLegend::renderGeneric(OpenGLContext* oglContext, const Vec2i& p
// Set up text drawer
float maxLegendRightPos = 0;
setupTextDrawer(m_textDrawer.p(), &m_Layout );
if(m_orientation == HORIZONTAL)
setupHorizontalTextDrawer(m_textDrawer.p(), &m_Layout );
else
setupVerticalTextDrawer(m_textDrawer.p(), &m_Layout);
Vec2f backgroundSize(size);
@ -184,7 +189,7 @@ void OverlayScaleLegend::renderGeneric(OpenGLContext* oglContext, const Vec2i& p
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OverlayScaleLegend::setupTextDrawer(TextDrawer* textDrawer, const LayoutInfo* layout)
void OverlayScaleLegend::setupHorizontalTextDrawer(TextDrawer* textDrawer, const LayoutInfo* layout)
{
CVF_ASSERT(layout);
@ -193,10 +198,10 @@ void OverlayScaleLegend::setupTextDrawer(TextDrawer* textDrawer, const LayoutInf
m_visibleTickLabels.clear();
const float textX = layout->startPt.x() + layout->majorTickSize / 2.0f /* tickEndX*/ + layout->tickTextLeadSpace;
const float textY = layout->axisStartPt.y() + layout->majorTickSize / 2.0f + layout->tickTextLeadSpace + layout->charHeight;
const float overlapTolerance = 1.2f * layout->charHeight;
float lastVisibleTextY = 0.0;
const float overlapTolerance = 1.2f * layout->charWidth;
float lastVisibleTextX = 0.0;
size_t numTicks = layout->ticks.size();
size_t it;
@ -204,11 +209,81 @@ void OverlayScaleLegend::setupTextDrawer(TextDrawer* textDrawer, const LayoutInf
{
if(!layout->ticks[it].isMajor) continue;
float textY = static_cast<float>(layout->startPt.y() + layout->ticks[it].displayValue);
double tickValue = layout->ticks[it].domainValue;
String valueString;
switch (m_numberFormat)
{
case FIXED:
valueString = String::number(tickValue, 'f', m_tickNumberPrecision);
break;
case SCIENTIFIC:
valueString = String::number(tickValue, 'e', m_tickNumberPrecision);
break;
default:
valueString = String::number(tickValue);
break;
}
auto textSize = m_font->textExtent(valueString);
float textX = static_cast<float>(layout->axisStartPt.x() + layout->ticks[it].displayValue - textSize.x() / 2.0f);
// Always draw first and last tick label. For all others, skip drawing if text ends up
// on top of the previous label.
if (it != 0 && it != (numTicks - 1))
{
if (cvf::Math::abs(textX - lastVisibleTextX) < overlapTolerance)
{
m_visibleTickLabels.push_back(false);
continue;
}
// Make sure it does not overlap the last tick as well
float lastTickY = static_cast<float>(layout->axisStartPt.y() + layout->axisLength);
if (cvf::Math::abs(textX - lastTickY) < overlapTolerance)
{
m_visibleTickLabels.push_back(false);
continue;
}
}
Vec2f pos(textX, textY);
textDrawer->addText(valueString, pos);
lastVisibleTextX = textX;
m_visibleTickLabels.push_back(true);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OverlayScaleLegend::setupVerticalTextDrawer(TextDrawer* textDrawer, const LayoutInfo* layout)
{
CVF_ASSERT(layout);
textDrawer->setVerticalAlignment(TextDrawer::CENTER);
textDrawer->setTextColor(this->textColor());
m_visibleTickLabels.clear();
const float textX = layout->axisStartPt.x() + layout->majorTickSize / 2.0f + layout->tickTextLeadSpace;
const float overlapTolerance = 1.2f * layout->charHeight;
float lastVisibleTextY = 0.0;
size_t numTicks = layout->ticks.size();
size_t it;
for (it = 0; it < numTicks; it++)
{
if (!layout->ticks[it].isMajor) continue;
float textY = static_cast<float>(layout->axisStartPt.y() + layout->ticks[it].displayValue);
// Always draw first and last tick label. For all others, skip drawing if text ends up
// on top of the previous label.
if (it != 0 && it != (numTicks - 1))
{
if (cvf::Math::abs(textY - lastVisibleTextY) < overlapTolerance)
{
@ -217,7 +292,7 @@ void OverlayScaleLegend::setupTextDrawer(TextDrawer* textDrawer, const LayoutInf
}
// Make sure it does not overlap the last tick as well
float lastTickY = static_cast<float>(layout->startPt.y() + layout->axisLength);
float lastTickY = static_cast<float>(layout->axisStartPt.y() + layout->axisLength);
if (cvf::Math::abs(textY - lastTickY) < overlapTolerance)
{
@ -230,15 +305,15 @@ void OverlayScaleLegend::setupTextDrawer(TextDrawer* textDrawer, const LayoutInf
String valueString;
switch (m_numberFormat)
{
case FIXED:
valueString = String::number(tickValue, 'f', m_tickNumberPrecision);
break;
case SCIENTIFIC:
valueString = String::number(tickValue, 'e', m_tickNumberPrecision);
break;
default:
valueString = String::number(tickValue);
break;
case FIXED:
valueString = String::number(tickValue, 'f', m_tickNumberPrecision);
break;
case SCIENTIFIC:
valueString = String::number(tickValue, 'e', m_tickNumberPrecision);
break;
default:
valueString = String::number(tickValue);
break;
}
Vec2f pos(textX, textY);
@ -247,18 +322,8 @@ void OverlayScaleLegend::setupTextDrawer(TextDrawer* textDrawer, const LayoutInf
lastVisibleTextY = textY;
m_visibleTickLabels.push_back(true);
}
float titleY = static_cast<float>(layout->overallLegendSize.y()) - layout->margins.y() - layout->charHeight/2.0f;
for (it = 0; it < this->titleStrings().size(); it++)
{
Vec2f pos(layout->margins.x(), titleY);
textDrawer->addText(this->titleStrings()[it], pos);
titleY -= layout->lineSpacing;
}
}
//--------------------------------------------------------------------------------------------------
/// Draw the legend using shader programs
//--------------------------------------------------------------------------------------------------
@ -310,10 +375,19 @@ void OverlayScaleLegend::renderLegendUsingShaders(OpenGLContext* oglContext, Lay
// Draw axis
{
v0[0] = layout->startPt.x();
v0[1] = layout->startPt.y();
v1[0] = v0[0];
v1[1] = v0[1] + layout->axisLength;
v0[0] = layout->axisStartPt.x();
v0[1] = layout->axisStartPt.y();
if (m_orientation == HORIZONTAL)
{
v1[0] = v0[0] + layout->axisLength;
v1[1] = v0[1];
}
else
{
v1[0] = v0[0];
v1[1] = v0[1] + layout->axisLength;
}
static const ushort axisConnects[] = { 0, 1 };
@ -330,18 +404,20 @@ void OverlayScaleLegend::renderLegendUsingShaders(OpenGLContext* oglContext, Lay
// Draw ticks
for (const auto& tickInfo : layout->ticks)
{
if (tickInfo.isMajor)
float currTickSize = tickInfo.isMajor ? layout->majorTickSize : layout->minorTickSize;
if (m_orientation == HORIZONTAL)
{
v0[0] = layout->startPt.x() - layout->majorTickSize / 2.0f;
v0[1] = static_cast<float>(tickInfo.displayValue) + layout->startPt.y();
v1[0] = v0[0] + layout->majorTickSize;
v1[1] = v0[1];
v0[0] = layout->axisStartPt.x() + static_cast<float>(tickInfo.displayValue);
v0[1] = layout->axisStartPt.y() - currTickSize / 2.0f;
v1[0] = v0[0];
v1[1] = v0[1] + currTickSize;
}
else
{
v0[0] = layout->startPt.x() - layout->minorTickSize / 2.0f;
v0[1] = static_cast<float>(tickInfo.displayValue) + layout->startPt.y();
v1[0] = v0[0] + layout->minorTickSize;
v0[0] = layout->axisStartPt.x() - currTickSize / 2.0f;
v0[1] = layout->axisStartPt.y() + static_cast<float>(tickInfo.displayValue);
v1[0] = v0[0] + currTickSize;
v1[1] = v0[1];
}
@ -519,21 +595,47 @@ void OverlayScaleLegend::layoutInfo(LayoutInfo* layout)
{
CVF_TIGHT_ASSERT(layout);
ref<Glyph> glyph = this->font()->getGlyph(L'A');
layout->charHeight = static_cast<float>(glyph->height());
layout->lineSpacing = layout->charHeight*1.5f;
layout->margins = Vec2f(8.0f, 8.0f);
layout->tickTextLeadSpace = 5.0f;
layout->majorTickSize = 9.0f;
layout->minorTickSize = 5.0f;
// Input values
float marginAlongAxis = 8.0f;
float marginAcrossAxis = 8.0f;
float tickTextLeadSpace = 5.0f;
float majorTickSize = 9.0f;
float minorTickSize = 5.0f;
layout->axisLength = static_cast<float>(layout->overallLegendSize.y())
- 2*layout->margins.y()
ref<Glyph> glyph = this->font()->getGlyph(L'A');
layout->charWidth = static_cast<float>(glyph->width());
layout->charHeight = static_cast<float>(glyph->height());
layout->lineSpacing = layout->charHeight*1.5f;
layout->tickTextLeadSpace = tickTextLeadSpace;
layout->majorTickSize = majorTickSize;
layout->minorTickSize = minorTickSize;
double overallSizeValue;
float marginValue;
if (m_orientation == HORIZONTAL)
{
layout->margins = Vec2f(marginAlongAxis, marginAcrossAxis);
overallSizeValue = layout->overallLegendSize.x();
marginValue = layout->margins.x();
layout->axisStartPt = {layout->margins.x() + layout->charWidth / 2.0f,
layout->margins.y() + layout->majorTickSize / 2.0f};
}
else
{
layout->margins = Vec2f(marginAcrossAxis, marginAlongAxis);
overallSizeValue = layout->overallLegendSize.y();
marginValue = layout->margins.y();
layout->axisStartPt = {layout->margins.x() + layout->majorTickSize / 2.0f,
layout->margins.y() + layout->charHeight / 2.0f};
}
layout->axisLength = static_cast<float>(overallSizeValue)
- 2 * marginValue
- static_cast<float>(this->titleStrings().size()) * layout->lineSpacing
- layout->lineSpacing;
auto currentScale = m_currentScale != 0.0 ? m_currentScale : 1.0;
layout->startPt = {layout->margins.x() + layout->majorTickSize / 2.0f, layout->margins.y() + layout->charHeight / 2.0f };
layout->ticks.clear();
size_t numTicks = m_ticksInDomain.size();
@ -580,51 +682,30 @@ void OverlayScaleLegend::setTickFormat(NumberFormat format)
m_numberFormat = format;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OverlayScaleLegend::setOrientation(Orientation orientation)
{
m_orientation = orientation;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::OverlayScaleLegend::Orientation OverlayScaleLegend::orientation() const
{
return m_orientation;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Vec2ui OverlayScaleLegend::preferredSize()
{
LayoutInfo layout({200,200}); // Use default size
layoutInfo(&layout);
float prefferredYSize = 400;
//float prefferredYSize = 2 * layout.margins.y()
// + layout.lineSpacing * this->titleStrings().size()
// + 1.5f * layout.lineSpacing * m_tickValues.size();
unsigned int maxTickTextWidth = 0;
for (double tickValue : m_ticksInDomain )
{
String valueString;
switch ( m_numberFormat )
{
case FIXED:
valueString = String::number(tickValue, 'f', m_tickNumberPrecision);
break;
case SCIENTIFIC:
valueString = String::number(tickValue, 'e', m_tickNumberPrecision);
break;
default:
valueString = String::number(tickValue);
break;
}
unsigned int textWidth = this->font()->textExtent(valueString).x();
maxTickTextWidth = maxTickTextWidth < textWidth ? textWidth : maxTickTextWidth;
}
float prefferredXSize = layout.margins.x() + layout.tickTextLeadSpace + maxTickTextWidth;
for (const cvf::String& titleLine : titleStrings())
{
float titleWidth = this->font()->textExtent(titleLine).x() + 2*layout.margins.x();
prefferredXSize = prefferredXSize < titleWidth ? titleWidth : prefferredXSize;
}
prefferredXSize = std::min(prefferredXSize, 400.0f);
return { (unsigned int)(std::ceil(prefferredXSize)), (unsigned int)(std::ceil(prefferredYSize)) };
uint preferredXSize = 100;
uint preferredYSize = 100;
return { (unsigned int)(std::ceil(preferredXSize)), (unsigned int)(std::ceil(preferredYSize)) };
}
@ -659,15 +740,36 @@ void OverlayScaleLegend::updateFromCamera(const Camera* camera)
camera->project(windowOrigoInDomain, &windowOrigoPoint);
camera->project(windowMaxInDomain, &windowMaxPoint);
m_currentScale = (windowMaxPoint.y() - windowOrigoPoint.y()) / (windowMaxInDomain.y() - windowOrigoInDomain.y());
double minStepSizeInDomain;
double windowOrigoInDomainValue;
double windowMaxInDomainValue;
double windowOrigoPointValue;
double windowMaxPointValue;
int tickMaxCount;
auto textSize = m_font->textExtent(String::number(-1.999e-17));
int xTickMaxCount = windowSize.x() / (2 * textSize.x());
int yTickMaxCount = windowSize.y() / (2 * textSize.x());
auto textSize = m_font->textExtent(String::number(-1.999e-17));
if (m_orientation == HORIZONTAL)
{
windowOrigoInDomainValue = windowOrigoInDomain.x();
windowMaxInDomainValue = windowMaxInDomain.x();
windowOrigoPointValue = windowOrigoPoint.x();
windowMaxPointValue = windowMaxPoint.x();
tickMaxCount = windowSize.x() / (2 * textSize.x());
}
else
{
windowOrigoInDomainValue = windowOrigoInDomain.y();
windowMaxInDomainValue = windowMaxInDomain.y();
windowOrigoPointValue = windowOrigoPoint.y();
windowMaxPointValue = windowMaxPoint.y();
tickMaxCount = windowSize.y() / (2 * textSize.x());
}
double minDomainYStepSize = (windowMaxInDomain.y() - windowOrigoInDomain.y()) / yTickMaxCount;
caf::TickMarkGenerator yTickCreator(windowOrigoInDomain.y(), windowMaxInDomain.y(), minDomainYStepSize);
auto ticks = yTickCreator.tickMarkValues();
m_currentScale = (windowMaxPointValue - windowOrigoPointValue) / (windowMaxInDomainValue - windowOrigoInDomainValue);
minStepSizeInDomain = (windowMaxInDomainValue - windowOrigoInDomainValue) / tickMaxCount;
caf::TickMarkGenerator tickCreator(windowOrigoInDomainValue, windowMaxInDomainValue, minStepSizeInDomain);
auto ticks = tickCreator.tickMarkValues();
m_ticksInDomain.clear();
for (const auto& tick : ticks)

View File

@ -45,6 +45,8 @@
#include "cvfString.h"
#include "cvfRect.h"
#include <array>
namespace cvf {
class Font;
class ShaderProgram;
@ -64,20 +66,18 @@ namespace caf {
class OverlayScaleLegend : public caf::TitledOverlayFrame
{
using Font = cvf::Font;
using ScalarMapper = cvf::ScalarMapper;
using OpenGLContext = cvf::OpenGLContext;
using Vec2i = cvf::Vec2i;
using Vec2ui = cvf::Vec2ui;
using Color3f = cvf::Color3f;
using Color4f = cvf::Color4f;
using String = cvf::String;
using DoubleArray = cvf::DoubleArray;
using MatrixState = cvf::MatrixState;
using Vec2f = cvf::Vec2f;
using Rectf = cvf::Rectf;
using TextDrawer = cvf::TextDrawer;
using Camera = cvf::Camera;
public:
enum Orientation {HORIZONTAL, VERTICAL};
public:
OverlayScaleLegend(Font* font);
virtual ~OverlayScaleLegend();
@ -85,6 +85,8 @@ public:
void setTickPrecision(int precision);
enum NumberFormat { AUTO, SCIENTIFIC, FIXED};
void setTickFormat(NumberFormat format);
void setOrientation(Orientation orientation);
Orientation orientation() const;
virtual cvf::Vec2ui preferredSize() override;
@ -113,13 +115,14 @@ protected:
overallLegendSize = setSize;
}
float charWidth;
float charHeight;
float lineSpacing;
Vec2f margins;
float tickTextLeadSpace;
//Rectf colorBarRect;
Vec2f startPt;
Vec2f axisStartPt;
float axisLength;
float majorTickSize;
float minorTickSize;
@ -140,23 +143,23 @@ protected:
const MatrixState& matrixState);
void renderLegendImmediateMode(OpenGLContext* oglContext,
LayoutInfo* layout);
void setupTextDrawer(TextDrawer* textDrawer,
const LayoutInfo* layout);
void setupHorizontalTextDrawer(TextDrawer* textDrawer, const LayoutInfo* layout);
void setupVerticalTextDrawer(TextDrawer* textDrawer, const LayoutInfo* layout);
protected:
std::vector<bool> m_visibleTickLabels; // Skip tick labels ending up on top of previous visible label
int m_tickNumberPrecision;
NumberFormat m_numberFormat;
LayoutInfo m_Layout;
cvf::ref<TextDrawer> m_textDrawer;
cvf::cref<ScalarMapper> m_scalarMapper;
Orientation m_orientation;
LayoutInfo m_Layout;
cvf::ref<TextDrawer> m_textDrawer;
cvf::ref<Font> m_font;
cvf::ref<Font> m_font;
cvf::cref<caf::DisplayCoordTransform> m_dispalyCoordsTransform;
double m_currentScale = 0.0; // [pixels/length]
std::vector<double> m_ticksInDomain;
cvf::cref<caf::DisplayCoordTransform> m_dispalyCoordsTransform;
double m_currentScale; // [pixels/length]
std::vector<double> m_ticksInDomain;
};
}