示例#1
0
        /// <summary>
        /// Form a Delaunay triangulation by incrementally inserting vertices.
        /// </summary>
        /// <returns>Returns the number of edges on the convex hull of the
        /// triangulation.</returns>
        public IMesh Triangulate(IList <Vertex> points, Configuration config)
        {
            _TriangleNetMesh = new TriangleNetMesh(config);
            _TriangleNetMesh.TransferNodes(points);

            Otri starttri = new Otri();

            // Create a triangular bounding box.
            GetBoundingBox();

            foreach (var v in _TriangleNetMesh.vertices.Values)
            {
                starttri.tri = _TriangleNetMesh.dummytri;
                Osub tmp = default(Osub);
                if (_TriangleNetMesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate)
                {
                    if (Log.Verbose)
                    {
                        Log.Instance.Warning("A duplicate vertex appeared and was ignored.",
                                             "Incremental.Triangulate()");
                    }
                    v.type = VertexType.UndeadVertex;
                    _TriangleNetMesh.undeads++;
                }
            }

            // Remove the bounding box.
            _TriangleNetMesh.hullsize = RemoveBox();

            return(_TriangleNetMesh);
        }
示例#2
0
        /// <summary>
        /// Compute the Voronoi vertices (the circumcenters of the triangles).
        /// </summary>
        /// <returns>An empty map, which will map all vertices to a list of leaving edges.</returns>
        protected List <HalfEdge>[] ComputeVertices(TriangleNetMesh triangleNetMesh, Vertex[] vertices)
        {
            Otri   tri = default(Otri);
            float  xi = 0, eta = 0;
            Vertex vertex;
            Point  pt;
            int    id;

            // Maps all vertices to a list of leaving edges.
            var map = new List <HalfEdge> [triangleNetMesh.triangles.Count];

            // Compue triangle circumcenters
            foreach (var t in triangleNetMesh.triangles)
            {
                id      = t.id;
                tri.tri = t;

                pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);

                vertex    = factory.CreateVertex(pt.x, pt.y);
                vertex.id = id;

                vertices[id] = vertex;
                map[id]      = new List <HalfEdge>();
            }

            return(map);
        }
示例#3
0
        /// <summary>
        /// Gets the permutation vector for the Reverse Cuthill-McKee numbering.
        /// </summary>
        /// <param name="triangleNetMesh">The mesh.</param>
        /// <returns>Permutation vector.</returns>
        public int[] Renumber(TriangleNetMesh triangleNetMesh)
        {
            // Algorithm needs linear numbering of the nodes.
            triangleNetMesh.Renumber(NodeNumbering.Linear);

            return(Renumber(new AdjacencyMatrix(triangleNetMesh)));
        }
示例#4
0
 /// <summary>
 /// Number the vertices and write them to a .node file.
 /// </summary>
 /// <param name="triangleNetMesh"></param>
 /// <param name="filename"></param>
 public void WriteNodes(TriangleNetMesh triangleNetMesh, string filename)
 {
     using (var writer = new StreamWriter(filename))
     {
         WriteNodes(writer, triangleNetMesh);
     }
 }
示例#5
0
        /// <summary>
        /// Write the triangle neighbors to a .neigh file.
        /// </summary>
        /// <param name="triangleNetMesh"></param>
        /// <param name="filename"></param>
        /// <remarks>WARNING: Be sure WriteElements has been called before,
        /// so the elements are numbered right!</remarks>
        public void WriteNeighbors(TriangleNetMesh triangleNetMesh, string filename)
        {
            Otri tri = default(Otri), trisym = default(Otri);
            int  n1, n2, n3;
            int  i = 0;

            using (StreamWriter writer = new StreamWriter(filename))
            {
                // Number of triangles, three neighbors per triangle.
                writer.WriteLine("{0} 3", triangleNetMesh.triangles.Count);

                foreach (var item in triangleNetMesh.triangles)
                {
                    tri.tri = item;

                    tri.orient = 1;
                    tri.Sym(ref trisym);
                    n1 = trisym.tri.id;

                    tri.orient = 2;
                    tri.Sym(ref trisym);
                    n2 = trisym.tri.id;

                    tri.orient = 0;
                    tri.Sym(ref trisym);
                    n3 = trisym.tri.id;

                    // Triangle number, neighboring triangle numbers.
                    writer.WriteLine("{0} {1} {2} {3}", i++, n1, n2, n3);
                }
            }
        }
        public TriangleLocator(TriangleNetMesh triangleNetMesh, IPredicates predicates)
        {
            _TriangleNetMesh = triangleNetMesh;
            this.predicates  = predicates;

            sampler = new TriangleSampler(triangleNetMesh);
        }
示例#7
0
        /// <summary>
        /// Generate the Voronoi diagram from given triangle mesh..
        /// </summary>
        /// <param name="triangleNetMesh"></param>
        /// <param name="bounded"></param>
        protected void Generate(TriangleNetMesh triangleNetMesh)
        {
            triangleNetMesh.Renumber();

            base.edges = new List <HalfEdge>();
            this.rays  = new List <HalfEdge>();

            // Allocate space for Voronoi diagram.
            var vertices = new Vertex[triangleNetMesh.triangles.Count + triangleNetMesh.hullsize];
            var faces    = new Face[triangleNetMesh.vertices.Count];

            if (factory == null)
            {
                factory = new DefaultVoronoiFactory();
            }

            factory.Initialize(vertices.Length, 2 * triangleNetMesh.NumberOfEdges, faces.Length);

            // Compute triangles circumcenters.
            var map = ComputeVertices(triangleNetMesh, vertices);

            // Create all Voronoi faces.
            foreach (var vertex in triangleNetMesh.vertices.Values)
            {
                faces[vertex.id] = factory.CreateFace(vertex);
            }

            ComputeEdges(triangleNetMesh, vertices, faces, map);

            // At this point all edges are computed, but the (edge.next) pointers aren't set.
            ConnectEdges(map);

            base.vertices = new List <Vertex>(vertices);
            base.faces    = new List <Face>(faces);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class.
        /// </summary>
        /// <param name="triangleNetMesh">Mesh instance.</param>
        public BoundedVoronoiLegacy(TriangleNetMesh triangleNetMesh, bool includeBoundary)
        {
            _TriangleNetMesh     = triangleNetMesh;
            this.includeBoundary = includeBoundary;

            Generate();
        }
示例#9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="EdgeIterator" /> class.
        /// </summary>
        public EdgeIterator(TriangleNetMesh triangleNetMesh)
        {
            triangles = triangleNetMesh.triangles.GetEnumerator();
            triangles.MoveNext();

            tri.tri    = triangles.Current;
            tri.orient = 0;
        }
示例#10
0
        /// <summary>
        /// Form a Delaunay triangulation by the divide-and-conquer method.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Sorts the vertices, calls a recursive procedure to triangulate them, and
        /// removes the bounding box, setting boundary markers as appropriate.
        /// </remarks>
        public IMesh Triangulate(IList <Vertex> points, Configuration config)
        {
            this.predicates = config.Predicates();

            this._TriangleNetMesh = new TriangleNetMesh(config);
            this._TriangleNetMesh.TransferNodes(points);

            Otri hullleft = default(Otri), hullright = default(Otri);
            int  i, j, n = points.Count;

            // Allocate an array of pointers to vertices for sorting.
            this.sortarray = new Vertex[n];
            i = 0;
            foreach (var v in points)
            {
                sortarray[i++] = v;
            }

            // Sort the vertices.
            VertexSorter.Sort(sortarray);

            // Discard duplicate vertices, which can really mess up the algorithm.
            i = 0;
            for (j = 1; j < n; j++)
            {
                if ((sortarray[i].x == sortarray[j].x) && (sortarray[i].y == sortarray[j].y))
                {
                    if (Log.Verbose)
                    {
                        Log.Instance.Warning(
                            String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].id),
                            "Dwyer.Triangulate()");
                    }
                    sortarray[j].type = VertexType.UndeadVertex;
                    _TriangleNetMesh.undeads++;
                }
                else
                {
                    i++;
                    sortarray[i] = sortarray[j];
                }
            }
            i++;
            if (UseDwyer)
            {
                // Re-sort the array of vertices to accommodate alternating cuts.
                VertexSorter.Alternate(sortarray, i);
            }

            // Form the Delaunay triangulation.
            DivconqRecurse(0, i - 1, 0, ref hullleft, ref hullright);

            this._TriangleNetMesh.hullsize = RemoveGhosts(ref hullleft);

            return(this._TriangleNetMesh);
        }
示例#11
0
 public static void DrawGizmos(this TriangleNetMesh triangleNetMesh)
 {
     foreach (var triangle in triangleNetMesh.triangles)
     {
         var verts = triangle.vertices;
         Gizmos.DrawLine((Vector3)verts[0], (Vector3)verts[1]);
         Gizmos.DrawLine((Vector3)verts[1], (Vector3)verts[2]);
         Gizmos.DrawLine((Vector3)verts[2], (Vector3)verts[0]);
     }
 }
        public StandardVoronoi(TriangleNetMesh triangleNetMesh, Rectangle box, IVoronoiFactory factory, IPredicates predicates)
            : base(triangleNetMesh, factory, predicates, true)
        {
            // We assume the box to be at least as large as the mesh.
            box.Expand(triangleNetMesh.bounds);

            // We explicitly told the base constructor to call the Generate method, so
            // at this point the basic Voronoi diagram is already created.
            PostProcess(box);
        }
 public void Triangulate()
 {
     _Polygon = new Polygon();
     foreach (var vector2 in _Contour)
     {
         _Polygon.Add(vector2);
     }
     _TriangleNetMesh = (TriangleNetMesh)_Polygon.Triangulate();
     _Filter.mesh     = _TriangleNetMesh.GenerateUnityMesh();
 }
示例#14
0
        public void Update(TriangleNetMesh triangleNetMesh)
        {
            _TriangleNetMesh = triangleNetMesh;

            // Reset all measures.
            areaMeasure.Reset();
            alphaMeasure.Reset();
            qMeasure.Reset();

            Compute();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="TriangleQuadTree" /> class.
        /// </summary>
        /// <param name="triangleNetMesh">Mesh containing triangles.</param>
        /// <param name="maxDepth">The maximum depth of the tree.</param>
        /// <param name="sizeBound">The maximum number of triangles contained in a leaf.</param>
        /// <remarks>
        /// The quadtree does not track changes of the mesh. If a mesh is refined or
        /// changed in any other way, a new quadtree has to be built to make the point
        /// location work.
        ///
        /// A node of the tree will be split, if its level if less than the max depth parameter
        /// AND the number of triangles in the node is greater than the size bound.
        /// </remarks>
        public TriangleQuadTree(TriangleNetMesh triangleNetMesh, int maxDepth = 10, int sizeBound = 10)
        {
            this.maxDepth  = maxDepth;
            this.sizeBound = sizeBound;

            triangles = triangleNetMesh.Triangles.ToArray();

            int currentDepth = 0;

            root = new QuadNode(triangleNetMesh.Bounds, this, true);
            root.CreateSubRegion(++currentDepth);
        }
示例#16
0
        /// <summary>
        /// Initializes a new instance of the <see cref="VoronoiBase" /> class.
        /// </summary>
        /// <param name="triangleNetMesh">Triangle mesh.</param>
        /// <param name="factory">Voronoi object factory.</param>
        /// <param name="predicates">Geometric predicates implementation.</param>
        /// <param name="generate">If set to true, the constuctor will call the Generate
        /// method, which builds the Voronoi diagram.</param>
        protected VoronoiBase(TriangleNetMesh triangleNetMesh, IVoronoiFactory factory, IPredicates predicates,
                              bool generate)
            : base(false)
        {
            this.factory    = factory;
            this.predicates = predicates;

            if (generate)
            {
                Generate(triangleNetMesh);
            }
        }
示例#17
0
        public AdjacencyMatrix(TriangleNetMesh triangleNetMesh)
        {
            N = triangleNetMesh.vertices.Count;

            // Set up the adj_row adjacency pointer array.
            pcol = AdjacencyCount(triangleNetMesh);
            nnz  = pcol[N];

            // Set up the adj adjacency array.
            irow = AdjacencySet(triangleNetMesh, pcol);

            SortIndices();
        }
示例#18
0
        private void HashVertices(TriangleNetMesh triangleNetMesh)
        {
            if (vertices == null || triangleNetMesh.Vertices.Count != vertices.Length)
            {
                vertices = new int[triangleNetMesh.Vertices.Count];
            }

            int i = 0;

            foreach (var v in triangleNetMesh.Vertices)
            {
                vertices[i++] = v.id;
            }
        }
示例#19
0
        /// <summary>
        /// Number the vertices and write them to a .node file.
        /// </summary>
        private void WriteNodes(StreamWriter writer, TriangleNetMesh triangleNetMesh)
        {
            int outvertices = triangleNetMesh.vertices.Count;
            int nextras     = triangleNetMesh.nextras;

            Behavior behavior = triangleNetMesh.behavior;

            if (behavior.Jettison)
            {
                outvertices = triangleNetMesh.vertices.Count - triangleNetMesh.undeads;
            }

            if (writer != null)
            {
                // Number of vertices, number of dimensions, number of vertex attributes,
                // and number of boundary markers (zero or one).
                writer.WriteLine("{0} {1} {2} {3}", outvertices, triangleNetMesh.mesh_dim, nextras,
                                 behavior.UseBoundaryMarkers ? "1" : "0");

                if (triangleNetMesh.numbering == NodeNumbering.None)
                {
                    // If the mesh isn't numbered yet, use linear node numbering.
                    triangleNetMesh.Renumber();
                }

                if (triangleNetMesh.numbering == NodeNumbering.Linear)
                {
                    // If numbering is linear, just use the dictionary values.
                    WriteNodes(writer, triangleNetMesh.vertices.Values, behavior.UseBoundaryMarkers,
                               nextras, behavior.Jettison);
                }
                else
                {
                    // If numbering is not linear, a simple 'foreach' traversal of the dictionary
                    // values doesn't reflect the actual numbering. Use an array instead.

                    // TODO: Could use a custom sorting function on dictionary values instead.
                    Vertex[] nodes = new Vertex[triangleNetMesh.vertices.Count];

                    foreach (var node in triangleNetMesh.vertices.Values)
                    {
                        nodes[node.id] = node;
                    }

                    WriteNodes(writer, nodes, behavior.UseBoundaryMarkers,
                               nextras, behavior.Jettison);
                }
            }
        }
示例#20
0
        public BoundedVoronoi(TriangleNetMesh triangleNetMesh, IVoronoiFactory factory, IPredicates predicates)
            : base(triangleNetMesh, factory, predicates, true)
        {
            // We explicitly told the base constructor to call the Generate method, so
            // at this point the basic Voronoi diagram is already created.
            offset = vertices.Count;

            // Each vertex of the hull will be part of a Voronoi cell.
            vertices.Capacity = offset + triangleNetMesh.hullsize;

            // Create bounded Voronoi diagram.
            PostProcess();

            ResolveBoundaryEdges();
        }
示例#21
0
        public QualityMesher(TriangleNetMesh triangleNetMesh, Configuration config)
        {
            logger = Log.Instance;

            badsubsegs = new Queue <BadSubseg>();
            queue      = new BadTriQueue();

            this._TriangleNetMesh = triangleNetMesh;
            this.predicates       = config.Predicates();

            this.behavior = triangleNetMesh.behavior;

            newLocation = new NewLocation(triangleNetMesh, predicates);

            newvertex_tri = new Triangle();
        }
示例#22
0
        /// <summary>
        /// Reconstruct a triangulation from its raw data representation.
        /// </summary>
        public static TriangleNetMesh ToMesh(Polygon polygon, ITriangle[] triangles)
        {
            Otri tri    = default(Otri);
            Osub subseg = default(Osub);
            int  i      = 0;

            int elements = triangles == null ? 0 : triangles.Length;
            int segments = polygon.Segments.Count;

            // TODO: Configuration should be a function argument.
            var mesh = new TriangleNetMesh(new Configuration());

            mesh.TransferNodes(polygon.Points);

            mesh.regions.AddRange(polygon.Regions);
            mesh.behavior.useRegions = polygon.Regions.Count > 0;

            if (polygon.Segments.Count > 0)
            {
                mesh.behavior.Poly = true;
                mesh.holes.AddRange(polygon.Holes);
            }

            // Create the triangles.
            for (i = 0; i < elements; i++)
            {
                mesh.MakeTriangle(ref tri);
            }

            if (mesh.behavior.Poly)
            {
                mesh.insegments = segments;

                // Create the subsegments.
                for (i = 0; i < segments; i++)
                {
                    mesh.MakeSegment(ref subseg);
                }
            }

            var vertexarray = SetNeighbors(mesh, triangles);

            SetSegments(mesh, polygon, vertexarray);

            return(mesh);
        }
示例#23
0
        private void Step(TriangleNetMesh triangleNetMesh, IVoronoiFactory factory, IPredicates predicates)
        {
            var voronoi = new BoundedVoronoi(triangleNetMesh, factory, predicates);

            float x, y;

            foreach (var face in voronoi.Faces)
            {
                if (face.generator.label == 0)
                {
                    Centroid(face, out x, out y);

                    face.generator.x = x;
                    face.generator.y = y;
                }
            }
        }
示例#24
0
        private bool VerticesChanged(TriangleNetMesh triangleNetMesh)
        {
            if (vertices == null || triangleNetMesh.Vertices.Count != vertices.Length)
            {
                return(true);
            }

            int i = 0;

            foreach (var v in triangleNetMesh.Vertices)
            {
                if (v.id != vertices[i++])
                {
                    return(true);
                }
            }

            return(false);
        }
示例#25
0
        /// <summary>
        /// Rebuild the input geometry.
        /// </summary>
        private Polygon Rebuild(TriangleNetMesh triangleNetMesh)
        {
            var data = new Polygon(triangleNetMesh.vertices.Count);

            foreach (var v in triangleNetMesh.vertices.Values)
            {
                // Reset to input vertex.
                v.type = VertexType.InputVertex;

                data.Points.Add(v);
            }

            data.Segments.AddRange(triangleNetMesh.subsegs.Values.Cast <ISegment>());

            data.Holes.AddRange(triangleNetMesh.holes);
            data.Regions.AddRange(triangleNetMesh.regions);

            return(data);
        }
示例#26
0
        /// <summary>
        /// Write the triangles to an .ele file.
        /// </summary>
        /// <param name="triangleNetMesh"></param>
        /// <param name="filename"></param>
        public void WriteElements(TriangleNetMesh triangleNetMesh, string filename)
        {
            Otri   tri = default(Otri);
            Vertex p1, p2, p3;
            bool   regions = triangleNetMesh.behavior.useRegions;

            int j = 0;

            tri.orient = 0;

            using (var writer = new StreamWriter(filename))
            {
                // Number of triangles, vertices per triangle, attributes per triangle.
                writer.WriteLine("{0} 3 {1}", triangleNetMesh.triangles.Count, regions ? 1 : 0);

                foreach (var item in triangleNetMesh.triangles)
                {
                    tri.tri = item;

                    p1 = tri.Org();
                    p2 = tri.Dest();
                    p3 = tri.Apex();

                    // Triangle number, indices for three vertices.
                    writer.Write("{0} {1} {2} {3}", j, p1.id, p2.id, p3.id);

                    if (regions)
                    {
                        writer.Write(" {0}", tri.tri.label);
                    }

                    writer.WriteLine();

                    // Number elements
                    item.id = j++;
                }
            }
        }
示例#27
0
        public static Mesh GenerateUnityMesh(this TriangleNetMesh triangleNetMesh, QualityOptions options = null)
        {
            if (options != null)
            {
                triangleNetMesh.Refine(options);
            }

            Mesh mesh             = new Mesh();
            var  triangleNetVerts = triangleNetMesh.Vertices.ToList();

            var triangles = triangleNetMesh.Triangles;

            Vector3[] verts     = new Vector3[triangleNetVerts.Count];
            int[]     trisIndex = new int[triangles.Count * 3];

            for (int i = 0; i < verts.Length; i++)
            {
                verts[i] = (Vector3)triangleNetVerts[i];
            }

            int k = 0;

            foreach (var triangle in triangles)
            {
                for (int i = 2; i >= 0; i--)
                {
                    trisIndex[k] = triangleNetVerts.IndexOf(triangle.GetVertex(i));
                    k++;
                }
            }

            mesh.vertices  = verts;
            mesh.triangles = trisIndex;

            mesh.RecalculateBounds();
            mesh.RecalculateNormals();
            return(mesh);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class.
 /// </summary>
 /// <param name="triangleNetMesh">Mesh instance.</param>
 public BoundedVoronoiLegacy(TriangleNetMesh triangleNetMesh)
     : this(triangleNetMesh, true)
 {
 }
示例#29
0
        /// <summary>
        /// Sets adjacencies in a triangulation.
        /// </summary>
        /// <remarks>
        /// This routine can be used to create the compressed column storage
        /// for a linear triangle finite element discretization of Poisson's
        /// equation in two dimensions.
        /// </remarks>
        int[] AdjacencySet(TriangleNetMesh triangleNetMesh, int[] pcol)
        {
            int n = N;

            int[] col = new int[n];

            // Copy of the adjacency rows input.
            Array.Copy(pcol, col, n);

            int i, nnz = pcol[n];

            // Output list, stores the actual adjacency information.
            int[] list = new int[nnz];

            // Set every node to be adjacent to itself.
            for (i = 0; i < n; i++)
            {
                list[col[i]] = i;
                col[i]      += 1;
            }

            int n1, n2, n3; // Vertex numbers.
            int tid, nid;   // Triangle and neighbor id.

            // Examine each triangle.
            foreach (var tri in triangleNetMesh.triangles)
            {
                tid = tri.id;

                n1 = tri.vertices[0].id;
                n2 = tri.vertices[1].id;
                n3 = tri.vertices[2].id;

                // Add edge (1,2) if this is the first occurrence, that is, if
                // the edge (1,2) is on a boundary (nid <= 0) or if this triangle
                // is the first of the pair in which the edge occurs (tid < nid).
                nid = tri.neighbors[2].tri.id;

                if (nid < 0 || tid < nid)
                {
                    list[col[n1]++] = n2;
                    list[col[n2]++] = n1;
                }

                // Add edge (2,3).
                nid = tri.neighbors[0].tri.id;

                if (nid < 0 || tid < nid)
                {
                    list[col[n2]++] = n3;
                    list[col[n3]++] = n2;
                }

                // Add edge (3,1).
                nid = tri.neighbors[1].tri.id;

                if (nid < 0 || tid < nid)
                {
                    list[col[n1]++] = n3;
                    list[col[n3]++] = n1;
                }
            }

            return(list);
        }
示例#30
0
        /// <summary>
        /// Counts adjacencies in a triangulation.
        /// </summary>
        /// <remarks>
        /// This routine is called to count the adjacencies, so that the
        /// appropriate amount of memory can be set aside for storage when
        /// the adjacency structure is created.
        ///
        /// The triangulation is assumed to involve 3-node triangles.
        ///
        /// Two nodes are "adjacent" if they are both nodes in some triangle.
        /// Also, a node is considered to be adjacent to itself.
        /// </remarks>
        int[] AdjacencyCount(TriangleNetMesh triangleNetMesh)
        {
            int n = N;
            int n1, n2, n3;
            int tid, nid;

            int[] pcol = new int[n + 1];

            // Set every node to be adjacent to itself.
            for (int i = 0; i < n; i++)
            {
                pcol[i] = 1;
            }

            // Examine each triangle.
            foreach (var tri in triangleNetMesh.triangles)
            {
                tid = tri.id;

                n1 = tri.vertices[0].id;
                n2 = tri.vertices[1].id;
                n3 = tri.vertices[2].id;

                // Add edge (1,2) if this is the first occurrence, that is, if
                // the edge (1,2) is on a boundary (nid <= 0) or if this triangle
                // is the first of the pair in which the edge occurs (tid < nid).
                nid = tri.neighbors[2].tri.id;

                if (nid < 0 || tid < nid)
                {
                    pcol[n1] += 1;
                    pcol[n2] += 1;
                }

                // Add edge (2,3).
                nid = tri.neighbors[0].tri.id;

                if (nid < 0 || tid < nid)
                {
                    pcol[n2] += 1;
                    pcol[n3] += 1;
                }

                // Add edge (3,1).
                nid = tri.neighbors[1].tri.id;

                if (nid < 0 || tid < nid)
                {
                    pcol[n3] += 1;
                    pcol[n1] += 1;
                }
            }

            // We used PCOL to count the number of entries in each column.
            // Convert it to pointers into the ADJ array.
            for (int i = n; i > 0; i--)
            {
                pcol[i] = pcol[i - 1];
            }

            pcol[0] = 0;
            for (int i = 1; i <= n; i++)
            {
                pcol[i] = pcol[i - 1] + pcol[i];
            }

            return(pcol);
        }