Provides some primitives regularly used in computational geometry.
예제 #1
0
        /// <summary>
        /// Find a triangle or edge containing a given point.
        /// </summary>
        /// <param name="searchpoint">The point to locate.</param>
        /// <param name="searchtri">The triangle to start the search at.</param>
        /// <returns>Location information.</returns>
        /// <remarks>
        /// Searching begins from one of:  the input 'searchtri', a recently
        /// encountered triangle 'recenttri', or from a triangle chosen from a
        /// random sample. The choice is made by determining which triangle's
        /// origin is closest to the point we are searching for. Normally,
        /// 'searchtri' should be a handle on the convex hull of the triangulation.
        ///
        /// Details on the random sampling method can be found in the Mucke, Saias,
        /// and Zhu paper cited in the header of this code.
        ///
        /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
        ///
        /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
        /// is a handle whose origin is the existing vertex.
        ///
        /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
        /// handle whose primary edge is the edge on which the point lies.
        ///
        /// Returns INTRIANGLE if the point lies strictly within a triangle.
        /// 'searchtri' is a handle on the triangle that contains the point.
        ///
        /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
        /// handle whose primary edge the point is to the right of.  This might
        /// occur when the circumcenter of a triangle falls just slightly outside
        /// the mesh due to floating-point roundoff error. It also occurs when
        /// seeking a hole or region point that a foolish user has placed outside
        /// the mesh.
        ///
        /// WARNING:  This routine is designed for convex triangulations, and will
        /// not generally work after the holes and concavities have been carved.
        /// </remarks>
        public LocateResult Locate(Point searchpoint, ref Otri searchtri)
        {
            Otri   sampletri = default(Otri);
            Vertex torg, tdest;
            float  searchdist, dist;
            float  ahead;

            // Record the distance from the suggested starting triangle to the
            // point we seek.
            torg       = searchtri.Org();
            searchdist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
                         (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);

            // If a recently encountered triangle has been recorded and has not been
            // deallocated, test it as a good starting point.
            if (recenttri.triangle != null)
            {
                if (!Otri.IsDead(recenttri.triangle))
                {
                    torg = recenttri.Org();
                    if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
                    {
                        recenttri.Copy(ref searchtri);
                        return(LocateResult.OnVertex);
                    }
                    dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
                           (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
                    if (dist < searchdist)
                    {
                        recenttri.Copy(ref searchtri);
                        searchdist = dist;
                    }
                }
            }

            // TODO: Improve sampling.
            sampler.Update(mesh);
            int[] samples = sampler.GetSamples(mesh);

            foreach (var key in samples)
            {
                sampletri.triangle = mesh.triangles[key];
                if (!Otri.IsDead(sampletri.triangle))
                {
                    torg = sampletri.Org();
                    dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
                           (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
                    if (dist < searchdist)
                    {
                        sampletri.Copy(ref searchtri);
                        searchdist = dist;
                    }
                }
            }

            // Where are we?
            torg  = searchtri.Org();
            tdest = searchtri.Dest();
            // Check the starting triangle's vertices.
            if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
            {
                return(LocateResult.OnVertex);
            }
            if ((tdest.x == searchpoint.X) && (tdest.y == searchpoint.Y))
            {
                searchtri.LnextSelf();
                return(LocateResult.OnVertex);
            }
            // Orient 'searchtri' to fit the preconditions of calling preciselocate().
            ahead = Primitives.CounterClockwise(torg, tdest, searchpoint);
            if (ahead < 0.0)
            {
                // Turn around so that 'searchpoint' is to the left of the
                // edge specified by 'searchtri'.
                searchtri.SymSelf();
            }
            else if (ahead == 0.0)
            {
                // Check if 'searchpoint' is between 'torg' and 'tdest'.
                if (((torg.x < searchpoint.X) == (searchpoint.X < tdest.x)) &&
                    ((torg.y < searchpoint.Y) == (searchpoint.Y < tdest.y)))
                {
                    return(LocateResult.OnEdge);
                }
            }
            return(PreciseLocate(searchpoint, ref searchtri, false));
        }
예제 #2
0
        public void CarveHoles()
        {
            Otri otri = new Otri();

            Triangle[] triangleArray = null;
            if (!this.mesh.behavior.Convex)
            {
                this.InfectHull();
            }
            if (!this.mesh.behavior.NoHoles)
            {
                foreach (Point hole in this.mesh.holes)
                {
                    if (!this.mesh.bounds.Contains(hole))
                    {
                        continue;
                    }
                    otri.triangle = Mesh.dummytri;
                    otri.orient   = 0;
                    otri.SymSelf();
                    if (Primitives.CounterClockwise(otri.Org(), otri.Dest(), hole) <= 0 || this.mesh.locator.Locate(hole, ref otri) == LocateResult.Outside || otri.IsInfected())
                    {
                        continue;
                    }
                    otri.Infect();
                    this.viri.Add(otri.triangle);
                }
            }
            if (this.mesh.regions.Count > 0)
            {
                int num = 0;
                triangleArray = new Triangle[this.mesh.regions.Count];
                foreach (RegionPointer region in this.mesh.regions)
                {
                    triangleArray[num] = Mesh.dummytri;
                    if (this.mesh.bounds.Contains(region.point))
                    {
                        otri.triangle = Mesh.dummytri;
                        otri.orient   = 0;
                        otri.SymSelf();
                        if (Primitives.CounterClockwise(otri.Org(), otri.Dest(), region.point) > 0 && this.mesh.locator.Locate(region.point, ref otri) != LocateResult.Outside && !otri.IsInfected())
                        {
                            triangleArray[num]        = otri.triangle;
                            triangleArray[num].region = region.id;
                        }
                    }
                    num++;
                }
            }
            if (this.viri.Count > 0)
            {
                this.Plague();
            }
            if (triangleArray != null)
            {
                RegionIterator regionIterator = new RegionIterator(this.mesh);
                for (int i = 0; i < (int)triangleArray.Length; i++)
                {
                    if (triangleArray[i] != Mesh.dummytri && !Otri.IsDead(triangleArray[i]))
                    {
                        regionIterator.Process(triangleArray[i]);
                    }
                }
            }
            this.viri.Clear();
        }
예제 #3
0
        /// <summary>
        /// Find a triangle or edge containing a given point.
        /// </summary>
        /// <param name="searchpoint">The point to locate.</param>
        /// <param name="searchtri">The triangle to start the search at.</param>
        /// <param name="stopatsubsegment"> If 'stopatsubsegment' is set, the search
        /// will stop if it tries to walk through a subsegment, and will return OUTSIDE.</param>
        /// <returns>Location information.</returns>
        /// <remarks>
        /// Begins its search from 'searchtri'. It is important that 'searchtri'
        /// be a handle with the property that 'searchpoint' is strictly to the left
        /// of the edge denoted by 'searchtri', or is collinear with that edge and
        /// does not intersect that edge. (In particular, 'searchpoint' should not
        /// be the origin or destination of that edge.)
        ///
        /// These conditions are imposed because preciselocate() is normally used in
        /// one of two situations:
        ///
        /// (1)  To try to find the location to insert a new point.  Normally, we
        ///      know an edge that the point is strictly to the left of. In the
        ///      incremental Delaunay algorithm, that edge is a bounding box edge.
        ///      In Ruppert's Delaunay refinement algorithm for quality meshing,
        ///      that edge is the shortest edge of the triangle whose circumcenter
        ///      is being inserted.
        ///
        /// (2)  To try to find an existing point.  In this case, any edge on the
        ///      convex hull is a good starting edge. You must screen out the
        ///      possibility that the vertex sought is an endpoint of the starting
        ///      edge before you call preciselocate().
        ///
        /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
        ///
        /// This implementation differs from that given by Guibas and Stolfi.  It
        /// walks from triangle to triangle, crossing an edge only if 'searchpoint'
        /// is on the other side of the line containing that edge. After entering
        /// a triangle, there are two edges by which one can leave that triangle.
        /// If both edges are valid ('searchpoint' is on the other side of both
        /// edges), one of the two is chosen by drawing a line perpendicular to
        /// the entry edge (whose endpoints are 'forg' and 'fdest') passing through
        /// 'fapex'. Depending on which side of this perpendicular 'searchpoint'
        /// falls on, an exit edge is chosen.
        ///
        /// This implementation is empirically faster than the Guibas and Stolfi
        /// point location routine (which I originally used), which tends to spiral
        /// in toward its target.
        ///
        /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
        /// is a handle whose origin is the existing vertex.
        ///
        /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
        /// handle whose primary edge is the edge on which the point lies.
        ///
        /// Returns INTRIANGLE if the point lies strictly within a triangle.
        /// 'searchtri' is a handle on the triangle that contains the point.
        ///
        /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
        /// handle whose primary edge the point is to the right of.  This might
        /// occur when the circumcenter of a triangle falls just slightly outside
        /// the mesh due to floating-point roundoff error. It also occurs when
        /// seeking a hole or region point that a foolish user has placed outside
        /// the mesh.
        ///
        /// WARNING:  This routine is designed for convex triangulations, and will
        /// not generally work after the holes and concavities have been carved.
        /// However, it can still be used to find the circumcenter of a triangle, as
        /// long as the search is begun from the triangle in question.</remarks>
        public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri,
                                          bool stopatsubsegment)
        {
            Otri   backtracktri = default(Otri);
            Osub   checkedge = default(Osub);
            Vertex forg, fdest, fapex;
            float  orgorient, destorient;
            bool   moveleft;

            // Where are we?
            forg  = searchtri.Org();
            fdest = searchtri.Dest();
            fapex = searchtri.Apex();
            while (true)
            {
                // Check whether the apex is the point we seek.
                if ((fapex.x == searchpoint.X) && (fapex.y == searchpoint.Y))
                {
                    searchtri.LprevSelf();
                    return(LocateResult.OnVertex);
                }
                // Does the point lie on the other side of the line defined by the
                // triangle edge opposite the triangle's destination?
                destorient = Primitives.CounterClockwise(forg, fapex, searchpoint);
                // Does the point lie on the other side of the line defined by the
                // triangle edge opposite the triangle's origin?
                orgorient = Primitives.CounterClockwise(fapex, fdest, searchpoint);
                if (destorient > 0.0)
                {
                    if (orgorient > 0.0)
                    {
                        // Move left if the inner product of (fapex - searchpoint) and
                        // (fdest - forg) is positive.  This is equivalent to drawing
                        // a line perpendicular to the line (forg, fdest) and passing
                        // through 'fapex', and determining which side of this line
                        // 'searchpoint' falls on.
                        moveleft = (fapex.x - searchpoint.X) * (fdest.x - forg.x) +
                                   (fapex.y - searchpoint.Y) * (fdest.y - forg.y) > 0.0;
                    }
                    else
                    {
                        moveleft = true;
                    }
                }
                else
                {
                    if (orgorient > 0.0)
                    {
                        moveleft = false;
                    }
                    else
                    {
                        // The point we seek must be on the boundary of or inside this
                        // triangle.
                        if (destorient == 0.0)
                        {
                            searchtri.LprevSelf();
                            return(LocateResult.OnEdge);
                        }
                        if (orgorient == 0.0)
                        {
                            searchtri.LnextSelf();
                            return(LocateResult.OnEdge);
                        }
                        return(LocateResult.InTriangle);
                    }
                }

                // Move to another triangle. Leave a trace 'backtracktri' in case
                // floating-point roundoff or some such bogey causes us to walk
                // off a boundary of the triangulation.
                if (moveleft)
                {
                    searchtri.Lprev(ref backtracktri);
                    fdest = fapex;
                }
                else
                {
                    searchtri.Lnext(ref backtracktri);
                    forg = fapex;
                }
                backtracktri.Sym(ref searchtri);

                if (mesh.checksegments && stopatsubsegment)
                {
                    // Check for walking through a subsegment.
                    backtracktri.SegPivot(ref checkedge);
                    if (checkedge.seg != Mesh.dummysub)
                    {
                        // Go back to the last triangle.
                        backtracktri.Copy(ref searchtri);
                        return(LocateResult.Outside);
                    }
                }
                // Check for walking right out of the triangulation.
                if (searchtri.triangle == Mesh.dummytri)
                {
                    // Go back to the last triangle.
                    backtracktri.Copy(ref searchtri);
                    return(LocateResult.Outside);
                }

                fapex = searchtri.Apex();
            }
        }
예제 #4
0
        /// <summary>
        /// Inserts a vertex at the circumcenter of a triangle. Deletes
        /// the newly inserted vertex if it encroaches upon a segment.
        /// </summary>
        /// <param name="badtri"></param>
        private void SplitTriangle(BadTriangle badtri)
        {
            Otri               badotri = default(Otri);
            Vertex             borg, bdest, bapex;
            Point              newloc; // Location of the new vertex
            float              xi = 0, eta = 0;
            InsertVertexResult success;
            bool               errorflag;

            badotri = badtri.poortri;
            borg    = badotri.Org();
            bdest   = badotri.Dest();
            bapex   = badotri.Apex();

            // Make sure that this triangle is still the same triangle it was
            // when it was tested and determined to be of bad quality.
            // Subsequent transformations may have made it a different triangle.
            if (!Otri.IsDead(badotri.triangle) && (borg == badtri.triangorg) &&
                (bdest == badtri.triangdest) && (bapex == badtri.triangapex))
            {
                errorflag = false;
                // Create a new vertex at the triangle's circumcenter.

                // Using the original (simpler) Steiner point location method
                // for mesh refinement.
                // TODO: NewLocation doesn't work for refinement. Why? Maybe
                // reset VertexType?
                if (behavior.fixedArea || behavior.VarArea)
                {
                    newloc = Primitives.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant);
                }
                else
                {
                    newloc = newLocation.FindLocation(borg, bdest, bapex, ref xi, ref eta, true, badotri);
                }

                // Check whether the new vertex lies on a triangle vertex.
                if (((newloc.x == borg.x) && (newloc.y == borg.y)) ||
                    ((newloc.x == bdest.x) && (newloc.y == bdest.y)) ||
                    ((newloc.x == bapex.x) && (newloc.y == bapex.y)))
                {
                    if (Behavior.Verbose)
                    {
                        logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()");
                        errorflag = true;
                    }
                }
                else
                {
                    // The new vertex must be in the interior, and therefore is a
                    // free vertex with a marker of zero.
                    Vertex newvertex = new Vertex(newloc.x, newloc.y, 0, mesh.nextras);
                    newvertex.type = VertexType.FreeVertex;

                    for (int i = 0; i < mesh.nextras; i++)
                    {
                        // Interpolate the vertex attributes at the circumcenter.
                        newvertex.attributes[i] = borg.attributes[i]
                                                  + xi * (bdest.attributes[i] - borg.attributes[i])
                                                  + eta * (bapex.attributes[i] - borg.attributes[i]);
                    }

                    // Ensure that the handle 'badotri' does not represent the longest
                    // edge of the triangle.  This ensures that the circumcenter must
                    // fall to the left of this edge, so point location will work.
                    // (If the angle org-apex-dest exceeds 90 degrees, then the
                    // circumcenter lies outside the org-dest edge, and eta is
                    // negative.  Roundoff error might prevent eta from being
                    // negative when it should be, so I test eta against xi.)
                    if (eta < xi)
                    {
                        badotri.LprevSelf();
                    }

                    // Insert the circumcenter, searching from the edge of the triangle,
                    // and maintain the Delaunay property of the triangulation.
                    Osub tmp = default(Osub);
                    success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true);

                    if (success == InsertVertexResult.Successful)
                    {
                        newvertex.hash = mesh.hash_vtx++;
                        newvertex.id   = newvertex.hash;

                        mesh.vertices.Add(newvertex.hash, newvertex);

                        if (mesh.steinerleft > 0)
                        {
                            mesh.steinerleft--;
                        }
                    }
                    else if (success == InsertVertexResult.Encroaching)
                    {
                        // If the newly inserted vertex encroaches upon a subsegment,
                        // delete the new vertex.
                        mesh.UndoVertex();
                    }
                    else if (success == InsertVertexResult.Violating)
                    {
                        // Failed to insert the new vertex, but some subsegment was
                        // marked as being encroached.
                    }
                    else
                    {   // success == DUPLICATEVERTEX
                        // Couldn't insert the new vertex because a vertex is already there.
                        if (Behavior.Verbose)
                        {
                            logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()");
                            errorflag = true;
                        }
                    }
                }
                if (errorflag)
                {
                    logger.Error("The new vertex is at the circumcenter of triangle: This probably "
                                 + "means that I am trying to refine triangles to a smaller size than can be "
                                 + "accommodated by the finite precision of floating point arithmetic.",
                                 "Quality.SplitTriangle()");

                    throw new Exception("The new vertex is at the circumcenter of triangle.");
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Test the mesh for topological consistency.
        /// </summary>
        public bool CheckMesh()
        {
            Otri   tri = default(Otri);
            Otri   oppotri = default(Otri), oppooppotri = default(Otri);
            Vertex triorg, tridest, triapex;
            Vertex oppoorg, oppodest;
            int    horrors;
            bool   saveexact;

            // Temporarily turn on exact arithmetic if it's off.
            saveexact        = Behavior.NoExact;
            Behavior.NoExact = false;
            horrors          = 0;

            // Run through the list of triangles, checking each one.
            foreach (var t in mesh.triangles.Values)
            {
                tri.triangle = t;

                // Check all three edges of the triangle.
                for (tri.orient = 0; tri.orient < 3; tri.orient++)
                {
                    triorg  = tri.Org();
                    tridest = tri.Dest();
                    if (tri.orient == 0)
                    {   // Only test for inversion once.
                        // Test if the triangle is flat or inverted.
                        triapex = tri.Apex();
                        if (Primitives.CounterClockwise(triorg, tridest, triapex) <= 0.0)
                        {
                            logger.Warning("Triangle is flat or inverted.",
                                           "Quality.CheckMesh()");
                            horrors++;
                        }
                    }
                    // Find the neighboring triangle on this edge.
                    tri.Sym(ref oppotri);
                    if (oppotri.triangle != Mesh.dummytri)
                    {
                        // Check that the triangle's neighbor knows it's a neighbor.
                        oppotri.Sym(ref oppooppotri);
                        if ((tri.triangle != oppooppotri.triangle) || (tri.orient != oppooppotri.orient))
                        {
                            if (tri.triangle == oppooppotri.triangle)
                            {
                                logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)",
                                               "Quality.CheckMesh()");
                            }

                            horrors++;
                        }
                        // Check that both triangles agree on the identities
                        // of their shared vertices.
                        oppoorg  = oppotri.Org();
                        oppodest = oppotri.Dest();
                        if ((triorg != oppodest) || (tridest != oppoorg))
                        {
                            logger.Warning("Mismatched edge coordinates between two triangles.",
                                           "Quality.CheckMesh()");

                            horrors++;
                        }
                    }
                }
            }

            // Check for unconnected vertices
            mesh.MakeVertexMap();
            foreach (var v in mesh.vertices.Values)
            {
                if (v.tri.triangle == null)
                {
                    logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)",
                                   "Quality.CheckMesh()");
                }
            }

            if (horrors == 0) // && Behavior.Verbose
            {
                logger.Info("Mesh topology appears to be consistent.");
            }

            // Restore the status of exact arithmetic.
            Behavior.NoExact = saveexact;

            return(horrors == 0);
        }
예제 #6
0
        /// <summary>
        /// Split all the encroached subsegments.
        /// </summary>
        /// <param name="triflaws">A flag that specifies whether one should take
        /// note of new bad triangles that result from inserting vertices to repair
        /// encroached subsegments.</param>
        /// <remarks>
        /// Each encroached subsegment is repaired by splitting it - inserting a
        /// vertex at or near its midpoint.  Newly inserted vertices may encroach
        /// upon other subsegments; these are also repaired.
        /// </remarks>
        private void SplitEncSegs(bool triflaws)
        {
            Otri               enctri     = default(Otri);
            Otri               testtri    = default(Otri);
            Osub               testsh     = default(Osub);
            Osub               currentenc = default(Osub);
            BadSubseg          seg;
            Vertex             eorg, edest, eapex;
            Vertex             newvertex;
            InsertVertexResult success;
            float              segmentlength, nearestpoweroftwo;
            float              split;
            float              multiplier, divisor;
            bool               acuteorg, acuteorg2, acutedest, acutedest2;

            // Note that steinerleft == -1 if an unlimited number
            // of Steiner points is allowed.
            while (badsubsegs.Count > 0)
            {
                if (mesh.steinerleft == 0)
                {
                    break;
                }

                seg = badsubsegs.Dequeue();

                currentenc = seg.encsubseg;
                eorg       = currentenc.Org();
                edest      = currentenc.Dest();
                // Make sure that this segment is still the same segment it was
                // when it was determined to be encroached.  If the segment was
                // enqueued multiple times (because several newly inserted
                // vertices encroached it), it may have already been split.
                if (!Osub.IsDead(currentenc.seg) && (eorg == seg.subsegorg) && (edest == seg.subsegdest))
                {
                    // To decide where to split a segment, we need to know if the
                    // segment shares an endpoint with an adjacent segment.
                    // The concern is that, if we simply split every encroached
                    // segment in its center, two adjacent segments with a small
                    // angle between them might lead to an infinite loop; each
                    // vertex added to split one segment will encroach upon the
                    // other segment, which must then be split with a vertex that
                    // will encroach upon the first segment, and so on forever.
                    // To avoid this, imagine a set of concentric circles, whose
                    // radii are powers of two, about each segment endpoint.
                    // These concentric circles determine where the segment is
                    // split. (If both endpoints are shared with adjacent
                    // segments, split the segment in the middle, and apply the
                    // concentric circles for later splittings.)

                    // Is the origin shared with another segment?
                    currentenc.TriPivot(ref enctri);
                    enctri.Lnext(ref testtri);
                    testtri.SegPivot(ref testsh);
                    acuteorg = testsh.seg != Mesh.dummysub;
                    // Is the destination shared with another segment?
                    testtri.LnextSelf();
                    testtri.SegPivot(ref testsh);
                    acutedest = testsh.seg != Mesh.dummysub;

                    // If we're using Chew's algorithm (rather than Ruppert's)
                    // to define encroachment, delete free vertices from the
                    // subsegment's diametral circle.
                    if (!behavior.ConformingDelaunay && !acuteorg && !acutedest)
                    {
                        eapex = enctri.Apex();
                        while ((eapex.type == VertexType.FreeVertex) &&
                               ((eorg.x - eapex.x) * (edest.x - eapex.x) +
                                (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
                        {
                            mesh.DeleteVertex(ref testtri);
                            currentenc.TriPivot(ref enctri);
                            eapex = enctri.Apex();
                            enctri.Lprev(ref testtri);
                        }
                    }

                    // Now, check the other side of the segment, if there's a triangle there.
                    enctri.Sym(ref testtri);
                    if (testtri.triangle != Mesh.dummytri)
                    {
                        // Is the destination shared with another segment?
                        testtri.LnextSelf();
                        testtri.SegPivot(ref testsh);
                        acutedest2 = testsh.seg != Mesh.dummysub;
                        acutedest  = acutedest || acutedest2;
                        // Is the origin shared with another segment?
                        testtri.LnextSelf();
                        testtri.SegPivot(ref testsh);
                        acuteorg2 = testsh.seg != Mesh.dummysub;
                        acuteorg  = acuteorg || acuteorg2;

                        // Delete free vertices from the subsegment's diametral circle.
                        if (!behavior.ConformingDelaunay && !acuteorg2 && !acutedest2)
                        {
                            eapex = testtri.Org();
                            while ((eapex.type == VertexType.FreeVertex) &&
                                   ((eorg.x - eapex.x) * (edest.x - eapex.x) +
                                    (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
                            {
                                mesh.DeleteVertex(ref testtri);
                                enctri.Sym(ref testtri);
                                eapex = testtri.Apex();
                                testtri.LprevSelf();
                            }
                        }
                    }

                    // Use the concentric circles if exactly one endpoint is shared
                    // with another adjacent segment.
                    if (acuteorg || acutedest)
                    {
                        segmentlength = UnityEngine.Mathf.Sqrt((edest.x - eorg.x) * (edest.x - eorg.x) +
                                                               (edest.y - eorg.y) * (edest.y - eorg.y));
                        // Find the power of two that most evenly splits the segment.
                        // The worst case is a 2:1 ratio between subsegment lengths.
                        nearestpoweroftwo = 1.0f;
                        while (segmentlength > 3.0f * nearestpoweroftwo)
                        {
                            nearestpoweroftwo *= 2.0f;
                        }
                        while (segmentlength < 1.5f * nearestpoweroftwo)
                        {
                            nearestpoweroftwo *= 0.5f;
                        }
                        // Where do we split the segment?
                        split = nearestpoweroftwo / segmentlength;
                        if (acutedest)
                        {
                            split = 1.0f - split;
                        }
                    }
                    else
                    {
                        // If we're not worried about adjacent segments, split
                        // this segment in the middle.
                        split = 0.5f;
                    }

                    // Create the new vertex (interpolate coordinates).
                    newvertex = new Vertex(
                        eorg.x + split * (edest.x - eorg.x),
                        eorg.y + split * (edest.y - eorg.y),
                        currentenc.Mark(),
                        mesh.nextras);

                    newvertex.type = VertexType.SegmentVertex;

                    newvertex.hash = mesh.hash_vtx++;
                    newvertex.id   = newvertex.hash;

                    mesh.vertices.Add(newvertex.hash, newvertex);

                    // Interpolate attributes.
                    for (int i = 0; i < mesh.nextras; i++)
                    {
                        newvertex.attributes[i] = eorg.attributes[i]
                                                  + split * (edest.attributes[i] - eorg.attributes[i]);
                    }

                    if (!Behavior.NoExact)
                    {
                        // Roundoff in the above calculation may yield a 'newvertex'
                        // that is not precisely collinear with 'eorg' and 'edest'.
                        // Improve collinearity by one step of iterative refinement.
                        multiplier = Primitives.CounterClockwise(eorg, edest, newvertex);
                        divisor    = ((eorg.x - edest.x) * (eorg.x - edest.x) +
                                      (eorg.y - edest.y) * (eorg.y - edest.y));
                        if ((multiplier != 0.0) && (divisor != 0.0))
                        {
                            multiplier = multiplier / divisor;
                            // Watch out for NANs.
                            if (!float.IsNaN(multiplier))
                            {
                                newvertex.x += multiplier * (edest.y - eorg.y);
                                newvertex.y += multiplier * (eorg.x - edest.x);
                            }
                        }
                    }

                    // Check whether the new vertex lies on an endpoint.
                    if (((newvertex.x == eorg.x) && (newvertex.y == eorg.y)) ||
                        ((newvertex.x == edest.x) && (newvertex.y == edest.y)))
                    {
                        logger.Error("Ran out of precision: I attempted to split a"
                                     + " segment to a smaller size than can be accommodated by"
                                     + " the finite precision of floating point arithmetic.",
                                     "Quality.SplitEncSegs()");

                        throw new Exception("Ran out of precision");
                    }
                    // Insert the splitting vertex.  This should always succeed.
                    success = mesh.InsertVertex(newvertex, ref enctri, ref currentenc, true, triflaws);
                    if ((success != InsertVertexResult.Successful) && (success != InsertVertexResult.Encroaching))
                    {
                        logger.Error("Failure to split a segment.", "Quality.SplitEncSegs()");
                        throw new Exception("Failure to split a segment.");
                    }
                    if (mesh.steinerleft > 0)
                    {
                        mesh.steinerleft--;
                    }
                    // Check the two new subsegments to see if they're encroached.
                    CheckSeg4Encroach(ref currentenc);
                    currentenc.NextSelf();
                    CheckSeg4Encroach(ref currentenc);
                }

                // Set subsegment's origin to NULL. This makes it possible to detect dead
                // badsubsegs when traversing the list of all badsubsegs.
                seg.subsegorg = null;
            }
        }
예제 #7
0
        /// <summary>
        /// Ensure that the mesh is (constrained) Delaunay.
        /// </summary>
        public bool CheckDelaunay()
        {
            Otri   loop = default(Otri);
            Otri   oppotri = default(Otri);
            Osub   opposubseg = default(Osub);
            Vertex triorg, tridest, triapex;
            Vertex oppoapex;
            bool   shouldbedelaunay;
            int    horrors;
            bool   saveexact;

            // Temporarily turn on exact arithmetic if it's off.
            saveexact        = Behavior.NoExact;
            Behavior.NoExact = false;
            horrors          = 0;

            // Run through the list of triangles, checking each one.
            foreach (var tri in mesh.triangles.Values)
            {
                loop.triangle = tri;

                // Check all three edges of the triangle.
                for (loop.orient = 0; loop.orient < 3;
                     loop.orient++)
                {
                    triorg  = loop.Org();
                    tridest = loop.Dest();
                    triapex = loop.Apex();
                    loop.Sym(ref oppotri);
                    oppoapex = oppotri.Apex();
                    // Only test that the edge is locally Delaunay if there is an
                    // adjoining triangle whose pointer is larger (to ensure that
                    // each pair isn't tested twice).
                    shouldbedelaunay = (oppotri.triangle != Mesh.dummytri) &&
                                       !Otri.IsDead(oppotri.triangle) && loop.triangle.id < oppotri.triangle.id &&
                                       (triorg != mesh.infvertex1) && (triorg != mesh.infvertex2) &&
                                       (triorg != mesh.infvertex3) &&
                                       (tridest != mesh.infvertex1) && (tridest != mesh.infvertex2) &&
                                       (tridest != mesh.infvertex3) &&
                                       (triapex != mesh.infvertex1) && (triapex != mesh.infvertex2) &&
                                       (triapex != mesh.infvertex3) &&
                                       (oppoapex != mesh.infvertex1) && (oppoapex != mesh.infvertex2) &&
                                       (oppoapex != mesh.infvertex3);
                    if (mesh.checksegments && shouldbedelaunay)
                    {
                        // If a subsegment separates the triangles, then the edge is
                        // constrained, so no local Delaunay test should be done.
                        loop.SegPivot(ref opposubseg);
                        if (opposubseg.seg != Mesh.dummysub)
                        {
                            shouldbedelaunay = false;
                        }
                    }
                    if (shouldbedelaunay)
                    {
                        if (Primitives.NonRegular(triorg, tridest, triapex, oppoapex) > 0.0)
                        {
                            logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).",
                                                         loop.triangle.id, oppotri.triangle.id), "Quality.CheckDelaunay()");
                            horrors++;
                        }
                    }
                }
            }

            if (horrors == 0) // && Behavior.Verbose
            {
                logger.Info("Mesh is Delaunay.");
            }

            // Restore the status of exact arithmetic.
            Behavior.NoExact = saveexact;

            return(horrors == 0);
        }