void SVLocusSet:: getIntersectingEdgeNodes( const LocusIndexType inputLocusIndex, const NodeIndexType inputRemoteNodeIndex, const EdgeMapType& remoteIntersectNodeToLocalNodeMap, const LocusSetIndexerType& remoteIntersectNodes, std::vector<EdgeInfoType>& edges) const { typedef EdgeMapType::const_iterator rliter_t; typedef std::pair<rliter_t,rliter_t> rlmap_range_t; edges.clear(); // find all nodes, from the remoteIntersectNodes set, which intersect this function's input node: // // for this application, inputLocus is an input set isolated from the rest of the graph, so nodes // intersected in the inputLocus are filtered out // std::set<NodeAddressType> edgeIntersectRemoteTemp; getNodeIntersectCore(inputLocusIndex,inputRemoteNodeIndex,remoteIntersectNodes,inputLocusIndex,edgeIntersectRemoteTemp); for (const NodeAddressType& remoteIsectAddy : edgeIntersectRemoteTemp) { // find what local nodes the remote nodes trace back to: const rlmap_range_t remoteIsectRange(remoteIntersectNodeToLocalNodeMap.equal_range(remoteIsectAddy)); assert(remoteIsectRange.first != remoteIntersectNodeToLocalNodeMap.end()); for (rliter_t riter(remoteIsectRange.first); riter != remoteIsectRange.second; ++riter) { const NodeAddressType localIntersectAddy(std::make_pair(remoteIsectAddy.first,riter->second)); edges.push_back(std::make_pair(localIntersectAddy,remoteIsectAddy.second)); } } }
void SVLocusSet:: getNodeMergeableIntersect( const LocusIndexType inputLocusIndex, const NodeIndexType inputNodeIndex, const bool isInputLocusMoved, std::set<NodeAddressType>& mergeIntersectNodes) const { // // TODO: There's room for significant optimization of these methods. The improvements are not trivial, // but they would allow us to filter fewer nodes from being merged when node intersection counts become large. // // // There are two ways sets of mergeable nodes can occur: // // (1) There is a set of nodes which overlap with both input node and one // of the remote nodes that the input points to (ie they have a shared edge). // When totaled together, the edge count of this set + the inputNode edge // exceeds minMergeEdgeCount. // // (2) The input node either contains an edge which is greater than minMergeEdgeCount // or will contain such an edge due to (1), in this case the input node can be merged // with a locally overlapping node which also contains an edge which is greater than // minMergeEdgeCount. Note that in case (2) remote node intersection is not required. // const NodeAddressType inputAddy(std::make_pair(inputLocusIndex,inputNodeIndex)); const SVLocusNode& inputNode(getNode(inputAddy)); #ifdef DEBUG_SVL static const std::string logtag("SVLocusSet::getNodeMergableIntersect"); log_os << logtag << " inputNode: " << inputAddy << " " << inputNode; checkState(); #endif // reuse this intersectNodes as a temporary throughout the methods below std::set<NodeAddressType> intersectNodes; // // build a new index, which contains, for all nodes x which intersect the input, an // enumeration of the remote nodes Y connected by edges to node x (remoteIntersectNodes) // and a map for each node y \in Y pointing back to node x (remoteIntersectNodeToLocalNodeMap) // LocusSetIndexerType remoteIntersectNodes(*this); EdgeMapType remoteIntersectNodeToLocalNodeMap; // nodes which intersect the input and have already been certified as signal: std::set<NodeAddressType> signalIntersectNodes; { // get a standard intersection of the input node: getNodeIntersect(inputLocusIndex, inputNodeIndex, intersectNodes); // // 1. build the new remoteIntersectNodes/remoteIntersectNodeToLocalNodeMap index // for (const NodeAddressType& intersectAddy : intersectNodes) { const SVLocusNode& intersectNode(getNode(intersectAddy)); // get the remotes of each node which intersect with the query node, // place these in remoteIntersectNodes const SVLocusEdgeManager edgeMap(intersectNode.getEdgeManager()); for (const SVLocusEdgesType::value_type& intersectEdge : edgeMap.getMap()) { // build remote <-> local indexing structures: NodeAddressType remoteAddy(std::make_pair(intersectAddy.first,intersectEdge.first)); remoteIntersectNodes.data().insert(remoteAddy); remoteIntersectNodeToLocalNodeMap.insert(std::make_pair(remoteAddy,intersectAddy.second)); } } #ifdef DEBUG_SVL log_os << logtag << " remoteIntersectNodes.size(): " << remoteIntersectNodes.data().size() << "\n"; for (const NodeAddressType& addy : remoteIntersectNodes.data()) { log_os << logtag << "\tremoteIntersectNode: " << addy << " " << getNode(addy); } #endif // // 2. get the signal node set: // // Note that the signal node search is not transitive b/c we have required all signal nodes // in the graph to have merged already. // for (const NodeAddressType& intersectAddy : intersectNodes) { if (! isNoiseNode(intersectAddy)) { signalIntersectNodes.insert(intersectAddy); } } #ifdef DEBUG_SVL log_os << logtag << " signalIntersect.size(): " << signalIntersectNodes.size() << "\n"; for (const NodeAddressType& addy : signalIntersectNodes) { log_os << logtag << "\tsignalIntersectNode: " << addy << " " << getNode(addy); } #endif } // // begin building the primary function output, mergeIntersectNodes, by enumerating all edges of the input node // mergeIntersectNodes.clear(); // loop through each edge connected to the input node const SVLocusEdgeManager edgeMap(inputNode.getEdgeManager()); for (const SVLocusEdgesType::value_type& inputEdge : edgeMap.getMap()) { #ifdef DEBUG_SVL log_os << logtag << " processing edge: " << inputAddy << "->" << inputLocusIndex << ":" << inputEdge.first << "\n"; checkState(); #endif // // for each edge from the input node, get all intersecting edges // // 'intersecting edge' means that the nodes connected by the two edges each overlap // std::vector<EdgeInfoType> inputIntersectEdges; getIntersectingEdgeNodes(inputLocusIndex, inputEdge.first, remoteIntersectNodeToLocalNodeMap, remoteIntersectNodes, inputIntersectEdges); unsigned intersectCount(inputIntersectEdges.size()); if (! isInputLocusMoved) { /// TODO: doc this adjustment, does this normalize the edge count to always include self-intersect? intersectCount++; } // isRegionCheck initiates a more detailed evidence signal threshold check process // // - The default process checks the total evidence summed over the entire // Node intersect set. This neglects to account for the possibility that that evidence // density could be low, and yet a high evidence sum could be achieved by transitive over // lap of many nodes. // // - The regioncheck pathway sums up evidence at each genomic region. It more accurately // reflects peak evidence but is somewhat slower to compute. // // Example: // // Assume each node below has an evidence count of 1. // // |---node1-----| // |-----node2-----| // |-----node3---| // // Default evidence count: // 33333333333333333333333333333333333333 // // isRegionCheck evidence count: // 11111111112222211111111222211111111111 // // // peak RegionCheck count will always equal default count when 2 or fewer nodes exist, // so there's no reason to turn it on until we have more nodes const bool isRegionCheck(intersectCount>2); if (isRegionCheck) { _mergeRegions.clear(); } // enumerate counts as part of the (non-RegionCheck) process to determine if the intersection set // contains sufficient evidence to initiate a merge unsigned mergedLocalEdgeCount(0); unsigned mergedRemoteEdgeCount(0); /// /// enumerate node evidence using either the default or RegionCheck process: /// auto addEdgeEvidenceCount = [&]( const SVLocus& edgeLocus, const NodeIndexType localNodeIndex, const NodeIndexType remoteNodeIndex) { // total edge counts on the remote->local edge: const unsigned remoteEdgeCount = edgeLocus.getEdge(remoteNodeIndex,localNodeIndex).getCount(); // total edge counts on the local->remote edge: const unsigned localEdgeCount = edgeLocus.getEdge(localNodeIndex,remoteNodeIndex).getCount(); if (isRegionCheck) { const known_pos_range2& localRange(edgeLocus.getNode(localNodeIndex).getInterval().range); const known_pos_range2& remoteRange(edgeLocus.getNode(remoteNodeIndex).getInterval().range); _mergeRegions.localNodeOutbound.add(localRange,localEdgeCount); _mergeRegions.localNodeInbound.add(localRange,remoteEdgeCount); _mergeRegions.remoteNodeOutbound.add(remoteRange,remoteEdgeCount); _mergeRegions.remoteNodeInbound.add(remoteRange,localEdgeCount); } else { mergedLocalEdgeCount += localEdgeCount; mergedRemoteEdgeCount += remoteEdgeCount; } }; for (const EdgeInfoType& edgeInfo : inputIntersectEdges) { addEdgeEvidenceCount(getLocus(edgeInfo.first.first),edgeInfo.first.second,edgeInfo.second); } // if the input hasn't been moved into the primary locus graph yet, then we need to include the inputLocus // in order to get an accurate edge intersection count: if (! isInputLocusMoved) { addEdgeEvidenceCount(getLocus(inputAddy.first),inputNodeIndex,inputEdge.first); } if (isRegionCheck) { mergedLocalEdgeCount=(std::min(_mergeRegions.localNodeOutbound.maxVal(),_mergeRegions.remoteNodeInbound.maxVal())); mergedRemoteEdgeCount=(std::min(_mergeRegions.localNodeInbound.maxVal(),_mergeRegions.remoteNodeOutbound.maxVal())); } #ifdef DEBUG_SVL log_os << logtag << " isRegionCheck: " << isRegionCheck << "\n"; log_os << logtag << " final merge counts" << " local: " << mergedLocalEdgeCount << " remote: " << mergedRemoteEdgeCount << "\n"; checkState(); #endif if ((mergedLocalEdgeCount < getMinMergeEdgeCount()) && (mergedRemoteEdgeCount < getMinMergeEdgeCount())) continue; // // Add type1 mergeable nodes: // for (const EdgeInfoType& edgeInfo : inputIntersectEdges) { mergeIntersectNodes.insert(edgeInfo.first); } /// for each type1 node, add any new intersections to the signal node set: /// /// this is not very efficient for now -- each type1 edge added in potentially /// expands the current node to intersect new signal nodes /// -- this loop looks for those new signal nodes /// { // this is used to search for the (rare) case where the intersection set // locals overlap with the intersection set remotes std::set<NodeAddressType> inputIntersectRemotes; for (const EdgeInfoType& edgeInfo : inputIntersectEdges) { inputIntersectRemotes.insert(std::make_pair(edgeInfo.first.first,edgeInfo.second)); } bool isIntersectRemotes(false); // check both the original node and intersected nodes for intersection to // any of the group's remotes, and for new type2 signal intersect: findSignalNodes(inputLocusIndex, inputAddy, signalIntersectNodes, inputIntersectRemotes, isIntersectRemotes); for (const EdgeInfoType& edgeInfo : inputIntersectEdges) { findSignalNodes(inputLocusIndex, edgeInfo.first, signalIntersectNodes, inputIntersectRemotes, isIntersectRemotes); } if (isIntersectRemotes) { for (const NodeAddressType& intersectAddy : inputIntersectRemotes) { #ifdef DEBUG_SVL log_os << logtag << " adding ownRemote: " << intersectAddy << "\n"; #endif mergeIntersectNodes.insert(intersectAddy); // check to see if this adds even more signal nodes! findSignalNodes(inputLocusIndex, intersectAddy, signalIntersectNodes, inputIntersectRemotes, isIntersectRemotes); } } } // // Add type2 mergeable nodes: // for (const NodeAddressType& signalAddy : signalIntersectNodes) { mergeIntersectNodes.insert(signalAddy); } } #ifdef DEBUG_SVL log_os << logtag << " END. IntersectNodeSize: " << mergeIntersectNodes.size() << " Nodes:\n"; for (const NodeAddressType addy : mergeIntersectNodes) { log_os << logtag << "\tInode: " << addy << "\n"; } #endif }
static bool buildGeodesicSphereData( const float radius, const unsigned int subdivisions, osg::Geometry* geom ) { unsigned int subdivide( subdivisions ); if( subdivisions > 5 ) { // Would create index array too large for use with DrawElementsUShort. // For now, clamp. In the future, just use DrawElementsUInt. osg::notify( osg::WARN ) << "makeGeodesicSphere: Clamping subdivisions to 5." << std::endl; subdivide = 5; } GLfloat vertData[] = { 0.000000, 0.850651, 0.525731, 0.000000, 0.850651, -0.525731, 0.000000, -0.850651, -0.525731, 0.000000, -0.850651, 0.525731, 0.525731, 0.000000, 0.850651, 0.525731, 0.000000, -0.850651, -0.525731, 0.000000, -0.850651, -0.525731, 0.000000, 0.850651, 0.850651, 0.525731, 0.000000, 0.850651, -0.525731, 0.000000, -0.850651, -0.525731, 0.000000, -0.850651, 0.525731, 0.000000 }; int faces = 20; int _numVerts = 12; int _numIndices = faces * 3; // Data is initially in "golden mean" coordinate system. // Rotate around y so that 2 verts exist at (0,0,+/-1). { //osg::Vec3 v0( 0.525731, 0.000000, 0.850651 ); //osg::Vec3 v1( 0, 0, 1 ); //const double angle( acos( v0 * v1 ) ); const double sinAngle( 0.525731 ); const double cosAngle( 0.850651 ); int idx; for( idx=0; idx<_numVerts*3; idx+=3 ) { double x( vertData[ idx ] ); double z( vertData[ idx+2 ] ); vertData[ idx ] = x * cosAngle + z * -sinAngle; vertData[ idx+2 ] = x * sinAngle + z * cosAngle; } } int vertsSize = _numVerts * 3; GLfloat* _vertices = new GLfloat[ vertsSize ]; memcpy( _vertices, vertData, sizeof( vertData ) ); GLushort* _indices = new GLushort[ _numIndices ]; GLushort* indexPtr = _indices; *indexPtr++ = 0; *indexPtr++ = 7; *indexPtr++ = 4; *indexPtr++ = 0; *indexPtr++ = 4; *indexPtr++ = 8; *indexPtr++ = 0; *indexPtr++ = 8; *indexPtr++ = 1; *indexPtr++ = 0; *indexPtr++ = 1; *indexPtr++ = 11; *indexPtr++ = 0; *indexPtr++ = 11; *indexPtr++ = 7; *indexPtr++ = 2; *indexPtr++ = 6; *indexPtr++ = 5; *indexPtr++ = 2; *indexPtr++ = 5; *indexPtr++ = 9; *indexPtr++ = 2; *indexPtr++ = 9; *indexPtr++ = 3; *indexPtr++ = 2; *indexPtr++ = 3; *indexPtr++ = 10; *indexPtr++ = 2; *indexPtr++ = 10; *indexPtr++ = 6; *indexPtr++ = 7; *indexPtr++ = 3; *indexPtr++ = 4; *indexPtr++ = 4; *indexPtr++ = 3; *indexPtr++ = 9; *indexPtr++ = 4; *indexPtr++ = 9; *indexPtr++ = 8; *indexPtr++ = 8; *indexPtr++ = 9; *indexPtr++ = 5; *indexPtr++ = 8; *indexPtr++ = 5; *indexPtr++ = 1; *indexPtr++ = 1; *indexPtr++ = 5; *indexPtr++ = 6; *indexPtr++ = 1; *indexPtr++ = 6; *indexPtr++ = 11; *indexPtr++ = 11; *indexPtr++ = 6; *indexPtr++ = 10; *indexPtr++ = 11; *indexPtr++ = 10; *indexPtr++ = 7; *indexPtr++ = 7; *indexPtr++ = 10; *indexPtr++ = 3; GLuint _idxStart = 0; GLuint _idxEnd = 11; // Subdivide as requested int idx; for (idx = subdivide; idx; idx--) { // Make a map of edges typedef std::map< unsigned int, GLushort> EdgeMapType; EdgeMapType edgeMap; indexPtr = _indices; int f; for (f=faces; f; f--) { unsigned int key = makeKey(indexPtr[0], indexPtr[1]); if (edgeMap.find( key ) == edgeMap.end()) edgeMap[key] = ++_idxEnd; key = makeKey(indexPtr[1], indexPtr[2]); if (edgeMap.find( key ) == edgeMap.end()) edgeMap[key] = ++_idxEnd; key = makeKey(indexPtr[2], indexPtr[0]); if (edgeMap.find( key ) == edgeMap.end()) edgeMap[key] = ++_idxEnd; indexPtr += 3; } GLfloat* oldVerts = _vertices; GLushort* oldIndices = _indices; _numVerts += (int)(faces * 1.5f); int newFaces = faces * 4; _numIndices = newFaces * 3; // Create new indices _indices = new GLushort[ _numIndices ]; GLushort* oldIdxPtr = oldIndices; indexPtr = _indices; for (f=faces; f; f--) { GLushort vertA = *oldIdxPtr++; GLushort vertB = *oldIdxPtr++; GLushort vertC = *oldIdxPtr++; GLushort edgeAB = edgeMap[ makeKey(vertA,vertB) ]; GLushort edgeBC = edgeMap[ makeKey(vertB,vertC) ]; GLushort edgeCA = edgeMap[ makeKey(vertC,vertA) ]; *indexPtr++ = vertA; *indexPtr++ = edgeAB; *indexPtr++ = edgeCA; *indexPtr++ = edgeAB; *indexPtr++ = vertB; *indexPtr++ = edgeBC; *indexPtr++ = edgeAB; *indexPtr++ = edgeBC; *indexPtr++ = edgeCA; *indexPtr++ = edgeCA; *indexPtr++ = edgeBC; *indexPtr++ = vertC; } // Copy old vertices into new vertices _vertices = new GLfloat[ _numVerts * 3 ]; memcpy( _vertices, oldVerts, vertsSize * sizeof( GLfloat ) ); // Create new vertices at midpoint of each edge EdgeMapType::const_iterator it = edgeMap.begin(); while (it != edgeMap.end()) { GLushort idxA, idxB; idxA = ((*it).first) >> 16; idxB = ((*it).first) & 0xffff; GLfloat* dest = &(_vertices[ ((*it).second * 3) ]); GLfloat* srcA = &(_vertices[idxA*3]); GLfloat* srcB = &(_vertices[idxB*3]); average3fv( dest, srcA, srcB ); it++; } faces = newFaces; vertsSize = _numVerts * 3; delete[] oldVerts; delete[] oldIndices; } // // Create normal array by making vertices unit length GLfloat* _normals = new GLfloat[ _numVerts * 3 ]; GLfloat* vertPtr = _vertices; GLfloat* normPtr = _normals; for (idx = _numVerts; idx; idx--) { osg::Vec3 v( vertPtr[0], vertPtr[1], vertPtr[2] ); float lengthInv = (float)( 1. / v.length() ); *normPtr++ = *vertPtr++ * lengthInv; *normPtr++ = *vertPtr++ * lengthInv; *normPtr++ = *vertPtr++ * lengthInv; } // // Scale vertices out to the specified radius vertPtr = _vertices; normPtr = _normals; for (idx = _numVerts*3; idx; idx--) *vertPtr++ = *normPtr++ * radius; // // Texture coordinates are identical to normals for cube mapping GLfloat* _texCoords = new GLfloat[ _numVerts * 3 ]; memcpy( _texCoords, _normals, _numVerts * 3 * sizeof( GLfloat) ); // Convert to OSG { osg::Vec3Array* osgV = new osg::Vec3Array; osg::Vec3Array* osgN = new osg::Vec3Array; osg::Vec3Array* osgTC = new osg::Vec3Array; osgV->resize( _numVerts ); osgN->resize( _numVerts ); osgTC->resize( _numVerts ); geom->setVertexArray( osgV ); geom->setNormalArray( osgN ); geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); geom->setTexCoordArray( 0, osgTC ); osg::Vec4Array* osgC = new osg::Vec4Array; osgC->push_back( osg::Vec4( 1., 1., 1., 1. ) ); geom->setColorArray( osgC ); geom->setColorBinding( osg::Geometry::BIND_OVERALL ); vertPtr = _vertices; normPtr = _normals; GLfloat* tcPtr = _texCoords; int idx; for( idx=0; idx<_numVerts; idx++ ) { (*osgV)[ idx ].x() = *vertPtr++; (*osgV)[ idx ].y() = *vertPtr++; (*osgV)[ idx ].z() = *vertPtr++; (*osgN)[ idx ].x() = *normPtr++; (*osgN)[ idx ].y() = *normPtr++; (*osgN)[ idx ].z() = *normPtr++; (*osgTC)[ idx ].x() = *tcPtr++; (*osgTC)[ idx ].y() = *tcPtr++; (*osgTC)[ idx ].z() = *tcPtr++; } osg::UShortArray* osgIdx = new osg::UShortArray; osgIdx->resize( _numIndices ); indexPtr = _indices; for( idx=0; idx<_numIndices; idx++ ) { (*osgIdx)[ idx ] = *indexPtr++; } geom->addPrimitiveSet( new osg::DrawElementsUShort( GL_TRIANGLES, _numIndices, _indices ) ); } delete[] _indices; delete[] _vertices; delete[] _normals; delete[] _texCoords; osg::notify( osg::INFO ) << "makeGeodesicSphere: numVertices: " << _numVerts << std::endl; return( true ); }