From 7a275be33573a9411f1f7bf27742d6b061fbdc77 Mon Sep 17 00:00:00 2001 From: Gaute Lindkvist Date: Thu, 3 Jan 2019 17:07:55 +0100 Subject: [PATCH] #3876 Implement rotated labels on contour maps --- .../RivContourMapProjectionPartMgr.cpp | 327 +++++++++++++----- .../RivContourMapProjectionPartMgr.h | 14 +- .../ProjectDataModel/RimContourMapView.cpp | 62 ++++ .../ProjectDataModel/RimContourMapView.h | 6 + .../ProjectDataModel/RimViewWindow.cpp | 16 +- .../ProjectDataModel/RimViewWindow.h | 3 + ApplicationCode/UserInterface/RiuViewer.cpp | 2 +- Fwk/AppFwk/cafViewer/cafViewer.h | 2 +- Fwk/VizFwk/LibRender/cvfDrawableText.cpp | 41 ++- Fwk/VizFwk/LibRender/cvfDrawableText.h | 7 +- Fwk/VizFwk/LibRender/cvfGlyph.cpp | 58 +++- Fwk/VizFwk/LibRender/cvfGlyph.h | 13 + Fwk/VizFwk/LibRender/cvfTextDrawer.cpp | 239 +++++++------ Fwk/VizFwk/LibRender/cvfTextDrawer.h | 11 +- 14 files changed, 593 insertions(+), 208 deletions(-) diff --git a/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.cpp b/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.cpp index 1775790ccd..94f4919130 100644 --- a/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.cpp +++ b/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.cpp @@ -3,14 +3,17 @@ #include "RiaColorTools.h" #include "RiaFontCache.h" #include "RiaWeightedMeanCalculator.h" +#include "RigCellGeometryTools.h" #include "RivMeshLinesSourceInfo.h" #include "RivScalarMapperUtils.h" +#include "RivPartPriority.h" #include "RimContourMapView.h" #include "RimContourMapProjection.h" #include "cafEffectGenerator.h" +#include "cvfCamera.h" #include "cvfDrawableText.h" #include "cvfGeometryBuilderFaceList.h" #include "cvfGeometryUtils.h" @@ -18,6 +21,7 @@ #include "cvfPart.h" #include "cvfPrimitiveSetIndexedUInt.h" #include "cvfScalarMapper.h" +#include "cvfRay.h" #include @@ -30,52 +34,26 @@ RivContourMapProjectionPartMgr::RivContourMapProjectionPartMgr(RimContourMapProj m_parentContourMap = contourMap; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivContourMapProjectionPartMgr::createProjectionGeometry() +{ + m_contourMapProjection->generateContourPolygons(); + m_contourLinePolygons = m_contourMapProjection->contourPolygons(); + m_contourMapTriangles = m_contourMapProjection->generateTriangles(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivContourMapProjectionPartMgr::appendProjectionToModel(cvf::ModelBasicList* model, const caf::DisplayCoordTransform* displayCoordTransform) const { - cvf::ScalarMapper* mapper = m_contourMapProjection->legendConfig()->scalarMapper(); - - std::vector>> contourDrawablesForAllLevels = createContourPolygons(displayCoordTransform); cvf::ref mapPart = createProjectionMapPart(displayCoordTransform); if (mapPart.notNull()) { model->addPart(mapPart.p()); } - - if (m_contourMapProjection->showContourLines()) - { - std::vector tickValues; - mapper->majorTickValues(&tickValues); - - for (size_t i = 0; i < contourDrawablesForAllLevels.size(); ++i) - { - std::vector> contourDrawables = contourDrawablesForAllLevels[i]; - - cvf::Color3f backgroundColor(mapper->mapToColor(tickValues[i])); - cvf::Color3f lineColor = RiaColorTools::contrastColor(backgroundColor, true); - - for (cvf::ref contourDrawable : contourDrawables) - { - if (contourDrawable.notNull() && contourDrawable->boundingBox().isValid()) - { - caf::MeshEffectGenerator meshEffectGen(lineColor); - meshEffectGen.setLineWidth(1.0f); - meshEffectGen.createAndConfigurePolygonOffsetRenderState(caf::PO_1); - - cvf::ref effect = meshEffectGen.generateCachedEffect(); - - cvf::ref part = new cvf::Part; - part->setDrawable(contourDrawable.p()); - part->setEffect(effect.p()); - part->setSourceInfo(new RivMeshLinesSourceInfo(m_contourMapProjection.p())); - - model->addPart(part.p()); - } - } - } - } } //-------------------------------------------------------------------------------------------------- @@ -127,6 +105,70 @@ cvf::ref RivContourMapProjectionPartMgr::createTextureCoords(co return textureCoords; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivContourMapProjectionPartMgr::appendContourLinesToModel(const cvf::Camera* camera, + cvf::ModelBasicList* model, + const caf::DisplayCoordTransform* displayCoordTransform) const +{ + if (m_contourMapProjection->showContourLines()) + { + cvf::ScalarMapper* mapper = m_contourMapProjection->legendConfig()->scalarMapper(); + + std::vector> labelBBoxes; + std::vector> labelDrawables = createContourLabels(camera, displayCoordTransform, &labelBBoxes); + + std::vector>> contourDrawablesForAllLevels = + createContourPolygons(displayCoordTransform, labelBBoxes); + + std::vector tickValues; + mapper->majorTickValues(&tickValues); + + for (size_t i = 0; i < contourDrawablesForAllLevels.size(); ++i) + { + std::vector> contourDrawables = contourDrawablesForAllLevels[i]; + + cvf::Color3f backgroundColor(mapper->mapToColor(tickValues[i])); + cvf::Color3f lineColor = RiaColorTools::contrastColor(backgroundColor); + + for (cvf::ref contourDrawable : contourDrawables) + { + if (contourDrawable.notNull() && contourDrawable->boundingBox().isValid()) + { + caf::MeshEffectGenerator meshEffectGen(lineColor); + meshEffectGen.setLineWidth(1.0f); + meshEffectGen.createAndConfigurePolygonOffsetRenderState(caf::PO_1); + + cvf::ref effect = meshEffectGen.generateCachedEffect(); + + cvf::ref part = new cvf::Part; + part->setDrawable(contourDrawable.p()); + part->setEffect(effect.p()); + part->setSourceInfo(new RivMeshLinesSourceInfo(m_contourMapProjection.p())); + + model->addPart(part.p()); + } + } + } + for (auto labelDrawableRef : labelDrawables) + { + caf::MeshEffectGenerator meshEffectGen(cvf::Color3::BLACK); + meshEffectGen.setLineWidth(1.0f); + meshEffectGen.createAndConfigurePolygonOffsetRenderState(caf::PO_2); + + cvf::ref effect = meshEffectGen.generateCachedEffect(); + + cvf::ref part = new cvf::Part; + part->setDrawable(labelDrawableRef.p()); + part->setEffect(effect.p()); + part->setPriority(RivPartPriority::Text); + part->setSourceInfo(new RivMeshLinesSourceInfo(m_contourMapProjection.p())); + model->addPart(part.p()); + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -136,13 +178,14 @@ cvf::ref RivContourMapProjectionPartMgr::createTextLabel(cons cvf::ref labelDrawable = new cvf::DrawableText(); labelDrawable->setFont(font.p()); - labelDrawable->setCheckPosVisible(true); + labelDrawable->setCheckPosVisible(false); labelDrawable->setUseDepthBuffer(true); labelDrawable->setDrawBorder(false); labelDrawable->setDrawBackground(false); labelDrawable->setBackgroundColor(backgroundColor); - labelDrawable->setVerticalAlignment(cvf::TextDrawer::BASELINE); + labelDrawable->setVerticalAlignment(cvf::TextDrawer::CENTER); labelDrawable->setTextColor(textColor); + labelDrawable->setBorderColor(textColor); return labelDrawable; } @@ -152,7 +195,7 @@ cvf::ref RivContourMapProjectionPartMgr::createTextLabel(cons //-------------------------------------------------------------------------------------------------- cvf::ref RivContourMapProjectionPartMgr::createProjectionMapPart(const caf::DisplayCoordTransform* displayCoordTransform) const { - std::vector vertices = m_contourMapProjection->generateTriangles(); + const std::vector& vertices = m_contourMapTriangles; if (vertices.size() < 3u) { return cvf::ref(); @@ -192,60 +235,100 @@ cvf::ref RivContourMapProjectionPartMgr::createProjectionMapPart(cons //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::vector>> RivContourMapProjectionPartMgr::createContourPolygons(const caf::DisplayCoordTransform* displayCoordTransform) const +std::vector>> RivContourMapProjectionPartMgr::createContourPolygons(const caf::DisplayCoordTransform* displayCoordTransform, const std::vector>& labelBBoxes) const { - m_contourMapProjection->generateContourPolygons(); - const std::vector& contourPolygons = m_contourMapProjection->contourPolygons(); - const cvf::ScalarMapper* mapper = m_contourMapProjection->legendConfig()->scalarMapper(); std::vector tickValues; mapper->majorTickValues(&tickValues); std::vector>> contourDrawablesForAllLevels; - std::vector> labelDrawables; - for (int64_t i = (int64_t) contourPolygons.size() - 1; i > 0; --i) + contourDrawablesForAllLevels.resize(tickValues.size()); + + for (size_t i = 1; i < m_contourLinePolygons.size(); ++i) { std::vector> contourDrawables; - cvf::Color3f backgroundColor(mapper->mapToColor(tickValues[i])); - cvf::Color3f textColor = RiaColorTools::contrastColor(backgroundColor); - - for (size_t j = 0; j < contourPolygons[i].size(); ++j) + for (size_t j = 0; j < m_contourLinePolygons[i].size(); ++j) { - if (contourPolygons[i][j].vertices.empty()) continue; + if (m_contourLinePolygons[i][j].vertices.empty()) continue; - size_t nVertices = contourPolygons[i][j].vertices.size(); - size_t nLabels = m_contourMapProjection->showContourLabels() ? std::max((size_t)1, nVertices / 100u) : 0u; - for (size_t l = 0; l < nLabels; ++l) - { - cvf::ref label = createTextLabel(textColor, backgroundColor); - cvf::Vec3d globalVertex = contourPolygons[i][j].vertices[(nVertices * l) / nLabels] + m_contourMapProjection->origin3d(); - cvf::Vec3f displayVertex(displayCoordTransform->transformToDisplayCoord(globalVertex)); - displayVertex.z() += 3.0f; - label->addText(cvf::String(contourPolygons[i][j].value), displayVertex); - bool overlaps = false; - cvf::BoundingBox bbox = label->boundingBox(); - for (cvf::ref existingLabel : labelDrawables) - { - if (existingLabel->boundingBox().intersects(bbox)) - { - overlaps = true; - break; - } - } - if (!overlaps) - { - labelDrawables.push_back(label); - } - } - cvf::ref vertexArray = new cvf::Vec3fArray(nVertices); + cvf::String labelText(m_contourLinePolygons[i][j].value); + + size_t nVertices = m_contourLinePolygons[i][j].vertices.size(); + + std::vector displayLines; displayLines.reserve(nVertices * 2); for (size_t v = 0; v < nVertices; ++v) { - cvf::Vec3d globalVertex = contourPolygons[i][j].vertices[v] + m_contourMapProjection->origin3d(); - cvf::Vec3d displayVertex1 = displayCoordTransform->transformToDisplayCoord(globalVertex); - (*vertexArray)[v] = cvf::Vec3f(displayVertex1); + cvf::Vec3d globalVertex1 = m_contourLinePolygons[i][j].vertices[v] + m_contourMapProjection->origin3d(); + cvf::Vec3d displayVertex1 = displayCoordTransform->transformToDisplayCoord(globalVertex1); + + cvf::Vec3d globalVertex2; + if (v < nVertices - 1) + globalVertex2 = m_contourLinePolygons[i][j].vertices[v + 1] + m_contourMapProjection->origin3d(); + else + globalVertex2 = m_contourLinePolygons[i][j].vertices[0] + m_contourMapProjection->origin3d(); + + cvf::Vec3d displayVertex2 = displayCoordTransform->transformToDisplayCoord(globalVertex2); + + cvf::BoundingBox lineBBox; + lineBBox.add(displayVertex1); + lineBBox.add(displayVertex2); + + bool addOriginalSegment = true; + for (const cvf::BoundingBox& existingBBox : labelBBoxes[i]) + { + if (lineBBox.intersects(existingBBox)) + { + if (existingBBox.contains(displayVertex1) && existingBBox.contains(displayVertex2)) + { + addOriginalSegment = false; + } + else + { + cvf::Vec3d dir = displayVertex2 - displayVertex1; + + cvf::Ray ray; + ray.setOrigin(displayVertex1); + ray.setDirection(dir.getNormalized()); + ray.setMaximumDistance(dir.length()); + + if (!existingBBox.contains(displayVertex1)) + { + cvf::Vec3d intersection; + bool hit = ray.boxIntersect(existingBBox, &intersection); + if (hit) + { + displayLines.push_back(cvf::Vec3f(displayVertex1)); + displayLines.push_back(cvf::Vec3f(intersection)); + addOriginalSegment = false; + } + } + + if (!existingBBox.contains(displayVertex2)) + { + ray.setOrigin(displayVertex2); + ray.setDirection(-ray.direction()); + cvf::Vec3d intersection; + bool hit = ray.boxIntersect(existingBBox, &intersection); + if (hit) + { + displayLines.push_back(cvf::Vec3f(intersection)); + displayLines.push_back(cvf::Vec3f(displayVertex2)); + addOriginalSegment = false; + } + } + } + } + } + if (addOriginalSegment) + { + displayLines.push_back(cvf::Vec3f(displayVertex1)); + displayLines.push_back(cvf::Vec3f(displayVertex2)); + } } + cvf::ref vertexArray = new cvf::Vec3fArray(displayLines); + std::vector indices; indices.reserve(vertexArray->size()); for (cvf::uint k = 0; k < vertexArray->size(); ++k) @@ -253,7 +336,7 @@ std::vector>> RivContourMapProjectionPartMgr indices.push_back(k); } - cvf::ref indexedUInt = new cvf::PrimitiveSetIndexedUInt(cvf::PrimitiveType::PT_LINE_LOOP); + cvf::ref indexedUInt = new cvf::PrimitiveSetIndexedUInt(cvf::PrimitiveType::PT_LINES); cvf::ref indexArray = new cvf::UIntArray(indices); indexedUInt->setIndices(indexArray.p()); @@ -263,17 +346,89 @@ std::vector>> RivContourMapProjectionPartMgr geo->setVertexArray(vertexArray.p()); contourDrawables.push_back(geo); } - for (cvf::ref labelDrawable : labelDrawables) - { - contourDrawables.push_back(labelDrawable); - } - - contourDrawablesForAllLevels.push_back(contourDrawables); + contourDrawablesForAllLevels[i] = contourDrawables; } - return contourDrawablesForAllLevels; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector> + RivContourMapProjectionPartMgr::createContourLabels(const cvf::Camera* camera, + const caf::DisplayCoordTransform* displayCoordTransform, + std::vector>* labelBBoxes) const +{ + CVF_ASSERT(camera && displayCoordTransform && labelBBoxes); + + const cvf::ScalarMapper* mapper = m_contourMapProjection->legendConfig()->scalarMapper(); + std::vector tickValues; + mapper->majorTickValues(&tickValues); + + std::vector> labelDrawables; + labelBBoxes->clear(); + labelBBoxes->resize(m_contourLinePolygons.size()); + for (int64_t i = (int64_t)m_contourLinePolygons.size() - 1; i > 0; --i) + { + cvf::Color3f backgroundColor(mapper->mapToColor(tickValues[i])); + cvf::Color3f textColor = RiaColorTools::contrastColor(backgroundColor); + cvf::ref label = createTextLabel(textColor, backgroundColor); + + for (size_t j = 0; j < m_contourLinePolygons[i].size(); ++j) + { + if (m_contourLinePolygons[i][j].vertices.empty()) continue; + + cvf::String labelText(m_contourLinePolygons[i][j].value); + + size_t nVertices = m_contourLinePolygons[i][j].vertices.size(); + size_t nLabels = m_contourMapProjection->showContourLabels() ? std::max((size_t)1, nVertices / 50u) : 0u; + for (size_t l = 0; l < nLabels; ++l) + { + size_t nVertex = (nVertices * l) / nLabels; + cvf::Vec3d globalVertex1 = m_contourLinePolygons[i][j].vertices[nVertex] + m_contourMapProjection->origin3d(); + cvf::Vec3d globalVertex2 = + m_contourLinePolygons[i][j].vertices[nVertex + 1 % nVertices] + m_contourMapProjection->origin3d(); + cvf::Vec3d globalVertex = 0.5 * (globalVertex1 + globalVertex2); + + cvf::Vec3f segment = cvf::Vec3f(globalVertex2 - globalVertex1).getNormalized(); + cvf::Vec3d displayVertex = displayCoordTransform->transformToDisplayCoord(globalVertex); + cvf::Vec3d windowVertex; + camera->project(displayVertex, &windowVertex); + displayVertex.z() += 10.0f; + cvf::BoundingBox windowBBox = label->textBoundingBox(labelText, cvf::Vec3f::ZERO, segment); + cvf::Vec3d displayBBoxMin, displayBoxMax; + camera->unproject(windowBBox.min() + windowVertex, &displayBBoxMin); + camera->unproject(windowBBox.max() + windowVertex, &displayBoxMax); + cvf::BoundingBox displayBBox(displayBBoxMin - cvf::Vec3d::Z_AXIS * 20.0, displayBoxMax + cvf::Vec3d::Z_AXIS * 20.0); + + bool overlaps = false; + for (auto boxVector : *labelBBoxes) + { + for (const cvf::BoundingBox& existingBBox : boxVector) + { + if (existingBBox.intersects(displayBBox)) + { + overlaps = true; + break; + } + } + } + if (!overlaps) + { + label->addText(labelText, cvf::Vec3f(displayVertex), segment); + labelBBoxes->at(i).push_back(displayBBox); + } + } + } + if (label->numberOfTexts() != 0u) + { + labelDrawables.push_back(label); + } + } + return labelDrawables; +} + + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.h b/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.h index af787a2f80..91740132b9 100644 --- a/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.h +++ b/ApplicationCode/ModelVisualization/RivContourMapProjectionPartMgr.h @@ -18,6 +18,8 @@ #pragma once +#include "RimContourMapProjection.h" + #include "cafPdmPointer.h" #include "cafDisplayCoordTransform.h" @@ -26,29 +28,37 @@ #include "cvfDrawableText.h" #include "cvfModelBasicList.h" #include "cvfObject.h" +#include "cvfVector4.h" class RimContourMapView; -class RimContourMapProjection; class RivContourMapProjectionPartMgr : public cvf::Object { public: RivContourMapProjectionPartMgr(RimContourMapProjection* contourMapProjection, RimContourMapView* contourMap); + void createProjectionGeometry(); void appendProjectionToModel(cvf::ModelBasicList* model, const caf::DisplayCoordTransform* displayCoordTransform) const; + void appendContourLinesToModel(const cvf::Camera* camera, cvf::ModelBasicList* model, const caf::DisplayCoordTransform* displayCoordTransform) const; void appendPickPointVisToModel(cvf::ModelBasicList* model, const caf::DisplayCoordTransform* displayCoordTransform) const; cvf::ref createTextureCoords(const std::vector& values) const; + private: static cvf::ref createTextLabel(const cvf::Color3f& textColor, const cvf::Color3f& backgroundColor); cvf::ref createProjectionMapPart(const caf::DisplayCoordTransform* displayCoordTransform) const; - std::vector>> createContourPolygons(const caf::DisplayCoordTransform* displayCoordTransform) const; + std::vector>> createContourPolygons(const caf::DisplayCoordTransform* displayCoordTransform, const std::vector>& labelBBoxes) const; + std::vector> createContourLabels(const cvf::Camera* camera, const caf::DisplayCoordTransform* displayCoordTransform, std::vector>* labelBBoxes) const; cvf::ref createPickPointVisDrawable(const caf::DisplayCoordTransform* displayCoordTransform) const; private: caf::PdmPointer m_contourMapProjection; caf::PdmPointer m_parentContourMap; + + std::vector m_contourLinePolygons; + std::vector m_contourMapTriangles; + std::vector> m_labelBoundingBoxes; }; diff --git a/ApplicationCode/ProjectDataModel/RimContourMapView.cpp b/ApplicationCode/ProjectDataModel/RimContourMapView.cpp index 1c14b6622a..22a4d61f6e 100644 --- a/ApplicationCode/ProjectDataModel/RimContourMapView.cpp +++ b/ApplicationCode/ProjectDataModel/RimContourMapView.cpp @@ -49,6 +49,7 @@ const cvf::Mat4d defaultViewMatrix(1, 0, 0, 0, 0, 0, 0, 1); RimContourMapView::RimContourMapView() + : m_cameraPositionLastUpdate(cvf::Vec3d::UNDEFINED) { CAF_PDM_InitObject("Contour Map View", ":/2DMap16x16.png", "", ""); @@ -224,7 +225,9 @@ void RimContourMapView::updateGeometry() appendWellsAndFracturesToModel(); + createContourMapGeometry(); appendContourMapProjectionToModel(); + appendContourLinesToModel(); appendPickPointVisToModel(); @@ -243,6 +246,17 @@ void RimContourMapView::setFaultVisParameters() faultResultSettings()->customFaultResult()->setResultVariable("None"); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimContourMapView::createContourMapGeometry() +{ + if (m_viewer && m_contourMapProjection->isChecked()) + { + m_contourMapProjectionPartMgr->createProjectionGeometry(); + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -268,6 +282,31 @@ void RimContourMapView::appendContourMapProjectionToModel() } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimContourMapView::appendContourLinesToModel() +{ + if (m_viewer && m_contourMapProjection->isChecked()) + { + cvf::Scene* frameScene = m_viewer->frame(m_currentTimeStep); + if (frameScene) + { + cvf::String name = "ContourMapLines"; + this->removeModelByName(frameScene, name); + + cvf::ref contourMapLabelModelBasicList = new cvf::ModelBasicList; + contourMapLabelModelBasicList->setName(name); + + cvf::ref transForm = this->displayCoordTransform(); + + m_contourMapProjectionPartMgr->appendContourLinesToModel(viewer()->mainCamera(), contourMapLabelModelBasicList.p(), transForm.p()); + contourMapLabelModelBasicList->updateBoundingBoxesRecursive(); + frameScene->addModel(contourMapLabelModelBasicList.p()); + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -414,3 +453,26 @@ QWidget* RimContourMapView::createViewWidget(QWidget* mainWindowParent) } return widget; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimContourMapView::onViewNavigationChanged() +{ + cvf::Vec3d currentCameraPosition = viewer()->mainCamera()->position(); + if (m_cameraPositionLastUpdate.isUndefined() || zoomChangeAboveTreshold(currentCameraPosition)) + { + appendContourLinesToModel(); + m_cameraPositionLastUpdate = currentCameraPosition; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimContourMapView::zoomChangeAboveTreshold(const cvf::Vec3d& currentCameraPosition) const +{ + double distance = std::max(std::fabs(m_cameraPositionLastUpdate.z()), std::fabs(currentCameraPosition.z())); + const double threshold = 0.01 * distance; + return (m_cameraPositionLastUpdate - currentCameraPosition).length() > threshold; +} diff --git a/ApplicationCode/ProjectDataModel/RimContourMapView.h b/ApplicationCode/ProjectDataModel/RimContourMapView.h index ccf93e64e0..9126b32b15 100644 --- a/ApplicationCode/ProjectDataModel/RimContourMapView.h +++ b/ApplicationCode/ProjectDataModel/RimContourMapView.h @@ -43,7 +43,9 @@ protected: void updateCurrentTimeStep() override; void updateGeometry(); void setFaultVisParameters(); + void createContourMapGeometry(); void appendContourMapProjectionToModel(); + void appendContourLinesToModel(); void appendPickPointVisToModel(); void updateLegends() override; void updateViewWidgetAfterCreation() override; @@ -57,10 +59,14 @@ protected: QWidget* createViewWidget(QWidget* mainWindowParent) override; + void onViewNavigationChanged() override; + + bool zoomChangeAboveTreshold(const cvf::Vec3d& currentCameraPosition) const; private: cvf::ref m_contourMapProjectionPartMgr; caf::PdmChildField m_contourMapProjection; caf::PdmField m_showAxisLines; caf::PdmField m_showScaleLegend; + cvf::Vec3d m_cameraPositionLastUpdate; }; diff --git a/ApplicationCode/ProjectDataModel/RimViewWindow.cpp b/ApplicationCode/ProjectDataModel/RimViewWindow.cpp index 72bfa65042..afe9046da0 100644 --- a/ApplicationCode/ProjectDataModel/RimViewWindow.cpp +++ b/ApplicationCode/ProjectDataModel/RimViewWindow.cpp @@ -155,6 +155,21 @@ RimMdiWindowGeometry RimViewWindow::mdiWindowGeometry() } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimViewWindow::viewNavigationChanged() +{ + onViewNavigationChanged(); +} + +//-------------------------------------------------------------------------------------------------- +/// Default implementation of virtual method to trigger updates on view navigation (zoom, camera move, etc) +//-------------------------------------------------------------------------------------------------- +void RimViewWindow::onViewNavigationChanged() +{ +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -207,7 +222,6 @@ void RimViewWindow::setAsMdiWindow(int mainWindowID) } } - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/RimViewWindow.h b/ApplicationCode/ProjectDataModel/RimViewWindow.h index aae0500e23..cc4846dfb8 100644 --- a/ApplicationCode/ProjectDataModel/RimViewWindow.h +++ b/ApplicationCode/ProjectDataModel/RimViewWindow.h @@ -63,6 +63,8 @@ public: virtual QImage snapshotWindowContent() = 0; virtual void zoomAll() = 0; + void viewNavigationChanged(); + protected: void removeMdiWindowFromMdiArea(); @@ -75,6 +77,7 @@ protected: virtual void updateMdiWindowTitle(); // Has real default implementation virtual void deleteViewWidget() = 0; virtual void onLoadDataAndUpdate() = 0; + virtual void onViewNavigationChanged(); virtual bool isWindowVisible() { return m_showWindow();} // Virtual To allow special visibility control ////////// diff --git a/ApplicationCode/UserInterface/RiuViewer.cpp b/ApplicationCode/UserInterface/RiuViewer.cpp index 5797892cab..8539153839 100644 --- a/ApplicationCode/UserInterface/RiuViewer.cpp +++ b/ApplicationCode/UserInterface/RiuViewer.cpp @@ -749,7 +749,7 @@ void RiuViewer::updateNavigationPolicy() void RiuViewer::navigationPolicyUpdate() { caf::Viewer::navigationPolicyUpdate(); - + ownerViewWindow()->viewNavigationChanged(); if (m_rimView) { RimViewLinker* viewLinker = m_rimView->assosiatedViewLinker(); diff --git a/Fwk/AppFwk/cafViewer/cafViewer.h b/Fwk/AppFwk/cafViewer/cafViewer.h index 9a58b4ee64..eb02aaf883 100644 --- a/Fwk/AppFwk/cafViewer/cafViewer.h +++ b/Fwk/AppFwk/cafViewer/cafViewer.h @@ -174,6 +174,7 @@ protected: // Support the navigation policy concept virtual bool event( QEvent* e ); + cvf::ref m_navigationPolicy; bool m_navigationPolicyEnabled; @@ -196,7 +197,6 @@ private: void removeModelFromAllFrames(cvf::Model* model); void updateCamera(int width, int height); - void releaseOGlResourcesForCurrentFrame(); void debugShowRenderingSequencePartNames(); diff --git a/Fwk/VizFwk/LibRender/cvfDrawableText.cpp b/Fwk/VizFwk/LibRender/cvfDrawableText.cpp index a00ab3f356..67a1401336 100644 --- a/Fwk/VizFwk/LibRender/cvfDrawableText.cpp +++ b/Fwk/VizFwk/LibRender/cvfDrawableText.cpp @@ -189,14 +189,23 @@ void DrawableText::setUseDepthBuffer(bool useDepthBuffer) //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void DrawableText::addText(const String& text, const Vec3f& position) +void DrawableText::addText(const String& text, const Vec3f& position, const Vec3f& direction) { m_positions.push_back(position); m_texts.push_back(text); - m_boundingBox.add(position); + m_directions.push_back(direction); + m_boundingBox.add(position); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t DrawableText::numberOfTexts() const +{ + return m_texts.size(); +} + //-------------------------------------------------------------------------------------------------- /// Main shader based rendering path for the geometry //-------------------------------------------------------------------------------------------------- @@ -269,6 +278,28 @@ BoundingBox DrawableText::boundingBox() const } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::BoundingBox + DrawableText::textBoundingBox(const String& text, const Vec3f& position, const Vec3f& direction /*= Vec3f::X_AXIS*/) +{ + ref glyph = m_font->getGlyph(L'A'); + Vec2f textExtent(m_font->textExtent(text)); + short verticalAlignment = TextDrawer::calculateVerticalAlignmentOffset(*m_font, m_verticalAlignment); + float glyphMarginX = 0.25f * static_cast(glyph->width()); + float glyphMarginY = 0.25f * static_cast(glyph->height()); + + BoundingBox textBox; + std::array corners = + TextDrawer::textCorners(*glyph, cvf::Vec2f::ZERO, textExtent, verticalAlignment, direction, glyphMarginX, glyphMarginY); + for (const Vec3f& corner : corners) + { + textBox.add(position + corner); + } + return textBox; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -293,7 +324,7 @@ void DrawableText::renderText(OpenGLContext* oglContext, ShaderProgram* shaderPr CVF_ASSERT(!shaderProgram || ShaderProgram::supportedOpenGL(oglContext)); if (m_texts.size() == 0) return; - CVF_ASSERT(m_positions.size() == m_texts.size()); + CVF_ASSERT(m_positions.size() == m_texts.size() && m_positions.size() == m_directions.size()); if (m_checkPosVisible) { @@ -342,6 +373,7 @@ void DrawableText::renderText(OpenGLContext* oglContext, ShaderProgram* shaderPr Mat4d modelViewProjectionMatrix = Mat4d(matrixState.modelViewProjectionMatrix()); std::vector projCoords; std::vector textsToDraw; // Text strings to be drawn + std::vector directions; size_t pos; for (pos = 0; pos < m_positions.size(); pos++) { @@ -353,6 +385,7 @@ void DrawableText::renderText(OpenGLContext* oglContext, ShaderProgram* shaderPr // Note: Need to adjust for the current viewport, as the coords returned from project are in global windows coordinates projCoords.push_back(Vec3f(static_cast(proj.x() - matrixState.viewportPosition().x()), static_cast(proj.y() - matrixState.viewportPosition().y()), static_cast(1.0 - 2.0*proj.z()))); // Map z into 1 .. -1 textsToDraw.push_back(m_texts[pos]); + directions.push_back(m_directions[pos]); } } @@ -389,7 +422,7 @@ void DrawableText::renderText(OpenGLContext* oglContext, ShaderProgram* shaderPr for (i = 0; i < textsToDraw.size(); i++) { Vec3f pos = projCoords[i]; - drawer.addText(textsToDraw[i], pos); + drawer.addText(textsToDraw[i], pos, directions[i]); } if (shaderProgram) diff --git a/Fwk/VizFwk/LibRender/cvfDrawableText.h b/Fwk/VizFwk/LibRender/cvfDrawableText.h index beb75a568f..ffc08e69c8 100644 --- a/Fwk/VizFwk/LibRender/cvfDrawableText.h +++ b/Fwk/VizFwk/LibRender/cvfDrawableText.h @@ -65,8 +65,8 @@ public: void setFont(Font* font); ref font() const; - void addText(const String& text, const Vec3f& position); - + void addText(const String& text, const Vec3f& position, const Vec3f& direction = Vec3f::X_AXIS); + size_t numberOfTexts() const; void setVerticalAlignment(TextDrawer::Alignment alignment); void setTextColor(const Color3f& color); void setBackgroundColor(const Color3f& color); @@ -87,6 +87,7 @@ public: virtual size_t faceCount() const; virtual BoundingBox boundingBox() const; + BoundingBox textBoundingBox(const String& text, const Vec3f& position, const Vec3f& direction = Vec3f::X_AXIS); virtual bool rayIntersectCreateDetail(const Ray& ray, Vec3d* intersectionPoint, ref* hitDetail) const; bool rayIntersect(const Ray& ray, const Camera& camera, Vec3d* intersectionPoint); @@ -103,6 +104,8 @@ private: private: std::vector m_positions; // Coordinate of the lower left corner of where to place the text in pixel coordinates std::vector m_texts; // Text strings to be drawn + std::vector m_directions; // Text direction + ref m_font; // Font used to draw text TextDrawer::Alignment m_verticalAlignment; diff --git a/Fwk/VizFwk/LibRender/cvfGlyph.cpp b/Fwk/VizFwk/LibRender/cvfGlyph.cpp index ffe0a9f696..25e129beef 100644 --- a/Fwk/VizFwk/LibRender/cvfGlyph.cpp +++ b/Fwk/VizFwk/LibRender/cvfGlyph.cpp @@ -72,7 +72,9 @@ Glyph::Glyph(wchar_t character) m_horizontalBearingY(0), m_horizontalAdvance(0), m_textureImage(new TextureImage), - m_textureCoordinates(new FloatArray(8)) + m_textureCoordinates(new FloatArray(8)), + m_minFilter(NEAREST), + m_magFilter(NEAREST) { // Lower left m_textureCoordinates->set(0, 0.0f); @@ -345,8 +347,23 @@ void Glyph::setupAndBindTexture(OpenGLContext* oglContext, bool software) // Use fixed function texture setup ref texture = new Texture2D_FF(m_textureImage.p()); texture->setWrapMode(Texture2D_FF::CLAMP); - texture->setMinFilter(Texture2D_FF::NEAREST); - texture->setMagFilter(Texture2D_FF::NEAREST); + if (m_minFilter == NEAREST) + { + texture->setMinFilter(Texture2D_FF::NEAREST); + } + else + { + texture->setMinFilter(Texture2D_FF::LINEAR); + } + + if (m_magFilter == NEAREST) + { + texture->setMagFilter(Texture2D_FF::NEAREST); + } + else + { + texture->setMagFilter(Texture2D_FF::LINEAR); + } ref textureMapping = new RenderStateTextureMapping_FF(texture.p()); textureMapping->setTextureFunction(RenderStateTextureMapping_FF::MODULATE); @@ -359,8 +376,23 @@ void Glyph::setupAndBindTexture(OpenGLContext* oglContext, bool software) { ref sampler = new Sampler; sampler->setWrapMode(Sampler::CLAMP_TO_EDGE); - sampler->setMinFilter(Sampler::NEAREST); - sampler->setMagFilter(Sampler::NEAREST); + if (m_minFilter == NEAREST) + { + sampler->setMinFilter(Sampler::NEAREST); + } + else + { + sampler->setMinFilter(Sampler::LINEAR); + } + + if (m_magFilter == NEAREST) + { + sampler->setMagFilter(Sampler::NEAREST); + } + else + { + sampler->setMagFilter(Sampler::LINEAR); + } ref texture = new Texture(m_textureImage.p()); @@ -377,4 +409,20 @@ void Glyph::setupAndBindTexture(OpenGLContext* oglContext, bool software) } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void Glyph::setMinFilter(TextureFilter filter) +{ + m_minFilter = filter; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void Glyph::setMagFilter(TextureFilter filter) +{ + m_magFilter = filter; +} + } // namespace cvf diff --git a/Fwk/VizFwk/LibRender/cvfGlyph.h b/Fwk/VizFwk/LibRender/cvfGlyph.h index 3356098ed5..8e5302240a 100644 --- a/Fwk/VizFwk/LibRender/cvfGlyph.h +++ b/Fwk/VizFwk/LibRender/cvfGlyph.h @@ -55,6 +55,12 @@ class OpenGLContext; class Glyph : public Object { public: + enum TextureFilter + { + NEAREST, + LINEAR + }; + Glyph(wchar_t character); virtual ~Glyph(); @@ -78,6 +84,9 @@ public: void setupAndBindTexture(OpenGLContext* oglContext, bool software); + void setMinFilter(TextureFilter filter); + void setMagFilter(TextureFilter filter); + private: wchar_t m_character; // Character this glyph is generated from @@ -95,6 +104,10 @@ private: ref m_textureImage; // Pre-rendered image of m_character ref m_textureCoordinates; // Texture coordinates of where in the m_texgtureImage to find the given pre-rendered character ref m_textureBindings; // For shader based rendering this is a TextureBindings object, while software rendering uses RenderStateTextureMapping_FF instead + + // Texture filter options + TextureFilter m_minFilter; + TextureFilter m_magFilter; }; } // namespace cvf diff --git a/Fwk/VizFwk/LibRender/cvfTextDrawer.cpp b/Fwk/VizFwk/LibRender/cvfTextDrawer.cpp index 8106d4918a..7b9b2eeae2 100644 --- a/Fwk/VizFwk/LibRender/cvfTextDrawer.cpp +++ b/Fwk/VizFwk/LibRender/cvfTextDrawer.cpp @@ -99,13 +99,14 @@ TextDrawer::~TextDrawer() // -------------------------------------------------------------------------------------------------- /// Add text to be drawn // -------------------------------------------------------------------------------------------------- -void TextDrawer::addText(const String& text, const Vec2f& pos) +void TextDrawer::addText(const String& text, const Vec2f& pos, const Vec2f& dir) { CVF_ASSERT(!text.isEmpty()); CVF_ASSERT(!pos.isUndefined()); m_texts.push_back(text); m_positions.push_back(Vec3f(pos)); + m_directions.push_back(Vec3f(dir)); } @@ -115,13 +116,14 @@ void TextDrawer::addText(const String& text, const Vec2f& pos) /// Note: The Z coordinate needs to correspond with the orthographic projection that is setup /// to render the text. So the range should be <1..-1> with 1 being closest to the near plane. //-------------------------------------------------------------------------------------------------- -void TextDrawer::addText(const String& text, const Vec3f& pos) +void TextDrawer::addText(const String& text, const Vec3f& pos, const Vec3f& dir) { CVF_ASSERT(!text.isEmpty()); CVF_ASSERT(!pos.isUndefined()); m_texts.push_back(text); m_positions.push_back(pos); + m_directions.push_back(dir); } @@ -132,6 +134,7 @@ void TextDrawer::removeAllTexts() { m_texts.clear(); m_positions.clear(); + m_directions.clear(); } @@ -170,44 +173,7 @@ void TextDrawer::setVerticalAlignment(TextDrawer::Alignment alignment) CVF_ASSERT(m_font.notNull()); CVF_ASSERT(!m_font->isEmpty()); - switch (alignment) - { - case TextDrawer::TOP: - { - // Character assumed to reach all the way up - ref glyph_top = m_font->getGlyph(L'`'); - m_verticalAlignment = static_cast(-glyph_top->horizontalBearingY()); - break; - } - - case TextDrawer::CENTER: - { - // Center around A - ref glyph_top = m_font->getGlyph(L'A'); - m_verticalAlignment = static_cast(-((glyph_top->horizontalBearingY() + 1) >> 1)); - break; - } - - case TextDrawer::BASELINE: - { - m_verticalAlignment = 0; - break; - } - - case TextDrawer::BOTTOM: - { - // Character assumed to reach all the way down - ref glyph_bottom = m_font->getGlyph(L'g'); - m_verticalAlignment = static_cast(static_cast(glyph_bottom->height()) + glyph_bottom->horizontalBearingY()); - break; - } - - default: - { - CVF_FAIL_MSG("Unsupported alignment type"); - break; - } - } + m_verticalAlignment = calculateVerticalAlignmentOffset(*m_font, alignment); } @@ -307,7 +273,7 @@ void TextDrawer::renderSoftware(OpenGLContext* oglContext, const MatrixState& ma void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrixState, bool softwareRendering) { CVF_CALLSITE_OPENGL(oglContext); - CVF_ASSERT(m_positions.size() == m_texts.size()); + CVF_ASSERT(m_positions.size() == m_texts.size() && m_positions.size() == m_directions.size()); static const ushort connects[] = { 0, 1, 2, 0, 2, 3 }; static const ushort lineConnects[] = { 0, 1, 1, 2, 2, 3, 3, 0 }; @@ -315,11 +281,11 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix float vertexArray[12]; float textureCoords[8]; // Will be updated for each glyph - float* v1 = &vertexArray[0]; - float* v2 = &vertexArray[3]; - float* v3 = &vertexArray[6]; - float* v4 = &vertexArray[9]; - v1[2] = v2[2] = v3[2] = v4[2] = 0.0f; + std::array vertices = { &vertexArray[0], &vertexArray[3], &vertexArray[6], &vertexArray[9] }; + for (size_t i = 0; i < vertices.size(); ++i) + { + vertices[i][2] = 0.0f; + } // Prepare 2D pixel exact projection to draw texts Camera projCam; @@ -374,7 +340,7 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix // Use a fixed line spacing float lineSpacing = m_font->lineSpacing(); - Vec2f offset(0,0); + Vec2f offset(0, 0); // Render background and border // ------------------------------------------------------------------------- @@ -401,27 +367,24 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix // Figure out margin ref glyph = m_font->getGlyph(L'A'); - float charHeight = static_cast(glyph->height()); - float charWidth = static_cast(glyph->width()); - - offset.x() = cvf::Math::floor(charWidth/2.0f); - offset.y() = cvf::Math::floor(charHeight/2.0f); - + float glyphMarginX = cvf::Math::floor(0.5f * static_cast(glyph->width())); + float glyphMarginY = cvf::Math::floor(0.5f * static_cast(glyph->height())); size_t numTexts = m_texts.size(); for (size_t i = 0; i < numTexts; i++) { Vec3f pos = m_positions[i]; + String text = m_texts[i]; - Vec2ui textExtent = m_font->textExtent(text); + Vec2f textExtent(m_font->textExtent(text)); + std::array corners = textCorners(*glyph, cvf::Vec2f::ZERO, textExtent, m_verticalAlignment, m_directions[i], glyphMarginX, glyphMarginY); - Vec3f min = pos;//Vec3f(textBB.min()); - Vec3f max = Vec3f(min.x() + static_cast(textExtent.x()) + offset.x()*2.0f, min.y() + static_cast(textExtent.y()) + offset.y()*2.0f, 0.0f); - - // Draw the background triangle - v1[0] = min.x(); v1[1] = min.y(); v1[2] = pos.z(); - v2[0] = max.x(); v2[1] = min.y(); v2[2] = pos.z(); - v3[0] = max.x(); v3[1] = max.y(); v3[2] = pos.z(); - v4[0] = min.x(); v4[1] = max.y(); v4[2] = pos.z(); + for (size_t v = 0; v < vertices.size(); ++v) + { + for (int c = 0; c < 3; ++c) + { + vertices[v][c] = pos[c] + corners[v][c]; + } + } if (m_drawBackground) { @@ -432,10 +395,10 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix glDisable(GL_TEXTURE_2D); glColor3fv(m_backgroundColor.ptr()); glBegin(GL_TRIANGLE_FAN); - glVertex3fv(v1); - glVertex3fv(v2); - glVertex3fv(v3); - glVertex3fv(v4); + for (size_t v = 0; v < vertices.size(); ++v) + { + glVertex3fv(vertices[v]); + } glEnd(); #endif } @@ -456,10 +419,10 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix #ifndef CVF_OPENGL_ES glColor3fv(m_borderColor.ptr()); glBegin(GL_LINE_LOOP); - glVertex3fv(v1); - glVertex3fv(v2); - glVertex3fv(v3); - glVertex3fv(v4); + for (size_t v = 0; v < vertices.size(); ++v) + { + glVertex3fv(vertices[v]); + } glEnd(); #endif } @@ -523,16 +486,16 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix size_t i, j; for (i = 0; i < numTexts; i++) { - Vec3f pos = m_positions[i]; - - String text = m_texts[i]; + Vec3f pos = m_positions[i]; + Vec3f textDir = m_directions[i]; + String text = m_texts[i]; // Need to round off to integer positions to avoid buggy text drawing on iPad2 pos.x() = cvf::Math::floor(pos.x()); pos.y() = cvf::Math::floor(pos.y()); // Cursor incrementor - Vec2f cursor(pos); + Vec2f cursor(Vec2f::ZERO); cursor += offset; std::vector lines = text.split("\n"); @@ -547,30 +510,27 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix { character = line[j]; ref glyph = m_font->getGlyph(character); + cvf::Vec2f glyphExtent(static_cast(glyph->width()), static_cast(glyph->height())); + std::array corners = textCorners(*glyph, cursor, glyphExtent, m_verticalAlignment, textDir); - float textureWidth = static_cast(glyph->width()); - float textureHeight = static_cast(glyph->height()); - - // Lower left corner - v1[0] = cursor.x() + static_cast(glyph->horizontalBearingX()); - v1[1] = cursor.y() + static_cast(glyph->horizontalBearingY()) - textureHeight + static_cast(m_verticalAlignment); - v1[2] = pos.z(); - - // Lower right corner - v2[0] = v1[0] + textureWidth; - v2[1] = v1[1]; - v2[2] = pos.z(); - - // Upper right corner - v3[0] = v2[0]; - v3[1] = v1[1] + textureHeight; - v3[2] = pos.z(); - - // Upper left corner - v4[0] = v1[0]; - v4[1] = v3[1]; - v4[2] = pos.z(); - + for (size_t v = 0; v < vertices.size(); ++v) + { + for (int c = 0; c < 3; ++c) + { + vertices[v][c] = pos[c] + corners[v][c]; + } + } + if (textDir.dot(cvf::Vec3f::X_AXIS) < 0.9 && textDir.dot(cvf::Vec3f::Y_AXIS) < 0.9) + { + glyph->setMinFilter(Glyph::LINEAR); + glyph->setMagFilter(Glyph::LINEAR); + } + else + { + glyph->setMinFilter(Glyph::NEAREST); + glyph->setMagFilter(Glyph::NEAREST); + } + glyph->setupAndBindTexture(oglContext, softwareRendering); // Get texture coordinates @@ -592,19 +552,19 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix // First triangle in quad glTexCoord2fv(&textureCoordinatesPtr[0]); - glVertex3fv(v1); + glVertex3fv(vertices[0]); glTexCoord2fv(&textureCoordinatesPtr[2]); - glVertex3fv(v2); + glVertex3fv(vertices[1]); glTexCoord2fv(&textureCoordinatesPtr[4]); - glVertex3fv(v3); + glVertex3fv(vertices[2]); // Second triangle in quad glTexCoord2fv(&textureCoordinatesPtr[0]); - glVertex3fv(v1); + glVertex3fv(vertices[0]); glTexCoord2fv(&textureCoordinatesPtr[4]); - glVertex3fv(v3); + glVertex3fv(vertices[2]); glTexCoord2fv(&textureCoordinatesPtr[6]); - glVertex3fv(v4); + glVertex3fv(vertices[3]); glEnd(); #endif @@ -623,7 +583,7 @@ void TextDrawer::doRender2d(OpenGLContext* oglContext, const MatrixState& matrix } // CR - cursor.x() = pos.x() + offset.x(); + cursor.x() = offset.x(); cursor.y() += lineSpacing; } } @@ -693,4 +653,75 @@ bool TextDrawer::pickText(const Vec3f& pickCoord2d, const String& text, const Ve return false; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +short TextDrawer::calculateVerticalAlignmentOffset(Font& font, Alignment alignment) +{ + switch (alignment) + { + case TextDrawer::TOP: + { + // Character assumed to reach all the way up + ref glyph_top = font.getGlyph(L'`'); + return static_cast(-glyph_top->horizontalBearingY()); + } + + case TextDrawer::CENTER: + { + // Center around A + ref glyph_top = font.getGlyph(L'A'); + return static_cast(-((glyph_top->horizontalBearingY() + 1) >> 1)); + } + + case TextDrawer::BASELINE: + { + return 0; + } + + case TextDrawer::BOTTOM: + { + // Character assumed to reach all the way down + ref glyph_bottom = font.getGlyph(L'g'); + return static_cast(static_cast(glyph_bottom->height()) + glyph_bottom->horizontalBearingY()); + } + + default: + { + CVF_FAIL_MSG("Unsupported alignment type"); + break; + } + } + return 0; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::array + TextDrawer::textCorners(const Glyph& glyph, const Vec2f& textStart, const Vec2f& textExtent, short verticalAlignment, const Vec3f& textDirection, float marginX, float marginY) +{ + Vec3f tangent = textDirection; + if (tangent.x() < 0.0f) tangent *= -1.0f; + Vec2f tan2d(tangent.x(), tangent.y()); + Vec3f normal(-tan2d.perpendicularVector(), tangent.z()); + + float x1 = textStart.x() + static_cast(glyph.horizontalBearingX()) - marginY; + float y1 = textStart.y() + static_cast(glyph.horizontalBearingY()) - static_cast(glyph.height()) + static_cast(verticalAlignment) - marginY; + + float x2 = x1 + textExtent.x() + 2.0f * marginX; + float y2 = y1 + textExtent.y() + 2.0f * marginY; + + // Lower left corner + Vec3f c1 = tangent * x1 + normal * y1; + // Lower right corner + Vec3f c2 = tangent * x2 + normal * y1; + // Upper right corner + Vec3f c3 = tangent * x2 + normal * y2; + // Upper left corner + Vec3f c4 = tangent * x1 + normal * y2; + + return { c1, c2, c3, c4 }; +} + } // namespace cvf diff --git a/Fwk/VizFwk/LibRender/cvfTextDrawer.h b/Fwk/VizFwk/LibRender/cvfTextDrawer.h index 00c4da1a35..1ceabd0f6e 100644 --- a/Fwk/VizFwk/LibRender/cvfTextDrawer.h +++ b/Fwk/VizFwk/LibRender/cvfTextDrawer.h @@ -39,12 +39,15 @@ #include "cvfObject.h" #include "cvfString.h" +#include "cvfVector2.h" #include "cvfVector3.h" #include "cvfColor3.h" +#include namespace cvf { +class Glyph; class Font; class ShaderProgram; class MatrixState; @@ -72,8 +75,8 @@ public: TextDrawer(Font* font); virtual ~TextDrawer(); - void addText(const String& text, const Vec2f& pos); - void addText(const String& text, const Vec3f& pos); + void addText(const String& text, const Vec2f& pos, const Vec2f& dir = Vec2f::X_AXIS); + void addText(const String& text, const Vec3f& pos, const Vec3f& dir = Vec3f::X_AXIS); void removeAllTexts(); void setVerticalAlignment(Alignment alignment); @@ -96,7 +99,10 @@ public: void renderSoftware(OpenGLContext* oglContext, const MatrixState& matrixState); static bool pickText(const Vec3f& pickCoord2d, const String& text, const Vec3f& pos, Font* font); + + static short calculateVerticalAlignmentOffset(Font& font, Alignment alignment); + static std::array textCorners(const Glyph& glyph, const Vec2f& textStart, const Vec2f& textExtent, short verticalAlignment, const Vec3f& textDirection, float marginX = 0.0, float marginY = 0.0); private: void doRender2d(OpenGLContext* oglContext, const MatrixState& matrixState, bool softwareRendering); @@ -104,6 +110,7 @@ private: ref m_font; // Font used to draw text std::vector m_positions; // Coordinate of the lower left corner of where to place each individual text strings std::vector m_texts; // Text strings to be drawn + std::vector m_directions; // Clockwise rotations around the position in radians bool m_drawBackground; bool m_drawBorder;