///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2018- Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "Riv3dWellLogDrawSurfaceGenerator.h" #include "RimWellPath.h" #include "RimWellPathCollection.h" #include "RigWellPath.h" #include "RigWellPathGeometryTools.h" #include "cafDisplayCoordTransform.h" #include "cvfObject.h" #include "cvfPrimitiveSetIndexedUInt.h" #include "cvfBoundingBox.h" #include "cvfGeometryBuilderTriangles.h" #include "cvfArrowGenerator.h" #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- Riv3dWellLogDrawSurfaceGenerator::Riv3dWellLogDrawSurfaceGenerator(RimWellPath* wellPath) : m_wellPath(wellPath) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool Riv3dWellLogDrawSurfaceGenerator::createDrawSurface(const caf::DisplayCoordTransform* displayCoordTransform, const cvf::BoundingBox& wellPathClipBoundingBox, double planeAngle, double planeOffsetFromWellPathCenter, double planeWidth, double samplingIntervalSize) { CVF_ASSERT(samplingIntervalSize > 0); clearGeometry(); if (!wellPathGeometry() || wellPathGeometry()->m_measuredDepths.empty()) { return false; } if (!wellPathClipBoundingBox.isValid()) { return false; } RimWellPathCollection* wellPathCollection = nullptr; m_wellPath->firstAncestorOrThisOfTypeAsserted(wellPathCollection); std::vector wellPathDisplayCoords; { std::vector domainCoords = wellPathGeometry()->m_wellPathPoints; if (domainCoords.size() < (size_t)2) { // Need at least two well path points to create a valid path. return false; } wellPathDisplayCoords = displayCoordTransform->transformToDisplayCoords(domainCoords); } std::vector wellPathSegmentNormals = RigWellPathGeometryTools::calculateLineSegmentNormals(wellPathDisplayCoords, planeAngle); size_t indexToFirstVisibleSegment = 0u; if (wellPathCollection->wellPathClip) { double clipZDistance = wellPathCollection->wellPathClipZDistance; cvf::Vec3d clipLocation = wellPathClipBoundingBox.max() + clipZDistance * cvf::Vec3d(0, 0, 1); clipLocation = displayCoordTransform->transformToDisplayCoord(clipLocation); double horizontalLengthAlongWellToClipPoint; wellPathDisplayCoords = RigWellPath::clipPolylineStartAboveZ( wellPathDisplayCoords, clipLocation.z(), &horizontalLengthAlongWellToClipPoint, &indexToFirstVisibleSegment); } // Create curve normal vectors using the unclipped well path points and normals. createCurveNormalVectors(displayCoordTransform, indexToFirstVisibleSegment, planeOffsetFromWellPathCenter, planeWidth, samplingIntervalSize, wellPathSegmentNormals); // Note that normals are calculated on the full non-clipped well path. So we need to clip the start here. wellPathSegmentNormals.erase(wellPathSegmentNormals.begin(), wellPathSegmentNormals.end() - wellPathDisplayCoords.size()); if (wellPathDisplayCoords.size() < (size_t)2) { // Need at least two well path points to create a valid path. return false; } m_vertices.reserve(wellPathDisplayCoords.size() * 2); for (size_t i = 0; i < wellPathDisplayCoords.size(); i++) { m_vertices.push_back(wellPathDisplayCoords[i] + wellPathSegmentNormals[i] * (planeOffsetFromWellPathCenter - 0.025*planeWidth)); m_vertices.push_back(wellPathDisplayCoords[i] + wellPathSegmentNormals[i] * (planeOffsetFromWellPathCenter + 1.025*planeWidth)); } cvf::ref vertexArray = new cvf::Vec3fArray(m_vertices.size()); for (size_t i = 0; i < m_vertices.size(); ++i) { (*vertexArray)[i] = cvf::Vec3f(m_vertices[i]); } createBackground(vertexArray.p()); createBorder(vertexArray.p()); return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void Riv3dWellLogDrawSurfaceGenerator::clearGeometry() { m_background = nullptr; m_border = nullptr; m_curveNormalVectors = nullptr; m_vertices.clear(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref Riv3dWellLogDrawSurfaceGenerator::background() const { return m_background; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref Riv3dWellLogDrawSurfaceGenerator::border() const { return m_border; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref Riv3dWellLogDrawSurfaceGenerator::curveNormalVectors() const { return m_curveNormalVectors; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& Riv3dWellLogDrawSurfaceGenerator::vertices() const { return m_vertices; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void Riv3dWellLogDrawSurfaceGenerator::createCurveNormalVectors(const caf::DisplayCoordTransform* displayCoordTransform, size_t clipStartIndex, double planeOffsetFromWellPathCenter, double planeWidth, double samplingIntervalSize, const std::vector& segmentNormals) { std::vector interpolatedWellPathPoints; std::vector interpolatedWellPathNormals; double firstMd = wellPathGeometry()->m_measuredDepths.at(clipStartIndex); double lastMd = wellPathGeometry()->m_measuredDepths.back(); double md = lastMd; while (md >= firstMd) { cvf::Vec3d point = wellPathGeometry()->interpolatedPointAlongWellPath(md); point = displayCoordTransform->transformToDisplayCoord(point); cvf::Vec3d curveNormal = wellPathGeometry()->interpolatedVectorValuesAlongWellPath(segmentNormals, md); interpolatedWellPathPoints.push_back(point); interpolatedWellPathNormals.push_back(curveNormal.getNormalized()); md -= samplingIntervalSize; } std::vector arrowVertices; std::vector arrowVectors; arrowVertices.reserve(interpolatedWellPathPoints.size()); arrowVectors.reserve(interpolatedWellPathPoints.size()); double shaftRelativeRadius = 0.0125f; double arrowHeadRelativeRadius = shaftRelativeRadius * 3; double arrowHeadRelativeLength = arrowHeadRelativeRadius * 3; double totalArrowScaling = 1.0 / (1.0 - arrowHeadRelativeLength); // Normal lines. Start from one to avoid drawing at surface edge. for (size_t i = 1; i < interpolatedWellPathNormals.size(); i++) { arrowVertices.push_back(cvf::Vec3f(interpolatedWellPathPoints[i] + interpolatedWellPathNormals[i] * planeOffsetFromWellPathCenter)); arrowVectors.push_back(cvf::Vec3f(interpolatedWellPathNormals[i] * planeWidth * totalArrowScaling)); } if (arrowVertices.empty() || arrowVectors.empty()) { return; } m_curveNormalVectors = new cvf::DrawableVectors(); cvf::ref vertexArray = new cvf::Vec3fArray(arrowVertices); cvf::ref vectorArray = new cvf::Vec3fArray(arrowVectors); // Create the arrow glyph for the vector drawer cvf::GeometryBuilderTriangles arrowBuilder; cvf::ArrowGenerator gen; gen.setShaftRelativeRadius(shaftRelativeRadius); gen.setHeadRelativeRadius(arrowHeadRelativeRadius); gen.setHeadRelativeLength(arrowHeadRelativeLength); gen.setNumSlices(4); gen.generate(&arrowBuilder); m_curveNormalVectors->setGlyph(arrowBuilder.trianglesUShort().p(), arrowBuilder.vertices().p()); m_curveNormalVectors->setVectors(vertexArray.p(), vectorArray.p()); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void Riv3dWellLogDrawSurfaceGenerator::createBackground(cvf::Vec3fArray* vertexArray) { std::vector backgroundIndices; backgroundIndices.reserve(vertexArray->size()); for (size_t i = 0; i < vertexArray->size(); ++i) { backgroundIndices.push_back((cvf::uint) (i)); } // Background specific cvf::ref indexedUInt = new cvf::PrimitiveSetIndexedUInt(cvf::PrimitiveType::PT_TRIANGLE_STRIP); cvf::ref indexArray = new cvf::UIntArray(backgroundIndices); indexedUInt->setIndices(indexArray.p()); m_background = new cvf::DrawableGeo(); m_background->addPrimitiveSet(indexedUInt.p()); m_background->setVertexArray(vertexArray); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void Riv3dWellLogDrawSurfaceGenerator::createBorder(cvf::Vec3fArray* vertexArray) { std::vector borderIndices; borderIndices.reserve(m_vertices.size()); int secondLastEvenVertex = (int)vertexArray->size() - 4; // Border close to the well. All even indices. for (int i = 0; i <= secondLastEvenVertex; i += 2) { borderIndices.push_back((cvf::uint) i); borderIndices.push_back((cvf::uint) i + 2); } // Connect to border away from well borderIndices.push_back((cvf::uint) (vertexArray->size() - 2)); borderIndices.push_back((cvf::uint) (vertexArray->size() - 1)); int secondOddVertex = 3; int lastOddVertex = (int)vertexArray->size() - 1; // Border away from from well are odd indices in reverse order to create a closed surface. for (int i = lastOddVertex; i >= secondOddVertex; i -= 2) { borderIndices.push_back((cvf::uint) i); borderIndices.push_back((cvf::uint) i - 2); } // Close border borderIndices.push_back(1u); borderIndices.push_back(0u); cvf::ref indexedUInt = new cvf::PrimitiveSetIndexedUInt(cvf::PrimitiveType::PT_LINES); cvf::ref indexArray = new cvf::UIntArray(borderIndices); indexedUInt->setIndices(indexArray.p()); m_border = new cvf::DrawableGeo(); m_border->addPrimitiveSet(indexedUInt.p()); m_border->setVertexArray(vertexArray); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigWellPath* Riv3dWellLogDrawSurfaceGenerator::wellPathGeometry() const { return m_wellPath->wellPathGeometry(); }