///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) Statoil ASA // Copyright (C) Ceetron Solutions AS // Copyright (C) 2011-2012 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RivPipeGeometryGenerator.h" #include "RivObjectSourceInfo.h" #include "cafEffectGenerator.h" #include "cvfDrawableGeo.h" #include "cvfPlane.h" #include "cvfPrimitiveSetIndexedUInt.h" #include "cvfRay.h" //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RivPipeGeometryGenerator::RivPipeGeometryGenerator() { m_radius = 1.0; m_crossSectionNodeCount = 8; m_minimumBendAngle = 80.0; m_bendScalingFactor = 0.00001; m_firstVisibleSegmentIndex = 0; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RivPipeGeometryGenerator::~RivPipeGeometryGenerator() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivPipeGeometryGenerator::setPipeCenterCoords( const cvf::Vec3dArray* coords ) { m_originalPipeCenterCoords = coords; clearComputedData(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::cref RivPipeGeometryGenerator::pipeCenterCoords() const { return m_originalPipeCenterCoords.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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 RivPipeGeometryGenerator::createPipeSurface() { if ( m_radius == 0.0 ) { return nullptr; } updateFilteredPipeCenterCoords(); cvf::ref activeCoords = new cvf::Vec3dArray( m_filteredPipeCenterCoords ); return RivPipeGeometryGenerator::generateExtrudedCylinder( m_radius, m_crossSectionNodeCount, activeCoords.p() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RivPipeGeometryGenerator::createCenterLine() { return generateLine( m_originalPipeCenterCoords.p() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivPipeGeometryGenerator::pipeSurfaceTextureCoords( cvf::Vec2fArray* textureCoords, const std::vector& 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& 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* 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 RivPipeGeometryGenerator::generateLine( const cvf::Vec3dArray* coords ) { CVF_ASSERT( coords != nullptr ); if ( coords->size() < 2 ) return nullptr; size_t duplicateVertexCount = 2 * ( coords->size() - 1 ); cvf::ref indices = new cvf::UIntArray; indices->resize( duplicateVertexCount ); size_t i; for ( i = 0; i < duplicateVertexCount; i++ ) { indices->set( i, static_cast( i ) ); } cvf::ref geo = new cvf::DrawableGeo; // Convert double data to float data before sending them to ceeViz. // Sigh .... cvf::ref 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 prim = new cvf::PrimitiveSetIndexedUInt( cvf::PT_LINES ); prim->setIndices( indices.p() ); geo->addPrimitiveSet( prim.p() ); return geo; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RivPipeGeometryGenerator::generateExtrudedCylinder( double radius, size_t crossSectionNodeCount, const cvf::Vec3dArray* cylinderCenterCoords ) { CVF_ASSERT( cylinderCenterCoords != nullptr ); if ( cylinderCenterCoords->size() < 2 ) return nullptr; std::vector crossSectionVertices; std::vector 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 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.emplace_back( 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 nullptr; // 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 ) // candidateDir == -nextDir => 180 deg turn { CVF_ASSERT( false ); // This is never supposed to happen due to what's done in // updateFilteredPipeCenterCoords(). So look there for the bug... intersectionPlaneNormal = nextDir; } computeExtrudedCoordsAndNormals( secondCoord, intersectionPlaneNormal, candidateDir, crossSectionNodeCount, &extrudedNodes, &crossSectionVertices, &cylinderSegmentNormals ); } size_t crossSectionCount = crossSectionVertices.size() / crossSectionNodeCount; if ( crossSectionCount < 2 ) return nullptr; CVF_ASSERT( crossSectionVertices.size() - crossSectionNodeCount == cylinderSegmentNormals.size() ); size_t segmentCount = crossSectionCount - 1; size_t vertexCount = segmentCount * crossSectionNodeCount * 4; cvf::ref quadVertexArray = new cvf::Vec3fArray(); quadVertexArray->reserve( vertexCount ); cvf::ref quadNormalArray = new cvf::Vec3fArray(); quadNormalArray->reserve( vertexCount ); size_t segmentIdx = 0; for ( segmentIdx = 0; segmentIdx < segmentCount; segmentIdx++ ) { for ( size_t nodeIdx = 0; nodeIdx < crossSectionNodeCount - 1; nodeIdx++ ) { quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 0] ); quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 1 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 1 ) * crossSectionNodeCount ) + nodeIdx + 0] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 0] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 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 geo = new cvf::DrawableGeo; geo->setFromQuadVertexArray( quadVertexArray.p() ); geo->setNormalArray( quadNormalArray.p() ); return geo; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RivPipeGeometryGenerator::generateVariableRadiusTube( size_t crossSectionNodeCount, const cvf::Vec3dArray* cylinderCenterCoords, const std::vector& radii ) { CVF_ASSERT( cylinderCenterCoords != nullptr ); // Calculate first valid pipe direction, to be able to handle centerNodes in the same place cvf::Vec3d lastValidPipeDirection = cvf::Vec3d::UNDEFINED; for ( size_t i = 0; i < cylinderCenterCoords->size() - 1; i++ ) { cvf::Vec3d candidateDir = ( *cylinderCenterCoords )[i + 1] - ( *cylinderCenterCoords )[i]; if ( candidateDir.normalize() ) { lastValidPipeDirection = candidateDir; break; } } if ( lastValidPipeDirection.isUndefined() ) return nullptr; // The pipe coordinates are all the same point cvf::Vec3d dir = ( *cylinderCenterCoords )[1] - ( *cylinderCenterCoords )[0]; if ( !dir.normalize() ) { dir = lastValidPipeDirection; } cvf::Vec3d orient1 = dir.perpendicularVector(); orient1.normalize(); cvf::Vec3d orient2 = dir ^ orient1; orient2.normalize(); std::vector lastExtrudedNodes; computeCircle( radii[0], crossSectionNodeCount, ( *cylinderCenterCoords )[0], orient1, orient2, &lastExtrudedNodes ); std::vector crossSectionVertices; std::vector cylinderSegmentNormals; // Insert the first set of vertices for ( const auto& lastExtrudedNode : lastExtrudedNodes ) { crossSectionVertices.emplace_back( lastExtrudedNode ); } // 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 nextDir = secondCoord - firstCoord; if ( !nextDir.normalize() ) { nextDir = lastValidPipeDirection; } else { lastValidPipeDirection = nextDir; } orient1 = nextDir.perpendicularVector().getNormalized(); orient2 = ( nextDir ^ orient1 ).getNormalized(); std::vector extrudedNodes; computeCircle( radii[nextCcIdx], crossSectionNodeCount, ( *cylinderCenterCoords )[nextCcIdx], orient1, orient2, &extrudedNodes ); // Insert the next set of vertices and calculate normals for segment for ( size_t i = 0; i < extrudedNodes.size(); i++ ) { cvf::Vec3f nextNodeAlongWellPath = cvf::Vec3f( extrudedNodes[i] ); cvf::Vec3f currentNode = cvf::Vec3f( lastExtrudedNodes[i] ); cvf::Vec3f wellDirectionDir = nextNodeAlongWellPath - currentNode; if ( !wellDirectionDir.normalize() ) { wellDirectionDir = cvf::Vec3f( lastValidPipeDirection ); } cvf::Vec3f lastNodeAlongCircle; cvf::Vec3f nextNodeAlongCircle; if ( i > 0 ) { lastNodeAlongCircle = cvf::Vec3f( lastExtrudedNodes[i - 1] ); } else { lastNodeAlongCircle = cvf::Vec3f( lastExtrudedNodes.back() ); } if ( i < extrudedNodes.size() - 1 ) { nextNodeAlongCircle = cvf::Vec3f( lastExtrudedNodes[i + 1] ); } else { nextNodeAlongCircle = cvf::Vec3f( lastExtrudedNodes.front() ); } cvf::Vec3f circleDir = ( nextNodeAlongCircle - lastNodeAlongCircle ).getNormalized(); cvf::Vec3f segmentNormal = ( wellDirectionDir ^ circleDir ).getNormalized(); crossSectionVertices.emplace_back( nextNodeAlongWellPath ); cylinderSegmentNormals.push_back( segmentNormal ); } lastExtrudedNodes = extrudedNodes; } size_t crossSectionCount = crossSectionVertices.size() / crossSectionNodeCount; if ( crossSectionCount < 2 ) return nullptr; CVF_ASSERT( crossSectionVertices.size() - crossSectionNodeCount == cylinderSegmentNormals.size() ); size_t segmentCount = crossSectionCount - 1; size_t vertexCount = segmentCount * crossSectionNodeCount * 4; cvf::ref quadVertexArray = new cvf::Vec3fArray(); quadVertexArray->reserve( vertexCount ); cvf::ref quadNormalArray = new cvf::Vec3fArray(); quadNormalArray->reserve( vertexCount ); size_t segmentIdx = 0; for ( segmentIdx = 0; segmentIdx < segmentCount; segmentIdx++ ) { for ( size_t nodeIdx = 0; nodeIdx < crossSectionNodeCount - 1; nodeIdx++ ) { quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 0] ); quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 1 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadVertexArray->add( crossSectionVertices[( ( segmentIdx + 1 ) * crossSectionNodeCount ) + nodeIdx + 0] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 0] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 1] ); quadNormalArray->add( cylinderSegmentNormals[( ( segmentIdx + 0 ) * crossSectionNodeCount ) + nodeIdx + 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 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* extrudedNodes, std::vector* crossSectionVertices, std::vector* cylinderSegmentNormals ) { cvf::Plane intersectionPlane; intersectionPlane.setFromPointAndNormal( intersectionCoord, intersectionPlaneNormal ); cvf::Ray ray; ray.setDirection( segmentDirection ); // Calculate next set of extruded vertices std::vector 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.empty() ) return; double squareDistanceTolerance = 1e-4 * 1e-4; const size_t lastOriginalCoordIdx = m_originalPipeCenterCoords->size() - 1; size_t firstSegmentWithLength = findFirstSegmentWithLength( squareDistanceTolerance ); // Return if we have only zero-length segments if ( firstSegmentWithLength == cvf::UNDEFINED_SIZE_T ) return; m_filteredPipeCenterCoords.push_back( m_originalPipeCenterCoords->get( firstSegmentWithLength ) ); m_filteredPipeSegmentToResult.push_back( firstSegmentWithLength ); cvf::Vec3d lastValidDirectionAB; size_t lastValidSegment = 0; // Go along the line, inserting bends, and skipping zero segments. // The zero segments are skipped by ignoring the _first_ coordinate(s) equal to the next ones for ( size_t coordBIdx = firstSegmentWithLength + 1; coordBIdx < lastOriginalCoordIdx; coordBIdx++ ) { cvf::Vec3d coordA = m_originalPipeCenterCoords->get( coordBIdx - 1 ); cvf::Vec3d coordB = m_originalPipeCenterCoords->get( coordBIdx + 0 ); cvf::Vec3d coordC = m_originalPipeCenterCoords->get( coordBIdx + 1 ); cvf::Vec3d directionAB = coordB - coordA; if ( directionAB.lengthSquared() > squareDistanceTolerance ) { lastValidDirectionAB = directionAB.getNormalized(); lastValidSegment = coordBIdx; } // Wait to store a segment until we find an endpoint that is the start point of a valid segment cvf::Vec3d directionBC = coordC - coordB; if ( directionBC.lengthSquared() < squareDistanceTolerance ) { continue; } // Check if the angle between AB and BC is sharper than m_minimumBendAngle (Straight == 180 deg) // Sharper angle detected, insert bending stuff 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() ); pipeIntermediateDirection.getNormalized( &success ); if ( pipeIntermediateDirection.lengthSquared() < squareDistanceTolerance ) { pipeIntermediateDirection = lastValidDirectionAB.perpendicularVector(); } else { pipeIntermediateDirection.normalize(); } double bendRadius = m_bendScalingFactor * m_radius + 1.0e-30; cvf::Vec3d firstIntermediate = coordB - pipeIntermediateDirection * bendRadius; cvf::Vec3d secondIntermediate = coordB + pipeIntermediateDirection * bendRadius; m_filteredPipeCenterCoords.push_back( firstIntermediate ); m_filteredPipeSegmentToResult.push_back( lastValidSegment ); m_filteredPipeCenterCoords.push_back( coordB ); m_filteredPipeSegmentToResult.push_back( coordBIdx ); m_filteredPipeCenterCoords.push_back( secondIntermediate ); m_filteredPipeSegmentToResult.push_back( coordBIdx ); } else { m_filteredPipeCenterCoords.push_back( coordB ); m_filteredPipeSegmentToResult.push_back( coordBIdx ); } } // Add the last point, as the above loop will not end the last none-zero segment, but wait for the start of the next // valid one. m_filteredPipeCenterCoords.push_back( m_originalPipeCenterCoords->get( lastOriginalCoordIdx ) ); CVF_ASSERT( m_filteredPipeCenterCoords.size() - 1 == m_filteredPipeSegmentToResult.size() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RivPipeGeometryGenerator::findFirstSegmentWithLength( double squareDistanceTolerance ) { size_t segIdx; for ( segIdx = 0; segIdx < m_originalPipeCenterCoords->size() - 1; segIdx++ ) { cvf::Vec3d candidateDir = ( *m_originalPipeCenterCoords )[segIdx] - ( *m_originalPipeCenterCoords )[segIdx + 1]; double dirLengthSq = candidateDir.lengthSquared(); if ( dirLengthSq > squareDistanceTolerance && candidateDir.normalize() ) { return segIdx; } } return cvf::UNDEFINED_SIZE_T; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivPipeGeometryGenerator::clearComputedData() { m_filteredPipeCenterCoords.clear(); m_filteredPipeSegmentToResult.clear(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RivPipeGeometryGenerator::segmentIndexFromTriangleIndex( size_t triangleIndex ) const { size_t segIndex = triangleIndex / ( m_crossSectionNodeCount * 2 ); CVF_ASSERT( segIndex < m_filteredPipeSegmentToResult.size() ); size_t resultIndex = m_filteredPipeSegmentToResult[segIndex]; return resultIndex + m_firstVisibleSegmentIndex; } //-------------------------------------------------------------------------------------------------- /// Well pipes are clipped, set index to first segment in visible well path //-------------------------------------------------------------------------------------------------- void RivPipeGeometryGenerator::setFirstVisibleSegmentIndex( size_t segmentIndex ) { m_firstVisibleSegmentIndex = segmentIndex; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivPipeGeometryGenerator::cylinderWithCenterLineParts( cvf::Collection* destinationParts, const std::vector& centerCoords, const cvf::Color3f& color, double radius ) { RivPipeGeometryGenerator geoGenerator; geoGenerator.setRadius( radius ); geoGenerator.setCrossSectionVertexCount( 12 ); cvf::ref cvfCoords = new cvf::Vec3dArray( centerCoords ); geoGenerator.setPipeCenterCoords( cvfCoords.p() ); cvf::ref surfaceGeo = geoGenerator.createPipeSurface(); if ( surfaceGeo.notNull() ) { cvf::Part* part = new cvf::Part; part->setName( "RivPipeGeometryGenerator::surface" ); part->setDrawable( surfaceGeo.p() ); caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( color ), caf::PO_1 ); cvf::ref eff = surfaceGen.generateCachedEffect(); part->setEffect( eff.p() ); destinationParts->push_back( part ); } cvf::ref centerLineGeo = geoGenerator.createCenterLine(); if ( centerLineGeo.notNull() ) { cvf::Part* part = new cvf::Part; part->setName( "RivPipeGeometryGenerator::centerLineGeo" ); part->setDrawable( centerLineGeo.p() ); caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( color ), caf::PO_1 ); cvf::ref eff = surfaceGen.generateCachedEffect(); part->setEffect( eff.p() ); destinationParts->push_back( part ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivPipeGeometryGenerator::tubeWithCenterLinePartsAndVariableWidth( cvf::Collection* destinationParts, const std::vector& centerCoords, const std::vector& radii, const cvf::Color3f& color ) { cvf::ref activeCoords = new cvf::Vec3dArray( centerCoords ); const size_t crossSectionNodeCount = 12; cvf::ref surfaceGeo = generateVariableRadiusTube( crossSectionNodeCount, activeCoords.p(), radii ); if ( surfaceGeo.notNull() ) { cvf::Part* part = new cvf::Part; part->setName( "tubeWithCenterLinePartsAndVariableWidth::surface" ); part->setDrawable( surfaceGeo.p() ); caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( color ), caf::PO_1 ); cvf::ref eff = surfaceGen.generateCachedEffect(); part->setEffect( eff.p() ); destinationParts->push_back( part ); } cvf::ref centerLineGeo = generateLine( activeCoords.p() ); if ( centerLineGeo.notNull() ) { cvf::Part* part = new cvf::Part; part->setName( "tubeWithCenterLinePartsAndVariableWidth::centerLineGeo" ); part->setDrawable( centerLineGeo.p() ); caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( color ), caf::PO_1 ); cvf::ref eff = surfaceGen.generateCachedEffect(); part->setEffect( eff.p() ); destinationParts->push_back( part ); } }