C# Class TriangleNet.Mesh

Mesh data structure.
Mostrar archivo Open project: RegrowthStudios/VoxelRTS Class Usage Examples

Public Methods

Method Description
Check ( bool &isConsistent, bool &isDelaunay ) : void

Check mesh consistency and (constrained) Delaunay property.

Load ( InputGeometry input, List triangles ) : void

Reconstructs a mesh from raw input data.

Load ( string filename ) : void

Load a mesh from file (.node/poly and .ele).

Mesh ( ) : System

Initializes a new instance of the Mesh class.

Mesh ( Behavior behavior ) : System

Initializes a new instance of the Mesh class.

Refine ( ) : void

Refines the current mesh.

Refine ( bool halfArea ) : void

Refines the current mesh by finding the maximum triangle area and setting the a global area constraint to half its size.

Refine ( double areaConstraint ) : void

Refines the current mesh by setting a global area constraint.

Renumber ( ) : void

Renumber vertex and triangle id's.

Renumber ( NodeNumbering num ) : void

Renumber vertex and triangle id's.

Smooth ( ) : void

Smooth the current mesh.

Triangulate ( InputGeometry input ) : void

Triangulate given input data.

Triangulate ( string inputFile ) : void

Triangulate given input file (.node or .poly).

Private Methods

Method Description
ConstrainedEdge ( TriangleNet.Data.Otri &starttri, Vertex endpoint2, int newmark ) : void

Force a segment into a constrained Delaunay triangulation by deleting the triangles it intersects, and triangulating the polygons that form on each side of it.

Generates a single subsegment connecting 'endpoint1' to 'endpoint2'. The triangle 'starttri' has 'endpoint1' as its origin. 'newmark' is the boundary marker of the segment. To insert a segment, every triangle whose interior intersects the segment is deleted. The union of these deleted triangles is a polygon (which is not necessarily monotone, but is close enough), which is divided into two polygons by the new segment. This routine's task is to generate the Delaunay triangulation of these two polygons. You might think of this routine's behavior as a two-step process. The first step is to walk from endpoint1 to endpoint2, flipping each edge encountered. This step creates a fan of edges connected to endpoint1, including the desired edge to endpoint2. The second step enforces the Delaunay condition on each side of the segment in an incremental manner: proceeding along the polygon from endpoint1 to endpoint2 (this is done independently on each side of the segment), each vertex is "enforced" as if it had just been inserted, but affecting only the previous vertices. The result is the same as if the vertices had been inserted in the order they appear on the polygon, so the result is Delaunay. In truth, ConstrainedEdge() interleaves these two steps. The procedure walks from endpoint1 to endpoint2, and each time an edge is encountered and flipped, the newly exposed vertex (at the far end of the flipped edge) is "enforced" upon the previously flipped edges, usually affecting only one side of the polygon (depending upon which side of the segment the vertex falls on). The algorithm is complicated by the need to handle polygons that are not convex. Although the polygon is not necessarily monotone, it can be triangulated in a manner similar to the stack-based algorithms for monotone polygons. For each reflex vertex (local concavity) of the polygon, there will be an inverted triangle formed by one of the edge flips. (An inverted triangle is one with negative area - that is, its vertices are arranged in clockwise order - and is best thought of as a wrinkle in the fabric of the mesh.) Each inverted triangle can be thought of as a reflex vertex pushed on the stack, waiting to be fixed later. A reflex vertex is popped from the stack when a vertex is inserted that is visible to the reflex vertex. (However, if the vertex behind the reflex vertex is not visible to the reflex vertex, a new inverted triangle will take its place on the stack.) These details are handled by the DelaunayFixup() routine above.

Delaunay ( ) : int

Form a Delaunay triangulation.

DelaunayFixup ( TriangleNet.Data.Otri &fixuptri, bool leftside ) : void

Enforce the Delaunay condition at an edge, fanning out recursively from an existing vertex. Pay special attention to stacking inverted triangles.

This is a support routine for inserting segments into a constrained Delaunay triangulation. The origin of fixuptri is treated as if it has just been inserted, and the local Delaunay condition needs to be enforced. It is only enforced in one sector, however, that being the angular range defined by fixuptri. This routine also needs to make decisions regarding the "stacking" of triangles. (Read the description of ConstrainedEdge() below before reading on here, so you understand the algorithm.) If the position of the new vertex (the origin of fixuptri) indicates that the vertex before it on the polygon is a reflex vertex, then "stack" the triangle by doing nothing. (fixuptri is an inverted triangle, which is how stacked triangles are identified.) Otherwise, check whether the vertex before that was a reflex vertex. If so, perform an edge flip, thereby eliminating an inverted triangle (popping it off the stack). The edge flip may result in the creation of a new inverted triangle, depending on whether or not the new vertex is visible to the vertex three edges behind on the polygon. If neither of the two vertices behind the new vertex are reflex vertices, fixuptri and fartri, the triangle opposite it, are not inverted; hence, ensure that the edge between them is locally Delaunay.

DeleteVertex ( TriangleNet.Data.Otri &deltri ) : void

Delete a vertex from a Delaunay triangulation, ensuring that the triangulation remains Delaunay.

The origin of 'deltri' is deleted. The union of the triangles adjacent to this vertex is a polygon, for which the Delaunay triangulation is found. Two triangles are removed from the mesh. Only interior vertices that do not lie on segments or boundaries may be deleted.

DummyInit ( ) : void

Initialize the triangle that fills "outer space" and the omnipresent subsegment.

The triangle that fills "outer space," called 'dummytri', is pointed to by every triangle and subsegment on a boundary (be it outer or inner) of the triangulation. Also, 'dummytri' points to one of the triangles on the convex hull (until the holes and concavities are carved), making it possible to find a starting triangle for point location. The omnipresent subsegment, 'dummysub', is pointed to by every triangle or subsegment that doesn't have a full complement of real subsegments to point to. 'dummytri' and 'dummysub' are generally required to fulfill only a few invariants: their vertices must remain NULL and 'dummytri' must always be bonded (at offset zero) to some triangle on the convex hull of the mesh, via a boundary edge. Otherwise, the connections of 'dummytri' and 'dummysub' may change willy-nilly. This makes it possible to avoid writing a good deal of special-case code (in the edge flip, for example) for dealing with the boundary of the mesh, places where no subsegment is present, and so forth. Other entities are frequently bonded to 'dummytri' and 'dummysub' as if they were real mesh entities, with no harm done.

FindDirection ( TriangleNet.Data.Otri &searchtri, Vertex searchpoint ) : FindDirectionResult

Find the first triangle on the path from one point to another.

Finds the triangle that intersects a line segment drawn from the origin of 'searchtri' to the point 'searchpoint', and returns the result in 'searchtri'. The origin of 'searchtri' does not change, even though the triangle returned may differ from the one passed in. This routine is used to find the direction to move in to get from one point to another.

Flip ( TriangleNet.Data.Otri &flipedge ) : void

Transform two triangles to two different triangles by flipping an edge counterclockwise within a quadrilateral.

Imagine the original triangles, abc and bad, oriented so that the shared edge ab lies in a horizontal plane, with the vertex b on the left and the vertex a on the right. The vertex c lies below the edge, and the vertex d lies above the edge. The 'flipedge' handle holds the edge ab of triangle abc, and is directed left, from vertex a to vertex b. The triangles abc and bad are deleted and replaced by the triangles cdb and dca. The triangles that represent abc and bad are NOT deallocated; they are reused for dca and cdb, respectively. Hence, any handles that may have held the original triangles are still valid, although not directed as they were before. Upon completion of this routine, the 'flipedge' handle holds the edge dc of triangle dca, and is directed down, from vertex d to vertex c. (Hence, the two triangles have rotated counterclockwise.) WARNING: This transformation is geometrically valid only if the quadrilateral adbc is convex. Furthermore, this transformation is valid only if there is not a subsegment between the triangles abc and bad. This routine does not check either of these preconditions, and it is the responsibility of the calling routine to ensure that they are met. If they are not, the streets shall be filled with wailing and gnashing of teeth. Terminology A "local transformation" replaces a small set of triangles with another set of triangles. This may or may not involve inserting or deleting a vertex. The term "casing" is used to describe the set of triangles that are attached to the triangles being transformed, but are not transformed themselves. Think of the casing as a fixed hollow structure inside which all the action happens. A "casing" is only defined relative to a single transformation; each occurrence of a transformation will involve a different casing.

FormSkeleton ( InputGeometry input ) : void

Create the segments of a triangulation, including PSLG segments and edges on the convex hull.

InsertSegment ( Vertex endpoint1, Vertex endpoint2, int newmark ) : void

Insert a PSLG segment into a triangulation.

InsertSubseg ( TriangleNet.Data.Otri &tri, int subsegmark ) : void

Create a new subsegment and inserts it between two triangles. Its vertices are properly initialized.

InsertVertex ( Vertex newvertex, TriangleNet.Data.Otri &searchtri, TriangleNet.Data.Osub &splitseg, bool segmentflaws, bool triflaws ) : InsertVertexResult

Insert a vertex into a Delaunay triangulation, performing flips as necessary to maintain the Delaunay property.

The point 'newvertex' is located. If 'searchtri.triangle' is not NULL, the search for the containing triangle begins from 'searchtri'. If 'searchtri.triangle' is NULL, a full point location procedure is called. If 'insertvertex' is found inside a triangle, the triangle is split into three; if 'insertvertex' lies on an edge, the edge is split in two, thereby splitting the two adjacent triangles into four. Edge flips are used to restore the Delaunay property. If 'insertvertex' lies on an existing vertex, no action is taken, and the value DUPLICATEVERTEX is returned. On return, 'searchtri' is set to a handle whose origin is the existing vertex. InsertVertex() does not use flip() for reasons of speed; some information can be reused from edge flip to edge flip, like the locations of subsegments. Param 'splitseg': Normally, the parameter 'splitseg' is set to NULL, implying that no subsegment should be split. In this case, if 'insertvertex' is found to lie on a segment, no action is taken, and the value VIOLATINGVERTEX is returned. On return, 'searchtri' is set to a handle whose primary edge is the violated subsegment. If the calling routine wishes to split a subsegment by inserting a vertex in it, the parameter 'splitseg' should be that subsegment. In this case, 'searchtri' MUST be the triangle handle reached by pivoting from that subsegment; no point location is done. Param 'segmentflaws': Flags that indicate whether or not there should be checks for the creation of encroached subsegments. If a newly inserted vertex encroaches upon subsegments, these subsegments are added to the list of subsegments to be split if 'segmentflaws' is set. Param 'triflaws': Flags that indicate whether or not there should be checks for the creation of bad quality triangles. If bad triangles are created, these are added to the queue if 'triflaws' is set.

MakeSegment ( TriangleNet.Data.Osub &newsubseg ) : void

Create a new subsegment with orientation zero.

MakeTriangle ( TriangleNet.Data.Otri &newotri ) : void

Create a new triangle with orientation zero.

MakeVertexMap ( ) : void

Construct a mapping from vertices to triangles to improve the speed of point location for segment insertion.

Traverses all the triangles, and provides each corner of each triangle with a pointer to that triangle. Of course, pointers will be overwritten by other pointers because (almost) each vertex is a corner of several triangles, but in the end every vertex will point to some triangle that contains it.

MarkHull ( ) : void

Cover the convex hull of a triangulation with subsegments.

Reset ( ) : void

Reset the mesh triangulation state.

ResetData ( ) : void

Reset all the mesh data. This method will also wipe out all mesh data.

ScoutSegment ( TriangleNet.Data.Otri &searchtri, Vertex endpoint2, int newmark ) : bool

Scout the first triangle on the path from one endpoint to another, and check for completion (reaching the second endpoint), a collinear vertex, or the intersection of two segments.

If the first triangle on the path has the second endpoint as its destination or apex, a subsegment is inserted and the job is done. If the first triangle on the path has a destination or apex that lies on the segment, a subsegment is inserted connecting the first endpoint to the collinear vertex, and the search is continued from the collinear vertex. If the first triangle on the path has a subsegment opposite its origin, then there is a segment that intersects the segment being inserted. Their intersection vertex is inserted, splitting the subsegment.

SegmentIntersection ( TriangleNet.Data.Otri &splittri, TriangleNet.Data.Osub &splitsubseg, Vertex endpoint2 ) : void

Find the intersection of an existing segment and a segment that is being inserted. Insert a vertex at the intersection, splitting an existing subsegment.

The segment being inserted connects the apex of splittri to endpoint2. splitsubseg is the subsegment being split, and MUST adjoin splittri. Hence, endpoints of the subsegment being split are the origin and destination of splittri. On completion, splittri is a handle having the newly inserted intersection point as its origin, and endpoint1 as its destination.

SubsegDealloc ( Segment dyingsubseg ) : void

Deallocate space for a subsegment, marking it dead.

TransferNodes ( InputGeometry data ) : void

Read the vertices from memory.

TriangleDealloc ( TriangleNet.Data.Triangle dyingtriangle ) : void

Deallocate space for a triangle, marking it dead.

TriangulatePolygon ( TriangleNet.Data.Otri firstedge, TriangleNet.Data.Otri lastedge, int edgecount, bool doflip, bool triflaws ) : void

Find the Delaunay triangulation of a polygon that has a certain "nice" shape. This includes the polygons that result from deletion of a vertex or insertion of a segment.

This is a conceptually difficult routine. The starting assumption is that we have a polygon with n sides. n - 1 of these sides are currently represented as edges in the mesh. One side, called the "base", need not be. Inside the polygon is a structure I call a "fan", consisting of n - 1 triangles that share a common origin. For each of these triangles, the edge opposite the origin is one of the sides of the polygon. The primary edge of each triangle is the edge directed from the origin to the destination; note that this is not the same edge that is a side of the polygon. 'firstedge' is the primary edge of the first triangle. From there, the triangles follow in counterclockwise order about the polygon, until 'lastedge', the primary edge of the last triangle. 'firstedge' and 'lastedge' are probably connected to other triangles beyond the extremes of the fan, but their identity is not important, as long as the fan remains connected to them. Imagine the polygon oriented so that its base is at the bottom. This puts 'firstedge' on the far right, and 'lastedge' on the far left. The right vertex of the base is the destination of 'firstedge', and the left vertex of the base is the apex of 'lastedge'. The challenge now is to find the right sequence of edge flips to transform the fan into a Delaunay triangulation of the polygon. Each edge flip effectively removes one triangle from the fan, committing it to the polygon. The resulting polygon has one fewer edge. If 'doflip' is set, the final flip will be performed, resulting in a fan of one (useless?) triangle. If 'doflip' is not set, the final flip is not performed, resulting in a fan of two triangles, and an unfinished triangular polygon that is not yet filled out with a single triangle. On completion of the routine, 'lastedge' is the last remaining triangle, or the leftmost of the last two. Although the flips are performed in the order described above, the decisions about what flips to perform are made in precisely the reverse order. The recursive triangulatepolygon() procedure makes a decision, uses up to two recursive calls to triangulate the "subproblems" (polygons with fewer edges), and then performs an edge flip. The "decision" it makes is which vertex of the polygon should be connected to the base. This decision is made by testing every possible vertex. Once the best vertex is found, the two edges that connect this vertex to the base become the bases for two smaller polygons. These are triangulated recursively. Unfortunately, this approach can take O(n^2) time not only in the worst case, but in many common cases. It's rarely a big deal for vertex deletion, where n is rarely larger than ten, but it could be a big deal for segment insertion, especially if there's a lot of long segments that each cut many triangles. I ought to code a faster algorithm some day.

UndoVertex ( ) : void

Undo the most recent vertex insertion.

Walks through the list of transformations (flips and a vertex insertion) in the reverse of the order in which they were done, and undoes them. The inserted vertex is removed from the triangulation and deallocated. Two triangles (possibly just one) are also deallocated.

Unflip ( TriangleNet.Data.Otri &flipedge ) : void

Transform two triangles to two different triangles by flipping an edge clockwise within a quadrilateral. Reverses the flip() operation so that the data structures representing the triangles are back where they were before the flip().

See above Flip() remarks for more information. Upon completion of this routine, the 'flipedge' handle holds the edge cd of triangle cdb, and is directed up, from vertex c to vertex d. (Hence, the two triangles have rotated clockwise.)

VertexDealloc ( Vertex dyingvertex ) : void

Deallocate space for a vertex, marking it dead.

Method Details

Check() public method

Check mesh consistency and (constrained) Delaunay property.
public Check ( bool &isConsistent, bool &isDelaunay ) : void
isConsistent bool Value indicating if mesh topology is consistent.
isDelaunay bool Value indicating if mesh is Delaunay.
return void

Load() public method

Reconstructs a mesh from raw input data.
public Load ( InputGeometry input, List triangles ) : void
input TriangleNet.Geometry.InputGeometry
triangles List
return void

Load() public method

Load a mesh from file (.node/poly and .ele).
public Load ( string filename ) : void
filename string
return void

Mesh() public method

Initializes a new instance of the Mesh class.
public Mesh ( ) : System
return System

Mesh() public method

Initializes a new instance of the Mesh class.
public Mesh ( Behavior behavior ) : System
behavior Behavior
return System

Refine() public method

Refines the current mesh.
public Refine ( ) : void
return void

Refine() public method

Refines the current mesh by finding the maximum triangle area and setting the a global area constraint to half its size.
public Refine ( bool halfArea ) : void
halfArea bool
return void

Refine() public method

Refines the current mesh by setting a global area constraint.
public Refine ( double areaConstraint ) : void
areaConstraint double Global area constraint.
return void

Renumber() public method

Renumber vertex and triangle id's.
public Renumber ( ) : void
return void

Renumber() public method

Renumber vertex and triangle id's.
public Renumber ( NodeNumbering num ) : void
num NodeNumbering
return void

Smooth() public method

Smooth the current mesh.
public Smooth ( ) : void
return void

Triangulate() public method

Triangulate given input data.
public Triangulate ( InputGeometry input ) : void
input TriangleNet.Geometry.InputGeometry
return void

Triangulate() public method

Triangulate given input file (.node or .poly).
public Triangulate ( string inputFile ) : void
inputFile string
return void