ResInsight/ApplicationCode/ModelVisualization/RivPipeGeometryGenerator.cpp
Jacob Støren 4be58edf78 MSW code reverted. Regrettfully the work has to be reconsidered.
Both ERT and the resinsight code is rolled back to before MSW was introduced as refined.
p4#: 21999
2013-06-25 15:53:19 +02:00

577 lines
22 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2011-2012 Statoil ASA, Ceetron AS
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "cvfLibCore.h"
#include "cvfLibRender.h"
#include "cvfLibGeometry.h"
#include "cvfLibViewing.h"
#include "RivPipeGeometryGenerator.h"
#include <vector>
#include "cafEffectGenerator.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RivPipeGeometryGenerator::RivPipeGeometryGenerator()
{
m_radius = 1.0;
m_crossSectionNodeCount = 8;
m_minimumBendAngle = 80.0;
m_bendScalingFactor = 0.00001;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RivPipeGeometryGenerator::~RivPipeGeometryGenerator()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::setPipeCenterCoords(const cvf::Vec3dArray* coords)
{
m_originalPipeCenterCoords = coords;
clearComputedData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::setMinimumBendAngle(double degrees)
{
m_minimumBendAngle = degrees;
clearComputedData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::setBendScalingFactor(double scaleFactor)
{
m_bendScalingFactor = scaleFactor;
clearComputedData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::setRadius(double radius)
{
CVF_ASSERT(0 <= radius && radius < 1e100);
m_radius = radius;
clearComputedData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::setCrossSectionVertexCount(size_t nodeCount)
{
CVF_ASSERT( 2 < nodeCount && nodeCount < 1000000);
m_crossSectionNodeCount = nodeCount;
clearComputedData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::DrawableGeo> RivPipeGeometryGenerator::createPipeSurface()
{
if (m_radius == 0.0)
{
return NULL;
}
updateFilteredPipeCenterCoords();
cvf::ref<cvf::Vec3dArray> activeCoords = new cvf::Vec3dArray(m_filteredPipeCenterCoords);
return RivPipeGeometryGenerator::generateExtrudedCylinder(m_radius, m_crossSectionNodeCount, activeCoords.p());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::DrawableGeo> RivPipeGeometryGenerator::createCenterLine()
{
return generateLine(m_originalPipeCenterCoords.p());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::pipeSurfaceTextureCoords(cvf::Vec2fArray* textureCoords, const std::vector<double>& segmentResults, const cvf::ScalarMapper* mapper) const
{
CVF_ASSERT(textureCoords);
CVF_ASSERT(mapper);
CVF_ASSERT(segmentResults.size() == m_originalPipeCenterCoords->size() - 1);
size_t nodeCountPerSegment = m_crossSectionNodeCount * 4;
size_t vertexCount = m_filteredPipeSegmentToResult.size() * nodeCountPerSegment;
if (textureCoords->size() != vertexCount) textureCoords->resize(vertexCount);
size_t i;
for (i = 0; i < m_filteredPipeSegmentToResult.size(); i++)
{
size_t resultIndex = m_filteredPipeSegmentToResult[i];
cvf::Vec2f texCoord = mapper->mapToTextureCoord(segmentResults[resultIndex]);
size_t j;
for (j = 0; j < nodeCountPerSegment; j++)
{
textureCoords->set(i * nodeCountPerSegment + j, texCoord);
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::centerlineTextureCoords(cvf::Vec2fArray* textureCoords, const std::vector<double>& segmentResults, const cvf::ScalarMapper* mapper) const
{
CVF_ASSERT(textureCoords);
CVF_ASSERT(mapper);
CVF_ASSERT(segmentResults.size() == m_originalPipeCenterCoords->size() - 1);
size_t vertexCount = segmentResults.size() * 2;
if (textureCoords->size() != vertexCount) textureCoords->resize(vertexCount);
for (size_t vxIdx = 0; vxIdx < vertexCount; ++vxIdx)
{
cvf::Vec2f texCoord = mapper->mapToTextureCoord(segmentResults[vxIdx/2]);
textureCoords->set(vxIdx, texCoord);
}
}
//--------------------------------------------------------------------------------------------------
/// The circle points are generated in positive direction with circle normal pointing along yDir ^ zDir
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::computeCircle(double radius, size_t tesselationCount, const cvf::Vec3d& center, const cvf::Vec3d& yDirection, const cvf::Vec3d& zDirection, std::vector<cvf::Vec3d>* nodes)
{
cvf::Vec3d normal;
cvf::Vec3d expandedCoord;
double delta = (2 * cvf::PI_D) / tesselationCount;
double angle = 0.0;
size_t i;
for (i = 0; i < tesselationCount; i++)
{
// These are the local coordinates on the circle
double fLocalNormCoordY = cvf::Math::cos(angle);
double fLocalNormCoordZ = cvf::Math::sin(angle);
// Compute normal (vector going from center to the point on the circle)
// Do it this way and we can use this normal directly as long as both input orientation vectors are normalized (which they should be)
normal = yDirection*fLocalNormCoordY + zDirection*fLocalNormCoordZ;
normal.normalize();
// get the global expanded coord by scaling by radius
expandedCoord = center + radius*normal;
nodes->push_back(expandedCoord);
angle += delta;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::DrawableGeo> RivPipeGeometryGenerator::generateLine(const cvf::Vec3dArray* coords)
{
CVF_ASSERT(coords != NULL);
if (coords->size() < 2 ) return NULL;
size_t duplicateVertexCount = 2 * (coords->size() - 1);
cvf::ref<cvf::UIntArray> indices = new cvf::UIntArray;
indices->resize(duplicateVertexCount);
size_t i;
for (i = 0; i < duplicateVertexCount; i++)
{
indices->set(i, static_cast<cvf::uint>(i));
}
cvf::ref<cvf::DrawableGeo> geo = new cvf::DrawableGeo;
// Convert double data to float data before sending them to ceeViz.
// Sigh ....
cvf::ref<cvf::Vec3fArray> floatCoords = new cvf::Vec3fArray;
floatCoords->resize(duplicateVertexCount);
(*floatCoords)[0] = cvf::Vec3f((*coords)[0]);
for (i = 1; i < coords->size() - 1; ++i)
{
(*floatCoords)[2 * i - 1] = cvf::Vec3f((*coords)[i]);
(*floatCoords)[2 * i + 0] = cvf::Vec3f((*coords)[i]);
}
(*floatCoords)[duplicateVertexCount - 1] = cvf::Vec3f((*coords)[coords->size() - 1]);
geo->setVertexArray(floatCoords.p());
cvf::ref<cvf::PrimitiveSetIndexedUInt> prim = new cvf::PrimitiveSetIndexedUInt(cvf::PT_LINES);
prim->setIndices(indices.p());
geo->addPrimitiveSet(prim.p());
return geo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::DrawableGeo> RivPipeGeometryGenerator::generateExtrudedCylinder(double radius, size_t crossSectionNodeCount, const cvf::Vec3dArray* cylinderCenterCoords)
{
CVF_ASSERT(cylinderCenterCoords != NULL);
if (cylinderCenterCoords->size() < 2) return NULL;
std::vector<cvf::Vec3f> crossSectionVertices;
std::vector<cvf::Vec3f> cylinderSegmentNormals;
cvf::Vec3d dir = (*cylinderCenterCoords)[1] - (*cylinderCenterCoords)[0];
dir.normalize();
cvf::Vec3d orient1 = dir.perpendicularVector();
orient1.normalize();
cvf::Vec3d orient2 = dir ^ orient1;
orient2.normalize();
std::vector<cvf::Vec3d> extrudedNodes;
computeCircle(radius, crossSectionNodeCount, (*cylinderCenterCoords)[0], orient1, orient2, &extrudedNodes);
// Insert the first set of vertices
size_t i;
for (i = 0; i < extrudedNodes.size(); i++)
{
crossSectionVertices.push_back(cvf::Vec3f(extrudedNodes[i]));
}
// Calculate first valid pipe direction, to be able to handle centerNodes in the same place
cvf::Vec3d lastValidPipeDirection(0,0,-1);
for (i = 0; i < cylinderCenterCoords->size() - 1; i++)
{
cvf::Vec3d candidateDir = (*cylinderCenterCoords)[i] - (*cylinderCenterCoords)[i+1];
if (candidateDir.normalize())
{
lastValidPipeDirection = candidateDir;
break;
}
}
if (i >= cylinderCenterCoords->size()-1) return NULL; // The pipe coordinates is all the same point
// Loop along the cylinder center coords and calculate the cross section vertexes in each center vertex
for (size_t ccIdx = 0; ccIdx < cylinderCenterCoords->size() - 1; ccIdx++)
{
size_t nextCcIdx = ccIdx + 1;
// Calculate this and next pipe direction, and intersection plane
cvf::Vec3d firstCoord = (*cylinderCenterCoords)[ccIdx];
cvf::Vec3d secondCoord = (*cylinderCenterCoords)[nextCcIdx];
cvf::Vec3d candidateDir = secondCoord - firstCoord;
if (!candidateDir.normalize())
{
candidateDir = lastValidPipeDirection;
}
else
{
lastValidPipeDirection = candidateDir;
}
cvf::Vec3d nextDir = lastValidPipeDirection;
if (nextCcIdx + 1 < cylinderCenterCoords->size())
{
cvf::Vec3d thirdCoord = (*cylinderCenterCoords)[nextCcIdx + 1];
nextDir = thirdCoord - secondCoord;
}
// If the next vector is too small, just assume the pipe is heading straight on
if (!nextDir.normalize()) nextDir = candidateDir;
cvf::Vec3d intersectionPlaneNormal = candidateDir + nextDir;
// if (intersectionPlaneNormal.lengthSquared() < 1e-10) intersectionPlaneNormal = nextDir; // candidateDir == -nextDir => 180 deg turn
computeExtrudedCoordsAndNormals(secondCoord, intersectionPlaneNormal, candidateDir, crossSectionNodeCount, &extrudedNodes, &crossSectionVertices, &cylinderSegmentNormals);
}
size_t crossSectionCount = crossSectionVertices.size() / crossSectionNodeCount;
if (crossSectionCount < 2) return NULL;
CVF_ASSERT(crossSectionVertices.size() - crossSectionNodeCount == cylinderSegmentNormals.size());
size_t segmentCount = crossSectionCount - 1;
size_t vertexCount = segmentCount * crossSectionNodeCount * 4;
cvf::ref<cvf::Vec3fArray> quadVertexArray = new cvf::Vec3fArray();
quadVertexArray->reserve(vertexCount);
cvf::ref<cvf::Vec3fArray> quadNormalArray = new cvf::Vec3fArray();
quadNormalArray->reserve(vertexCount);
size_t segmentIdx = 0;
for (segmentIdx = 0; segmentIdx < segmentCount; segmentIdx++)
{
size_t i;
for (i = 0; i < crossSectionNodeCount - 1; i++)
{
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 0) * crossSectionNodeCount) + i + 0]);
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 0) * crossSectionNodeCount) + i + 1]);
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 1) * crossSectionNodeCount) + i + 1]);
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 1) * crossSectionNodeCount) + i + 0]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + i + 0]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + i + 1]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + i + 1]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + i + 0]);
}
// Last quad closing the cylinder
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 0) * crossSectionNodeCount) + crossSectionNodeCount - 1]);
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 0) * crossSectionNodeCount) + 0]);
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 1) * crossSectionNodeCount) + 0]);
quadVertexArray->add(crossSectionVertices[( (segmentIdx + 1) * crossSectionNodeCount) + crossSectionNodeCount - 1]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + crossSectionNodeCount - 1]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + 0]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + 0]);
quadNormalArray->add(cylinderSegmentNormals[( (segmentIdx + 0) * crossSectionNodeCount) + crossSectionNodeCount - 1]);
}
CVF_ASSERT(vertexCount == quadVertexArray->size());
CVF_ASSERT(vertexCount == quadNormalArray->size());
cvf::ref<cvf::DrawableGeo> geo = new cvf::DrawableGeo;
geo->setFromQuadVertexArray(quadVertexArray.p());
geo->setNormalArray(quadNormalArray.p());
return geo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::computeExtrudedCoordsAndNormals( cvf::Vec3d intersectionCoord,
cvf::Vec3d intersectionPlaneNormal,
cvf::Vec3d segmentDirection,
size_t crossSectionNodeCount,
std::vector<cvf::Vec3d>* extrudedNodes,
std::vector<cvf::Vec3f>* crossSectionVertices,
std::vector<cvf::Vec3f>* cylinderSegmentNormals)
{
cvf::Plane intersectionPlane;
intersectionPlane.setFromPointAndNormal(intersectionCoord, intersectionPlaneNormal);
cvf::Ray ray;
ray.setDirection(segmentDirection);
// Calculate next set of extruded vertices
std::vector<cvf::Vec3d> nextExtrudedNodes;
for (size_t csnIdx = 0; csnIdx < crossSectionNodeCount; csnIdx++)
{
ray.setOrigin((*extrudedNodes)[csnIdx]);
cvf::Vec3d intersection;
if (ray.planeIntersect(intersectionPlane, &intersection))
{
nextExtrudedNodes.push_back(intersection);
}
else
{
cvf::Ray oppositeRay(ray);
oppositeRay.setDirection(-segmentDirection);
if (oppositeRay.planeIntersect(intersectionPlane, &intersection))
{
nextExtrudedNodes.push_back(intersection);
}
else
{
// Parallel plane and ray
CVF_ASSERT(false);
}
}
}
// Calculate the normals for the cylinder segment
cvf::Plane cylinderPlane;
cylinderPlane.setFromPointAndNormal(intersectionCoord, segmentDirection);
for (size_t csnIdx = 0; csnIdx < crossSectionNodeCount; csnIdx++)
{
crossSectionVertices->push_back(cvf::Vec3f(nextExtrudedNodes[csnIdx]));
cvf::Vec3d pipeNormal;
cylinderPlane.projectVector(nextExtrudedNodes[csnIdx] - intersectionCoord, &pipeNormal);
cylinderSegmentNormals->push_back(cvf::Vec3f(pipeNormal));
}
*extrudedNodes = nextExtrudedNodes;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::updateFilteredPipeCenterCoords()
{
if (m_originalPipeCenterCoords->size() < 2) return;
if (m_filteredPipeCenterCoords.size() > 0) return;
double squareDistanceTolerance = 1e-4*1e-4;
double angleTolerance = 1e-5;
size_t firstSegmentWithLength = 0;
size_t i;
for (i = 0; i < m_originalPipeCenterCoords->size() - 1; i++)
{
cvf::Vec3d candidateDir = (*m_originalPipeCenterCoords)[i] - (*m_originalPipeCenterCoords)[i+1];
double dirLengthSq = candidateDir.lengthSquared();
if (dirLengthSq > squareDistanceTolerance && candidateDir.normalize())
{
firstSegmentWithLength = i;
break;
}
}
// Only zero-length segments
if (i == m_originalPipeCenterCoords->size() - 1) return;
m_filteredPipeCenterCoords.push_back(m_originalPipeCenterCoords->get(firstSegmentWithLength));
m_filteredPipeSegmentToResult.push_back(firstSegmentWithLength);
cvf::Vec3d lastValidDirectionAB;
size_t lastValidSegment = 0;
for (i = firstSegmentWithLength + 1; i < m_originalPipeCenterCoords->size() - 1; i++)
{
cvf::Vec3d coordA = m_originalPipeCenterCoords->get(i - 1);
cvf::Vec3d coordB = m_originalPipeCenterCoords->get(i + 0);
cvf::Vec3d coordC = m_originalPipeCenterCoords->get(i + 1);
cvf::Vec3d directionAB = coordB - coordA;
// Skip segment lengths below tolerance
double dirLengthSq = directionAB.lengthSquared();
if (directionAB.lengthSquared() > squareDistanceTolerance)
{
lastValidDirectionAB = directionAB.getNormalized();
lastValidSegment = i;
}
cvf::Vec3d directionBC = coordC - coordB;
if (directionBC.lengthSquared() < squareDistanceTolerance)
{
continue;
}
double cosMinBendAngle = cvf::Math::cos(cvf::Math::toRadians(m_minimumBendAngle));
double dotProduct = lastValidDirectionAB * (-directionBC).getNormalized();
if (dotProduct > cosMinBendAngle)
{
bool success = false;
cvf::Vec3d pipeIntermediateDirection = (lastValidDirectionAB + directionBC.getNormalized()).getNormalized(&success);
if (!success)
{
pipeIntermediateDirection = lastValidDirectionAB.perpendicularVector();
}
double bendRadius = m_bendScalingFactor * m_radius + 1.0e-30;
cvf::Vec3d firstIntermediate = coordB - pipeIntermediateDirection * bendRadius;
m_filteredPipeCenterCoords.push_back(firstIntermediate);
m_filteredPipeSegmentToResult.push_back(lastValidSegment);
m_filteredPipeCenterCoords.push_back(coordB);
m_filteredPipeSegmentToResult.push_back(i);
cvf::Vec3d secondIntermediate = coordB + pipeIntermediateDirection * bendRadius;
m_filteredPipeCenterCoords.push_back(secondIntermediate);
m_filteredPipeSegmentToResult.push_back(i);
}
else
{
m_filteredPipeCenterCoords.push_back(coordB);
m_filteredPipeSegmentToResult.push_back(i);
}
}
// Add last cross section if not duplicate coordinate
cvf::Vec3d coordA = m_originalPipeCenterCoords->get(m_originalPipeCenterCoords->size() - 2);
cvf::Vec3d coordB = m_originalPipeCenterCoords->get(m_originalPipeCenterCoords->size() - 1);
cvf::Vec3d directionAB = coordB - coordA;
if (directionAB.lengthSquared() > squareDistanceTolerance)
{
m_filteredPipeCenterCoords.push_back(m_originalPipeCenterCoords->get(m_originalPipeCenterCoords->size() - 1));
}
else
{
// Remove last segment as the length is below tolerance
m_filteredPipeSegmentToResult.pop_back();
}
CVF_ASSERT(m_filteredPipeCenterCoords.size() - 1 == m_filteredPipeSegmentToResult.size());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivPipeGeometryGenerator::clearComputedData()
{
m_filteredPipeCenterCoords.clear();
m_filteredPipeSegmentToResult.clear();
}