/// <summary> /// For testing only now. /// </summary> /// <param name="existingEdge"></param> /// <param name="fromVert"></param> /// <param name="toVert"></param> /// <returns></returns> private HandleHalfEdge ReuseExistingConnection(HandleEdge existingEdge, HandleVertex fromVert, HandleVertex toVert) { // Check half-edge 1 and 2 if one points to the actual face. This is the one we use for our face then. If no one we build a new connection. HEdgePtrCont hedge1 = _LhedgePtrCont[_LedgePtrCont[existingEdge]._he1]; HEdgePtrCont hedge2 = _LhedgePtrCont[_LedgePtrCont[existingEdge]._he2]; HandleHalfEdge hedgeToUse = new HandleHalfEdge(-1); if (hedge2._f == -1) { // It is hedge 2 that is free. We should use it. hedgeToUse = _LedgePtrCont[existingEdge]._he2; } else if (hedge1._f == -1) { // It is hedge 1 that is free. We should use it. Should never happen. TODO: Exception throw? hedgeToUse = _LedgePtrCont[existingEdge]._he1; } else { // Neither one of the faces of the existing half-edges was free so we build a new edge. return CreateAllNewConnection(fromVert, toVert); } // Updating the face pointer. HEdgePtrCont hedge = _LhedgePtrCont[hedgeToUse]; hedge._f = new HandleFace(_LfacePtrCont.Count - 1); _LhedgePtrCont[hedgeToUse] = new HEdgePtrCont() { _f = hedge._f, _he = hedge._he, _nhe = hedge._nhe, _v = hedge._v, _vn = hedge._vn, _vuv = hedge._vuv }; return hedgeToUse; }
/// <summary> /// For testing now only. /// </summary> /// <returns></returns> private HandleHalfEdge CreateAllNewConnection(HandleVertex fromVert, HandleVertex toVert) { HEdgePtrCont hedge1 = new HEdgePtrCont() { _f = new HandleFace(_LfacePtrCont.Count - 1), _he = new HandleHalfEdge(_LedgePtrCont.Count == 0 ? 1 : _LhedgePtrCont.Count + 1), _v = new HandleVertex(toVert), _vn = new HandleVertexNormal(-1), _vuv = new HandleVertexUV(-1), _nhe = new HandleHalfEdge(-1) }; HEdgePtrCont hedge2 = new HEdgePtrCont() { _f = new HandleFace(-1), _he = new HandleHalfEdge(_LedgePtrCont.Count == 0 ? 0 : _LhedgePtrCont.Count), _v = new HandleVertex(fromVert), _vn = new HandleVertexNormal(-1), _vuv = new HandleVertexUV(-1), _nhe = new HandleHalfEdge(-1) }; _LhedgePtrCont.Add(hedge1); _LhedgePtrCont.Add(hedge2); _LedgePtrCont.Add( new EdgePtrCont() { _he1 = new HandleHalfEdge(_LhedgePtrCont.Count - 2), _he2 = new HandleHalfEdge(_LhedgePtrCont.Count - 1) } ); _LedgeHndl.Add( new HandleEdge() { _DataIndex = _LedgePtrCont.Count - 1 } ); // Update the vertices. VertexPtrCont vertFrom = _LvertexPtrCont[fromVert._DataIndex]; VertexPtrCont vertTo = _LvertexPtrCont[toVert._DataIndex]; if (!vertFrom._h.isValid) { vertFrom._h = _LedgePtrCont[_LedgePtrCont.Count - 1]._he1; _LvertexPtrCont[fromVert] = new VertexPtrCont() { _h = vertFrom._h }; } if (!vertTo._h.isValid) { vertTo._h = _LedgePtrCont[_LedgePtrCont.Count - 1]._he2; _LvertexPtrCont[toVert] = new VertexPtrCont() { _h = vertTo._h }; } return _LedgePtrCont.Last()._he1; }
/// <summary> /// Only for testing now. /// </summary> /// <param name="fromVert"></param> /// <param name="toVert"></param> /// <returns></returns> private HandleEdge DoesConnectionExist(HandleVertex fromVert, HandleVertex toVert) { return new HandleEdge( _LedgePtrCont.FindIndex( edgePtrCont => _LhedgePtrCont[edgePtrCont._he1]._v == fromVert && _LhedgePtrCont[edgePtrCont._he2]._v == toVert || _LhedgePtrCont[edgePtrCont._he1]._v._DataIndex == toVert && _LhedgePtrCont[edgePtrCont._he2]._v == fromVert) ); }
/// <summary> /// Iterator. /// Circulate around a given vertex and enumerate all incoming halfedges. /// </summary> /// <param name="vertexHandle">A handle to a vertex to use as a 'center' vertex.</param> /// <returns>An Enumerable of HalfEdge handles to be used in loops, etc.</returns> public IEnumerable<HandleHalfEdge> EnVertexIncomingHalfEdge(HandleVertex vertexHandle) { List<HandleHalfEdge> LTmpIncomingHedges = new List<HandleHalfEdge>(); //Get the one outgoing half-edge for the vertex. HandleHalfEdge currentHedge = _LvertexPtrCont[vertexHandle]._h; //Remember the index of the first half-edge int startHedgeIndex = currentHedge; do { if (currentHedge == -1) break; HEdgePtrCont currentHedgeContainer = _LhedgePtrCont[currentHedge]; if (vertexHandle == _LhedgePtrCont[currentHedgeContainer._he]._v) { LTmpIncomingHedges.Add(currentHedgeContainer._he); } currentHedge = _LhedgePtrCont[currentHedgeContainer._he]._nhe; } while (currentHedge != startHedgeIndex); return LTmpIncomingHedges.AsEnumerable(); //return _LhedgePtrCont.Where(e => e._v == vertexHandle).AsParallel().Select(e => _LhedgePtrCont[e._he._DataIndex]._he).AsParallel().ToList(); //return (from e in _LhedgePtrCont where e._v == vertexHandle select _LhedgePtrCont[e._he]._he).AsParallel().ToList(); }
/// <summary> /// Iterator. /// Circulate around a given vertex and enumerate all outgoing halfedges. /// </summary> /// <param name="vertexHandle">A handle to a vertex to use as a 'center' vertex.</param> /// <returns>An Enumerable of HalfEdge handles to be used in loops, etc.</returns> public IEnumerable<HandleHalfEdge> EnVertexOutgoingHalfEdge(HandleVertex vertexHandle) { return EnVertexIncomingHalfEdge(vertexHandle).Select(handleHalfEdge => _LhedgePtrCont[handleHalfEdge]._he).AsEnumerable(); }
/// <summary> /// Iterator. /// Circulate around a given vertex and enumerate all faces adjacent to the center vertex. /// </summary> /// <param name="hv">A handle to a vertex to use as a 'center' vertex.</param> /// <param name="vertexHandle">Handle to the vertex to do this operation on.</param> /// <returns>An Enumerable of HalfEdge handles to be used in loops, etc.</returns> public IEnumerable<HandleFace> EnVertexAdjacentFaces(HandleVertex vertexHandle) { return EnVertexIncomingHalfEdge(vertexHandle).Select(handleHalfEdge => _LhedgePtrCont[handleHalfEdge]._f).AsEnumerable(); }
/// <summary> /// Iterator. /// Circulate around a given vertex and enumerate all vertices connected by a direct edge. /// </summary> /// <param name="vertexHandle">A handle to a vertex to use as a 'center' vertex.</param> /// <returns>An Enumerable of VertexHandles to be used in loops, etc.</returns> public IEnumerable<HandleVertex> EnStarVertexVertex(HandleVertex vertexHandle) { return EnVertexIncomingHalfEdge(vertexHandle).Select(handleHalfEdge => _LhedgePtrCont[_LhedgePtrCont[handleHalfEdge]._he]._v).AsEnumerable(); }
/// <summary> /// Establishes a connection between two vertices. /// 1) Creates two half-edges /// 2) Fills them with information /// 3) Creates an edge pointer container and adds it to the geo container. /// 4) returns a handle to an edge /// </summary> /// <param name="fromVert">HandleVertex from which vertex</param> /// <param name="toVert">Handlevertex to which vertex</param> /// <returns>Returns a handle to the half-edge that has just been inserted</returns> public HandleHalfEdge CreateConnection(HandleVertex fromVert, HandleVertex toVert) { // Check if the connection does already exist. HandleEdge existingEdge = DoesConnectionExist(fromVert, toVert); if (existingEdge != -1) { return ReuseExistingConnection(existingEdge, fromVert, toVert); } else { return CreateAllNewConnection(fromVert, toVert); } }
/// <summary> /// This method calculates vertex normals for a specific vertex in the geometry and inserts them at the corresponding half-edges on the correct faces. /// This method uses an angle based algorithm to determine whether to calculate with another faces normal or not. /// </summary> /// <param name="vertexHandle">A handle for the vertex to calc the normals for.</param> public void CalcVertexNormal(HandleVertex vertexHandle) { List<HandleHalfEdge> EincomingHEdges = EnVertexIncomingHalfEdge(vertexHandle).ToList(); // Loop over every incoming half-edge. foreach (HandleHalfEdge handleHedge in EincomingHEdges) { int hedgeIndex = handleHedge; // Check if the half-edge is pointing to a face. int faceIndex = _LhedgePtrCont[hedgeIndex]._f; if (faceIndex == -1) return; float3 currentFaceNormal = _LfaceNormals[_LfacePtrCont[faceIndex]._fn]; float3 normalAggregate = new float3(); // Loop over every incoming half-edge again, so we can compare the angles between the current one and all the others. // We do this to decide which normal should be added to the sum and which not. foreach (int eincomingHEdge in EincomingHEdges) { // Add the current normal if the index is on it and do not compare any angles etc. if (eincomingHEdge == hedgeIndex) { normalAggregate += currentFaceNormal; continue; } // Stop when the current half-edge is not pointing to a face. int faceIndex2 = _LhedgePtrCont[eincomingHEdge]._f; if (faceIndex2 == -1) continue; float3 normalToCompare = _LfaceNormals[_LfacePtrCont[faceIndex2]._fn]; float dot = float3.Dot(currentFaceNormal, normalToCompare); if (System.Math.Acos(dot) * _constPiFactor < _SmoothingAngle) normalAggregate += float3.Add(normalAggregate, normalToCompare); } _LVertexNormals.Add(float3.NormalizeFast(normalAggregate)); HEdgePtrCont currentHedge = _LhedgePtrCont[hedgeIndex]; _LhedgePtrCont[hedgeIndex] = new HEdgePtrCont() { _f = currentHedge._f, _he = currentHedge._he, _nhe = currentHedge._nhe, _v = currentHedge._v, _vn = new HandleVertexNormal(_LVertexNormals.Count - 1), _vuv = currentHedge._vuv }; } }
/// <summary> /// Adds a vertex to the geometry container. /// Will return a handle to the newly inserted or still existing vertex. /// </summary> /// <param name="val">float3 value to insert</param> /// <returns>Returns a handle to the just inserted vertex or a handle to an existing one because the given one was already inserterd.</returns> public HandleVertex AddVertex(float3 val) { int index = DoesVertexExist(val); HandleVertex hvToAdd = new HandleVertex() { _DataIndex = -1 }; // When vertex does not exist - insert it if (index == -1) { _LvertexVal.Add(val); _LvertexPtrCont.Add( new VertexPtrCont() { _h = new HandleHalfEdge() { _DataIndex = -1 } } ); hvToAdd = new HandleVertex() { _DataIndex = _LvertexPtrCont.Count - 1 }; } else { HandleVertex vHndl = new HandleVertex(); vHndl._DataIndex = index; hvToAdd = vHndl; } if (!_LverticeHndl.Contains(hvToAdd)) { _LverticeHndl.Add(hvToAdd); } else { if (LFGMessages._DEBUGOUTPUT) { Debug.WriteLine("$$$ Vertex has been already inserted!"); } } return hvToAdd; }