Пример #1
0
        public static void WriteVoronoi(Mesh mesh, string filename)
        {
            Vertex vertex;
            Vertex vertex1;
            double x;
            Otri   otri  = new Otri();
            Otri   otri1 = new Otri();
            double num   = 0;
            double num1  = 0;
            int    num2  = 0;

            otri.orient = 0;
            using (StreamWriter streamWriter = new StreamWriter(new FileStream(filename, FileMode.Create)))
            {
                streamWriter.WriteLine("{0} 2 {1} 0", mesh.triangles.Count, mesh.nextras);
                foreach (Triangle value in mesh.triangles.Values)
                {
                    otri.triangle = value;
                    vertex        = otri.Org();
                    vertex1       = otri.Dest();
                    Vertex vertex2 = otri.Apex();
                    Point  point   = Primitives.FindCircumcenter(vertex, vertex1, vertex2, ref num, ref num1);
                    object obj     = num2;
                    x = point.X;
                    string str = x.ToString(FileWriter.nfi);
                    x = point.Y;
                    streamWriter.Write("{0} {1} {2}", obj, str, x.ToString(FileWriter.nfi));
                    for (int i = 0; i < mesh.nextras; i++)
                    {
                        streamWriter.Write(" 0");
                    }
                    streamWriter.WriteLine();
                    int num3 = num2;
                    num2             = num3 + 1;
                    otri.triangle.id = num3;
                }
                streamWriter.WriteLine("{0} 0", mesh.edges);
                num2 = 0;
                foreach (Triangle triangle in mesh.triangles.Values)
                {
                    otri.triangle = triangle;
                    otri.orient   = 0;
                    while (otri.orient < 3)
                    {
                        otri.Sym(ref otri1);
                        if (otri.triangle.id < otri1.triangle.id || otri1.triangle == Mesh.dummytri)
                        {
                            int num4 = otri.triangle.id;
                            if (otri1.triangle != Mesh.dummytri)
                            {
                                int num5 = otri1.triangle.id;
                                streamWriter.WriteLine("{0} {1} {2}", num2, num4, num5);
                            }
                            else
                            {
                                vertex  = otri.Org();
                                vertex1 = otri.Dest();
                                object[] objArray = new object[] { num2, num4, null, null };
                                x           = vertex1[1] - vertex[1];
                                objArray[2] = x.ToString(FileWriter.nfi);
                                x           = vertex[0] - vertex1[0];
                                objArray[3] = x.ToString(FileWriter.nfi);
                                streamWriter.WriteLine("{0} {1} -1 {2} {3}", objArray);
                            }
                            num2++;
                        }
                        otri.orient = otri.orient + 1;
                    }
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Test a triangle for quality and size.
        /// </summary>
        /// <param name="testtri">Triangle to check.</param>
        /// <remarks>
        /// Tests a triangle to see if it satisfies the minimum angle condition and
        /// the maximum area condition.  Triangles that aren't up to spec are added
        /// to the bad triangle queue.
        /// </remarks>
        public void TestTriangle(ref Otri testtri)
        {
            Otri   tri1 = default(Otri), tri2 = default(Otri);
            Osub   testsub = default(Osub);
            Vertex torg, tdest, tapex;
            Vertex base1, base2;
            Vertex org1, dest1, org2, dest2;
            Vertex joinvertex;
            float  dxod, dyod, dxda, dyda, dxao, dyao;
            float  dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
            float  apexlen, orglen, destlen, minedge;
            float  angle;
            float  area;
            float  dist1, dist2;

            float maxangle;

            torg  = testtri.Org();
            tdest = testtri.Dest();
            tapex = testtri.Apex();
            dxod  = torg.x - tdest.x;
            dyod  = torg.y - tdest.y;
            dxda  = tdest.x - tapex.x;
            dyda  = tdest.y - tapex.y;
            dxao  = tapex.x - torg.x;
            dyao  = tapex.y - torg.y;
            dxod2 = dxod * dxod;
            dyod2 = dyod * dyod;
            dxda2 = dxda * dxda;
            dyda2 = dyda * dyda;
            dxao2 = dxao * dxao;
            dyao2 = dyao * dyao;
            // Find the lengths of the triangle's three edges.
            apexlen = dxod2 + dyod2;
            orglen  = dxda2 + dyda2;
            destlen = dxao2 + dyao2;

            if ((apexlen < orglen) && (apexlen < destlen))
            {
                // The edge opposite the apex is shortest.
                minedge = apexlen;
                // Find the square of the cosine of the angle at the apex.
                angle = dxda * dxao + dyda * dyao;
                angle = angle * angle / (orglen * destlen);
                base1 = torg;
                base2 = tdest;
                testtri.Copy(ref tri1);
            }
            else if (orglen < destlen)
            {
                // The edge opposite the origin is shortest.
                minedge = orglen;
                // Find the square of the cosine of the angle at the origin.
                angle = dxod * dxao + dyod * dyao;
                angle = angle * angle / (apexlen * destlen);
                base1 = tdest;
                base2 = tapex;
                testtri.Lnext(ref tri1);
            }
            else
            {
                // The edge opposite the destination is shortest.
                minedge = destlen;
                // Find the square of the cosine of the angle at the destination.
                angle = dxod * dxda + dyod * dyda;
                angle = angle * angle / (apexlen * orglen);
                base1 = tapex;
                base2 = torg;
                testtri.Lprev(ref tri1);
            }

            if (behavior.VarArea || behavior.fixedArea || behavior.Usertest)
            {
                // Check whether the area is larger than permitted.
                area = 0.5f * (dxod * dyda - dyod * dxda);
                if (behavior.fixedArea && (area > behavior.MaxArea))
                {
                    // Add this triangle to the list of bad triangles.
                    queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
                    return;
                }

                // Nonpositive area constraints are treated as unconstrained.
                if ((behavior.VarArea) && (area > testtri.triangle.area) && (testtri.triangle.area > 0.0))
                {
                    // Add this triangle to the list of bad triangles.
                    queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
                    return;
                }
            }

            // find the maximum edge and accordingly the pqr orientation
            if ((apexlen > orglen) && (apexlen > destlen))
            {
                // The edge opposite the apex is longest.
                // maxedge = apexlen;
                // Find the cosine of the angle at the apex.
                maxangle = (orglen + destlen - apexlen) / (2 * UnityEngine.Mathf.Sqrt(orglen * destlen));
            }
            else if (orglen > destlen)
            {
                // The edge opposite the origin is longest.
                // maxedge = orglen;
                // Find the cosine of the angle at the origin.
                maxangle = (apexlen + destlen - orglen) / (2 * UnityEngine.Mathf.Sqrt(apexlen * destlen));
            }
            else
            {
                // The edge opposite the destination is longest.
                // maxedge = destlen;
                // Find the cosine of the angle at the destination.
                maxangle = (apexlen + orglen - destlen) / (2 * UnityEngine.Mathf.Sqrt(apexlen * orglen));
            }

            // Check whether the angle is smaller than permitted.
            if ((angle > behavior.goodAngle) || (maxangle < behavior.maxGoodAngle && behavior.MaxAngle != 0.0))
            {
                // Use the rules of Miller, Pav, and Walkington to decide that certain
                // triangles should not be split, even if they have bad angles.
                // A skinny triangle is not split if its shortest edge subtends a
                // small input angle, and both endpoints of the edge lie on a
                // concentric circular shell.  For convenience, I make a small
                // adjustment to that rule:  I check if the endpoints of the edge
                // both lie in segment interiors, equidistant from the apex where
                // the two segments meet.
                // First, check if both points lie in segment interiors.
                if ((base1.type == VertexType.SegmentVertex) &&
                    (base2.type == VertexType.SegmentVertex))
                {
                    // Check if both points lie in a common segment. If they do, the
                    // skinny triangle is enqueued to be split as usual.
                    tri1.SegPivot(ref testsub);
                    if (testsub.seg == Mesh.dummysub)
                    {
                        // No common segment.  Find a subsegment that contains 'torg'.
                        tri1.Copy(ref tri2);
                        do
                        {
                            tri1.OprevSelf();
                            tri1.SegPivot(ref testsub);
                        } while (testsub.seg == Mesh.dummysub);
                        // Find the endpoints of the containing segment.
                        org1  = testsub.SegOrg();
                        dest1 = testsub.SegDest();
                        // Find a subsegment that contains 'tdest'.
                        do
                        {
                            tri2.DnextSelf();
                            tri2.SegPivot(ref testsub);
                        } while (testsub.seg == Mesh.dummysub);
                        // Find the endpoints of the containing segment.
                        org2  = testsub.SegOrg();
                        dest2 = testsub.SegDest();
                        // Check if the two containing segments have an endpoint in common.
                        joinvertex = null;
                        if ((dest1.x == org2.x) && (dest1.y == org2.y))
                        {
                            joinvertex = dest1;
                        }
                        else if ((org1.x == dest2.x) && (org1.y == dest2.y))
                        {
                            joinvertex = org1;
                        }
                        if (joinvertex != null)
                        {
                            // Compute the distance from the common endpoint (of the two
                            // segments) to each of the endpoints of the shortest edge.
                            dist1 = ((base1.x - joinvertex.x) * (base1.x - joinvertex.x) +
                                     (base1.y - joinvertex.y) * (base1.y - joinvertex.y));
                            dist2 = ((base2.x - joinvertex.x) * (base2.x - joinvertex.x) +
                                     (base2.y - joinvertex.y) * (base2.y - joinvertex.y));
                            // If the two distances are equal, don't split the triangle.
                            if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2))
                            {
                                // Return now to avoid enqueueing the bad triangle.
                                return;
                            }
                        }
                    }
                }

                // Add this triangle to the list of bad triangles.
                queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
            }
        }
Пример #3
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;
            }
        }
        /// <summary>
        /// Construct Voronoi region for given vertex.
        /// </summary>
        /// <param name="region"></param>
        private void ConstructCell(VoronoiRegion region)
        {
            var vertex = region.Generator as Vertex;

            var vpoints = new List <Point>();

            Otri f      = default(Otri);
            Otri f_init = default(Otri);
            Otri f_next = default(Otri);
            Otri f_prev = default(Otri);

            Osub sub = default(Osub);

            // Call f_init a triangle incident to x
            vertex.tri.Copy(ref f_init);

            f_init.Copy(ref f);
            f_init.Onext(ref f_next);

            // Check if f_init lies on the boundary of the triangulation.
            if (f_next.tri.id == Mesh.DUMMY)
            {
                f_init.Oprev(ref f_prev);

                if (f_prev.tri.id != Mesh.DUMMY)
                {
                    f_init.Copy(ref f_next);
                    // Move one triangle clockwise
                    f_init.Oprev();
                    f_init.Copy(ref f);
                }
            }

            // Go counterclockwise until we reach the border or the initial triangle.
            while (f_next.tri.id != Mesh.DUMMY)
            {
                // Add circumcenter of current triangle
                vpoints.Add(points[f.tri.id]);

                region.AddNeighbor(f.tri.id, regions[f.Apex().id]);

                if (f_next.Equals(f_init))
                {
                    // Voronoi cell is complete (bounded case).
                    region.Add(vpoints);
                    return;
                }

                f_next.Copy(ref f);
                f_next.Onext();
            }

            // Voronoi cell is unbounded
            region.Bounded = false;

            Vertex torg, tdest, tapex;
            Point  intersection;
            int    sid, n = mesh.triangles.Count;

            // Find the boundary segment id (we use this id to number the endpoints of infinit rays).
            f.Lprev(ref f_next);
            f_next.Pivot(ref sub);
            sid = sub.seg.hash;

            // Last valid f lies at the boundary. Add the circumcenter.
            vpoints.Add(points[f.tri.id]);
            region.AddNeighbor(f.tri.id, regions[f.Apex().id]);

            // Check if the intersection with the bounding box has already been computed.
            if (!rayPoints.TryGetValue(sid, out intersection))
            {
                torg         = f.Org();
                tapex        = f.Apex();
                intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], torg.y - tapex.y, tapex.x - torg.x);

                // Set the correct id for the vertex
                intersection.id = n + rayIndex;

                points[n + rayIndex] = intersection;
                rayIndex++;

                rayPoints.Add(sid, intersection);
            }

            vpoints.Add(intersection);

            // Now walk from f_init clockwise till we reach the boundary.
            vpoints.Reverse();

            f_init.Copy(ref f);
            f.Oprev(ref f_prev);

            while (f_prev.tri.id != Mesh.DUMMY)
            {
                vpoints.Add(points[f_prev.tri.id]);
                region.AddNeighbor(f_prev.tri.id, regions[f_prev.Apex().id]);

                f_prev.Copy(ref f);
                f_prev.Oprev();
            }

            // Find the boundary segment id.
            f.Pivot(ref sub);
            sid = sub.seg.hash;

            if (!rayPoints.TryGetValue(sid, out intersection))
            {
                // Intersection has not been computed yet.
                torg  = f.Org();
                tdest = f.Dest();

                intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], tdest.y - torg.y, torg.x - tdest.x);

                // Set the correct id for the vertex
                intersection.id = n + rayIndex;

                rayPoints.Add(sid, intersection);

                points[n + rayIndex] = intersection;
                rayIndex++;
            }

            vpoints.Add(intersection);
            region.AddNeighbor(intersection.id, regions[f.Dest().id]);

            // Add the new points to the region (in counter-clockwise order)
            vpoints.Reverse();
            region.Add(vpoints);
        }
Пример #5
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);
        }
Пример #6
0
        /// <summary>
        /// Reconstruct a triangulation from its raw data representation.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <remarks>
        /// Reads an .ele file and reconstructs the original mesh.  If the -p switch
        /// is used, this procedure will also read a .poly file and reconstruct the
        /// subsegments of the original mesh.  If the -a switch is used, this
        /// procedure will also read an .area file and set a maximum area constraint
        /// on each triangle.
        ///
        /// Vertices that are not corners of triangles, such as nodes on edges of
        /// subparametric elements, are discarded.
        ///
        /// This routine finds the adjacencies between triangles (and subsegments)
        /// by forming one stack of triangles for each vertex. Each triangle is on
        /// three different stacks simultaneously. Each triangle's subsegment
        /// pointers are used to link the items in each stack. This memory-saving
        /// feature makes the code harder to read. The most important thing to keep
        /// in mind is that each triangle is removed from a stack precisely when
        /// the corresponding pointer is adjusted to refer to a subsegment rather
        /// than the next triangle of the stack.
        /// </remarks>
        public static int Reconstruct(Mesh mesh, InputGeometry input, ITriangle[] triangles)
        {
            int hullsize = 0;

            Otri tri           = default(Otri);
            Otri triangleleft  = default(Otri);
            Otri checktri      = default(Otri);
            Otri checkleft     = default(Otri);
            Otri checkneighbor = default(Otri);
            Osub subseg        = default(Osub);

            List <Otri>[] vertexarray; // Triangle
            Otri          prevlink;    // Triangle
            Otri          nexttri;     // Triangle
            Vertex        tdest, tapex;
            Vertex        checkdest, checkapex;
            Vertex        shorg;
            Vertex        segmentorg, segmentdest;

            int[] corner = new int[3];
            int[] end    = new int[2];
            //bool segmentmarkers = false;
            int  boundmarker;
            int  aroundvertex;
            bool notfound;
            int  i = 0;

            int elements         = triangles == null ? 0 : triangles.Length;
            int numberofsegments = input.segments.Count;

            mesh.inelements = elements;
            mesh.regions.AddRange(input.regions);

            // Create the triangles.
            for (i = 0; i < mesh.inelements; i++)
            {
                mesh.MakeTriangle(ref tri);
                // Mark the triangle as living.
                //tri.triangle.neighbors[0].triangle = tri.triangle;
            }

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

                // Create the subsegments.
                for (i = 0; i < mesh.insegments; i++)
                {
                    mesh.MakeSegment(ref subseg);
                    // Mark the subsegment as living.
                    //subseg.ss.subsegs[0].ss = subseg.ss;
                }
            }

            // Allocate a temporary array that maps each vertex to some adjacent
            // triangle. I took care to allocate all the permanent memory for
            // triangles and subsegments first.
            vertexarray = new List <Otri> [mesh.vertices.Count];
            // Each vertex is initially unrepresented.
            for (i = 0; i < mesh.vertices.Count; i++)
            {
                Otri tmp = default(Otri);
                tmp.triangle   = Mesh.dummytri;
                vertexarray[i] = new List <Otri>(3);
                vertexarray[i].Add(tmp);
            }

            i = 0;

            // Read the triangles from the .ele file, and link
            // together those that share an edge.
            foreach (var item in mesh.triangles.Values)
            {
                tri.triangle = item;

                corner[0] = triangles[i].P0;
                corner[1] = triangles[i].P1;
                corner[2] = triangles[i].P2;

                // Copy the triangle's three corners.
                for (int j = 0; j < 3; j++)
                {
                    if ((corner[j] < 0) || (corner[j] >= mesh.invertices))
                    {
                        SimpleLog.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()");
                        throw new Exception("Triangle has an invalid vertex index.");
                    }
                }

                // Read the triangle's attributes.
                tri.triangle.region = triangles[i].Region;

                // TODO: VarArea
                if (mesh.behavior.VarArea)
                {
                    tri.triangle.area = triangles[i].Area;
                }

                // Set the triangle's vertices.
                tri.orient = 0;
                tri.SetOrg(mesh.vertices[corner[0]]);
                tri.SetDest(mesh.vertices[corner[1]]);
                tri.SetApex(mesh.vertices[corner[2]]);

                // Try linking the triangle to others that share these vertices.
                for (tri.orient = 0; tri.orient < 3; tri.orient++)
                {
                    // Take the number for the origin of triangleloop.
                    aroundvertex = corner[tri.orient];
                    int index = vertexarray[aroundvertex].Count - 1;
                    // Look for other triangles having this vertex.
                    nexttri = vertexarray[aroundvertex][index];
                    // Link the current triangle to the next one in the stack.
                    //tri.triangle.neighbors[tri.orient] = nexttri;
                    // Push the current triangle onto the stack.
                    vertexarray[aroundvertex].Add(tri);

                    checktri = nexttri;

                    if (checktri.triangle != Mesh.dummytri)
                    {
                        tdest = tri.Dest();
                        tapex = tri.Apex();

                        // Look for other triangles that share an edge.
                        do
                        {
                            checkdest = checktri.Dest();
                            checkapex = checktri.Apex();

                            if (tapex == checkdest)
                            {
                                // The two triangles share an edge; bond them together.
                                tri.Lprev(ref triangleleft);
                                triangleleft.Bond(ref checktri);
                            }

                            if (tdest == checkapex)
                            {
                                // The two triangles share an edge; bond them together.
                                checktri.Lprev(ref checkleft);
                                tri.Bond(ref checkleft);
                            }

                            // Find the next triangle in the stack.
                            index--;
                            nexttri = vertexarray[aroundvertex][index];

                            checktri = nexttri;
                        } while (checktri.triangle != Mesh.dummytri);
                    }
                }

                i++;
            }

            // Prepare to count the boundary edges.
            hullsize = 0;
            if (mesh.behavior.Poly)
            {
                // Read the segments from the .poly file, and link them
                // to their neighboring triangles.
                boundmarker = 0;
                i           = 0;
                foreach (var item in mesh.subsegs.Values)
                {
                    subseg.seg = item;

                    end[0]      = input.segments[i].P0;
                    end[1]      = input.segments[i].P1;
                    boundmarker = input.segments[i].Boundary;

                    for (int j = 0; j < 2; j++)
                    {
                        if ((end[j] < 0) || (end[j] >= mesh.invertices))
                        {
                            SimpleLog.Instance.Error("Segment has an invalid vertex index.",
                                                     "MeshReader.Reconstruct()");
                            throw new Exception("Segment has an invalid vertex index.");
                        }
                    }

                    // set the subsegment's vertices.
                    subseg.orient = 0;
                    segmentorg    = mesh.vertices[end[0]];
                    segmentdest   = mesh.vertices[end[1]];
                    subseg.SetOrg(segmentorg);
                    subseg.SetDest(segmentdest);
                    subseg.SetSegOrg(segmentorg);
                    subseg.SetSegDest(segmentdest);
                    subseg.seg.boundary = boundmarker;
                    // Try linking the subsegment to triangles that share these vertices.
                    for (subseg.orient = 0; subseg.orient < 2; subseg.orient++)
                    {
                        // Take the number for the destination of subsegloop.
                        aroundvertex = end[1 - subseg.orient];
                        int index = vertexarray[aroundvertex].Count - 1;
                        // Look for triangles having this vertex.
                        prevlink = vertexarray[aroundvertex][index];
                        nexttri  = vertexarray[aroundvertex][index];

                        checktri = nexttri;
                        shorg    = subseg.Org();
                        notfound = true;
                        // Look for triangles having this edge.  Note that I'm only
                        // comparing each triangle's destination with the subsegment;
                        // each triangle's apex is handled through a different vertex.
                        // Because each triangle appears on three vertices' lists, each
                        // occurrence of a triangle on a list can (and does) represent
                        // an edge.  In this way, most edges are represented twice, and
                        // every triangle-subsegment bond is represented once.
                        while (notfound && (checktri.triangle != Mesh.dummytri))
                        {
                            checkdest = checktri.Dest();

                            if (shorg == checkdest)
                            {
                                // We have a match. Remove this triangle from the list.
                                //prevlink = vertexarray[aroundvertex][index];
                                vertexarray[aroundvertex].Remove(prevlink);
                                // Bond the subsegment to the triangle.
                                checktri.SegBond(ref subseg);
                                // Check if this is a boundary edge.
                                checktri.Sym(ref checkneighbor);
                                if (checkneighbor.triangle == Mesh.dummytri)
                                {
                                    // The next line doesn't insert a subsegment (because there's
                                    // already one there), but it sets the boundary markers of
                                    // the existing subsegment and its vertices.
                                    mesh.InsertSubseg(ref checktri, 1);
                                    hullsize++;
                                }

                                notfound = false;
                            }

                            index--;
                            // Find the next triangle in the stack.
                            prevlink = vertexarray[aroundvertex][index];
                            nexttri  = vertexarray[aroundvertex][index];

                            checktri = nexttri;
                        }
                    }

                    i++;
                }
            }

            // Mark the remaining edges as not being attached to any subsegment.
            // Also, count the (yet uncounted) boundary edges.
            for (i = 0; i < mesh.vertices.Count; i++)
            {
                // Search the stack of triangles adjacent to a vertex.
                int index = vertexarray[i].Count - 1;
                nexttri  = vertexarray[i][index];
                checktri = nexttri;

                while (checktri.triangle != Mesh.dummytri)
                {
                    // Find the next triangle in the stack before this
                    // information gets overwritten.
                    index--;
                    nexttri = vertexarray[i][index];
                    // No adjacent subsegment.  (This overwrites the stack info.)
                    checktri.SegDissolve();
                    checktri.Sym(ref checkneighbor);
                    if (checkneighbor.triangle == Mesh.dummytri)
                    {
                        mesh.InsertSubseg(ref checktri, 1);
                        hullsize++;
                    }

                    checktri = nexttri;
                }
            }

            return(hullsize);
        }
Пример #7
0
        private void ConstructBoundaryBvdCell(Vertex vertex)
        {
            VoronoiRegion region = new VoronoiRegion(vertex);

            regions.Add(region);

            Otri f      = default(Otri);
            Otri f_init = default(Otri);
            Otri f_next = default(Otri);
            Otri f_prev = default(Otri);
            Osub sf     = default(Osub);
            Osub sfn    = default(Osub);

            Vertex torg, tdest, tapex, sorg, sdest;
            Point  cc_f, cc_f_next, p;

            int n = mesh.triangles.Count;

            // Call P the polygon (cell) in construction
            List <Point> vpoints = new List <Point>();

            // Call f_init a triangle incident to x
            vertex.tri.Copy(ref f_init);

            if (f_init.Org() != vertex)
            {
                throw new Exception("ConstructBoundaryBvdCell: inconsistent topology.");
            }
            // Let f be initialized to f_init
            f_init.Copy(ref f);
            // Call f_next the next triangle counterclockwise around x
            f_init.Onext(ref f_next);

            f_init.Oprev(ref f_prev);

            // Is the border to the left?
            if (f_prev.triangle != Mesh.dummytri)
            {
                // Go clockwise until we reach the border (or the initial triangle)
                while (f_prev.triangle != Mesh.dummytri && !f_prev.Equal(f_init))
                {
                    f_prev.Copy(ref f);
                    f_prev.OprevSelf();
                }

                f.Copy(ref f_init);
                f.Onext(ref f_next);
            }

            if (f_prev.triangle == Mesh.dummytri)
            {
                // For vertices on the domain boundaray, add the vertex. For
                // internal boundaries don't add it.
                p    = new Point(vertex.x, vertex.y);
                p.id = n + segIndex;
                points[n + segIndex] = p;
                segIndex++;

                vpoints.Add(p);
            }

            // Add midpoint of start triangles' edge.
            torg  = f.Org();
            tdest = f.Dest();
            p     = new Point((torg.X + tdest.X) / 2, (torg.Y + tdest.Y) / 2);
            p.id  = n + segIndex;
            points[n + segIndex] = p;
            segIndex++;

            vpoints.Add(p);

            // repeat ... until f = f_init
            do
            {
                // Call Lffnext the line going through the circumcenters of f and f_next
                cc_f = this.points[f.triangle.id];

                if (f_next.triangle == Mesh.dummytri)
                {
                    if (!f.triangle.infected)
                    {
                        // Add last circumcenter
                        vpoints.Add(cc_f);
                    }

                    // Add midpoint of last triangles' edge (chances are it has already
                    // been added, so post process cell to remove duplicates???)
                    torg  = f.Org();
                    tapex = f.Apex();
                    p     = new Point((torg.X + tapex.X) / 2, (torg.Y + tapex.Y) / 2);
                    p.id  = n + segIndex;
                    points[n + segIndex] = p;
                    segIndex++;

                    vpoints.Add(p);

                    break;
                }

                cc_f_next = this.points[f_next.triangle.id];

                // if f is tagged non-blind then
                if (!f.triangle.infected)
                {
                    // Insert the circumcenter of f into P
                    vpoints.Add(cc_f);

                    if (f_next.triangle.infected)
                    {
                        // Call S_fnext the constrained edge blinding f_next
                        sfn.seg = subsegMap[f_next.triangle.hash];

                        // Insert point Lf,f_next /\ Sf_next into P
                        if (SegmentsIntersect(sfn.SegOrg(), sfn.SegDest(), cc_f, cc_f_next, out p, true))
                        {
                            p.id = n + segIndex;
                            points[n + segIndex] = p;
                            segIndex++;

                            vpoints.Add(p);
                        }
                    }
                }
                else
                {
                    // Call Sf the constrained edge blinding f
                    sf.seg = subsegMap[f.triangle.hash];

                    sorg  = sf.SegOrg();
                    sdest = sf.SegDest();

                    // if f_next is tagged non-blind then
                    if (!f_next.triangle.infected)
                    {
                        tdest = f.Dest();
                        tapex = f.Apex();

                        // Both circumcenters lie on the blinded side, but we
                        // have to add the intersection with the segment.

                        // Center of f edge dest->apex
                        Point bisec = new Point((tdest.X + tapex.X) / 2, (tdest.Y + tapex.Y) / 2);

                        // Find intersection of seg with line through f's bisector and circumcenter
                        if (SegmentsIntersect(sorg, sdest, bisec, cc_f, out p, false))
                        {
                            p.id = n + segIndex;
                            points[n + segIndex] = p;
                            segIndex++;

                            vpoints.Add(p);
                        }

                        // Insert point Lf,f_next /\ Sf into P
                        if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true))
                        {
                            p.id = n + segIndex;
                            points[n + segIndex] = p;
                            segIndex++;

                            vpoints.Add(p);
                        }
                    }
                    else
                    {
                        // Call Sf_next the constrained edge blinding f_next
                        sfn.seg = subsegMap[f_next.triangle.hash];

                        // if Sf != Sf_next then
                        if (!sf.Equal(sfn))
                        {
                            // Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P
                            if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true))
                            {
                                p.id = n + segIndex;
                                points[n + segIndex] = p;
                                segIndex++;

                                vpoints.Add(p);
                            }

                            if (SegmentsIntersect(sfn.SegOrg(), sfn.SegDest(), cc_f, cc_f_next, out p, true))
                            {
                                p.id = n + segIndex;
                                points[n + segIndex] = p;
                                segIndex++;

                                vpoints.Add(p);
                            }
                        }
                        else
                        {
                            // Both circumcenters lie on the blinded side, but we
                            // have to add the intersection with the segment.

                            // Center of f_next edge org->dest
                            Point bisec = new Point((torg.X + tdest.X) / 2, (torg.Y + tdest.Y) / 2);

                            // Find intersection of seg with line through f_next's bisector and circumcenter
                            if (SegmentsIntersect(sorg, sdest, bisec, cc_f_next, out p, false))
                            {
                                p.id = n + segIndex;
                                points[n + segIndex] = p;
                                segIndex++;

                                vpoints.Add(p);
                            }
                        }
                    }
                }

                // f <- f_next
                f_next.Copy(ref f);

                // Call f_next the next triangle counterclockwise around x
                f_next.OnextSelf();
            }while (!f.Equal(f_init));

            // Output: Bounded Voronoi cell of x in counterclockwise order.
            region.Add(vpoints);
        }
Пример #8
0
        /// <summary>
        ///     Find the first triangle on the path from one point to another.
        /// </summary>
        /// <param name="searchtri"></param>
        /// <param name="searchpoint"></param>
        /// <returns>
        ///     The return value notes whether the destination or apex of the found
        ///     triangle is collinear with the two points in question.
        /// </returns>
        /// <remarks>
        ///     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.
        /// </remarks>
        private FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint)
        {
            Otri   checktri = default(Otri);
            Vertex startvertex;
            Vertex leftvertex, rightvertex;
            double leftccw, rightccw;
            bool   leftflag, rightflag;

            startvertex = searchtri.Org();
            rightvertex = searchtri.Dest();
            leftvertex  = searchtri.Apex();
            // Is 'searchpoint' to the left?
            leftccw  = RobustPredicates.CounterClockwise(searchpoint, startvertex, leftvertex);
            leftflag = leftccw > 0.0;
            // Is 'searchpoint' to the right?
            rightccw  = RobustPredicates.CounterClockwise(startvertex, searchpoint, rightvertex);
            rightflag = rightccw > 0.0;
            if (leftflag && rightflag)
            {
                // 'searchtri' faces directly away from 'searchpoint'. We could go left
                // or right. Ask whether it's a triangle or a boundary on the left.
                searchtri.Onext(ref checktri);
                if (checktri.tri.Id == Mesh.DUMMY)
                {
                    leftflag = false;
                }
                else
                {
                    rightflag = false;
                }
            }
            while (leftflag)
            {
                // Turn left until satisfied.
                searchtri.Onext();
                if (searchtri.tri.Id == Mesh.DUMMY)
                {
                    throw new Exception("Unable to find a triangle on path.");
                }
                leftvertex = searchtri.Apex();
                rightccw   = leftccw;
                leftccw    = RobustPredicates.CounterClockwise(searchpoint, startvertex, leftvertex);
                leftflag   = leftccw > 0.0;
            }
            while (rightflag)
            {
                // Turn right until satisfied.
                searchtri.Oprev();
                if (searchtri.tri.Id == Mesh.DUMMY)
                {
                    throw new Exception("Unable to find a triangle on path.");
                }
                rightvertex = searchtri.Dest();
                leftccw     = rightccw;
                rightccw    = RobustPredicates.CounterClockwise(startvertex, searchpoint, rightvertex);
                rightflag   = rightccw > 0.0;
            }
            if (leftccw == 0.0)
            {
                return(FindDirectionResult.Leftcollinear);
            }
            if (rightccw == 0.0)
            {
                return(FindDirectionResult.Rightcollinear);
            }
            return(FindDirectionResult.Within);
        }
Пример #9
0
        /// <summary>
        ///     Find the intersection of an existing segment and a segment that is being
        ///     inserted. Insert a vertex at the intersection, splitting an existing subsegment.
        /// </summary>
        /// <param name="splittri"></param>
        /// <param name="splitsubseg"></param>
        /// <param name="endpoint2"></param>
        /// <remarks>
        ///     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.
        /// </remarks>
        private void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2)
        {
            Osub               opposubseg = default(Osub);
            Vertex             endpoint1;
            Vertex             torg, tdest;
            Vertex             leftvertex, rightvertex;
            Vertex             newvertex;
            InsertVertexResult success;

            var dummysub = mesh.dummysub;

            double ex, ey;
            double tx, ty;
            double etx, ety;
            double split, denom;

            // Find the other three segment endpoints.
            endpoint1 = splittri.Apex();
            torg      = splittri.Org();
            tdest     = splittri.Dest();
            // Segment intersection formulae; see the Antonio reference.
            tx    = tdest.X - torg.X;
            ty    = tdest.Y - torg.Y;
            ex    = endpoint2.X - endpoint1.X;
            ey    = endpoint2.Y - endpoint1.Y;
            etx   = torg.X - endpoint2.X;
            ety   = torg.Y - endpoint2.Y;
            denom = ty * ex - tx * ey;
            if (denom == 0.0)
            {
                throw new Exception("Attempt to find intersection of parallel segments.");
            }
            split = (ey * etx - ex * ety) / denom;

            // Create the new vertex.
            newvertex = new Vertex(
                torg.X + split * (tdest.X - torg.X),
                torg.Y + split * (tdest.Y - torg.Y),
                splitsubseg.seg.boundary);

            newvertex.Id = mesh.hash_vtx++;

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

            // Insert the intersection vertex.  This should always succeed.
            success = mesh.InsertVertex(newvertex, ref splittri, ref splitsubseg, false, false);
            if (success != InsertVertexResult.Successful)
            {
                throw new Exception("Failure to split a segment.");
            }
            // Record a triangle whose origin is the new vertex.
            newvertex.tri = splittri;
            if (mesh.steinerleft > 0)
            {
                mesh.steinerleft--;
            }

            // Divide the segment into two, and correct the segment endpoints.
            splitsubseg.Sym();
            splitsubseg.Pivot(ref opposubseg);
            splitsubseg.Dissolve(dummysub);
            opposubseg.Dissolve(dummysub);
            do
            {
                splitsubseg.SetSegOrg(newvertex);
                splitsubseg.Next();
            } while (splitsubseg.seg.hash != Mesh.DUMMY);
            do
            {
                opposubseg.SetSegOrg(newvertex);
                opposubseg.Next();
            } while (opposubseg.seg.hash != Mesh.DUMMY);

            // Inserting the vertex may have caused edge flips.  We wish to rediscover
            // the edge connecting endpoint1 to the new intersection vertex.
            FindDirection(ref splittri, endpoint1);

            rightvertex = splittri.Dest();
            leftvertex  = splittri.Apex();
            if ((leftvertex.X == endpoint1.X) && (leftvertex.Y == endpoint1.Y))
            {
                splittri.Onext();
            }
            else if ((rightvertex.X != endpoint1.X) || (rightvertex.Y != endpoint1.Y))
            {
                throw new Exception("Topological inconsistency after splitting a segment.");
            }
            // 'splittri' should have destination endpoint1.
        }
Пример #10
0
        public bool CheckMesh()
        {
            Otri otri    = new Otri();
            Otri otri1   = new Otri();
            Otri otri2   = new Otri();
            bool noExact = Behavior.NoExact;

            Behavior.NoExact = false;
            int num = 0;

            foreach (Triangle value in this.mesh.triangles.Values)
            {
                otri.triangle = value;
                otri.orient   = 0;
                while (otri.orient < 3)
                {
                    Vertex vertex  = otri.Org();
                    Vertex vertex1 = otri.Dest();
                    if (otri.orient == 0 && Primitives.CounterClockwise(vertex, vertex1, otri.Apex()) <= 0)
                    {
                        this.logger.Warning("Triangle is flat or inverted.", "Quality.CheckMesh()");
                        num++;
                    }
                    otri.Sym(ref otri1);
                    if (otri1.triangle != Mesh.dummytri)
                    {
                        otri1.Sym(ref otri2);
                        if (otri.triangle != otri2.triangle || otri.orient != otri2.orient)
                        {
                            if (otri.triangle == otri2.triangle)
                            {
                                this.logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)", "Quality.CheckMesh()");
                            }
                            num++;
                        }
                        Vertex vertex2 = otri1.Org();
                        if (vertex != otri1.Dest() || vertex1 != vertex2)
                        {
                            this.logger.Warning("Mismatched edge coordinates between two triangles.", "Quality.CheckMesh()");
                            num++;
                        }
                    }
                    otri.orient = otri.orient + 1;
                }
            }
            this.mesh.MakeVertexMap();
            foreach (Vertex value1 in this.mesh.vertices.Values)
            {
                if (value1.tri.triangle != null)
                {
                    continue;
                }
                this.logger.Warning(string.Concat("Vertex (ID ", value1.id, ") not connected to mesh (duplicate input vertex?)"), "Quality.CheckMesh()");
            }
            if (num == 0)
            {
                this.logger.Info("Mesh topology appears to be consistent.");
            }
            Behavior.NoExact = noExact;
            return(num == 0);
        }
Пример #11
0
        private void ConstructBoundaryBvdCell(Vertex vertex)
        {
            Vertex        vertex1;
            Point         point;
            VoronoiRegion voronoiRegion = new VoronoiRegion(vertex);

            this.regions.Add(voronoiRegion);
            Otri         otri   = new Otri();
            Otri         otri1  = new Otri();
            Otri         otri2  = new Otri();
            Otri         otri3  = new Otri();
            Osub         item   = new Osub();
            Osub         osub   = new Osub();
            int          count  = this.mesh.triangles.Count;
            List <Point> points = new List <Point>();

            vertex.tri.Copy(ref otri1);
            if (otri1.Org() != vertex)
            {
                throw new Exception("ConstructBoundaryBvdCell: inconsistent topology.");
            }
            otri1.Copy(ref otri);
            otri1.Onext(ref otri2);
            otri1.Oprev(ref otri3);
            if (otri3.triangle != Mesh.dummytri)
            {
                while (otri3.triangle != Mesh.dummytri && !otri3.Equal(otri1))
                {
                    otri3.Copy(ref otri);
                    otri3.OprevSelf();
                }
                otri.Copy(ref otri1);
                otri.Onext(ref otri2);
            }
            if (otri3.triangle == Mesh.dummytri)
            {
                point = new Point(vertex.x, vertex.y)
                {
                    id = count + this.segIndex
                };
                this.points[count + this.segIndex] = point;
                this.segIndex = this.segIndex + 1;
                points.Add(point);
            }
            Vertex vertex2 = otri.Org();
            Vertex vertex3 = otri.Dest();

            point = new Point((vertex2.X + vertex3.X) / 2, (vertex2.Y + vertex3.Y) / 2)
            {
                id = count + this.segIndex
            };
            this.points[count + this.segIndex] = point;
            this.segIndex = this.segIndex + 1;
            points.Add(point);
            do
            {
                Point point1 = this.points[otri.triangle.id];
                if (otri2.triangle != Mesh.dummytri)
                {
                    Point point2 = this.points[otri2.triangle.id];
                    if (otri.triangle.infected)
                    {
                        item.seg = this.subsegMap[otri.triangle.hash];
                        Vertex vertex4 = item.SegOrg();
                        Vertex vertex5 = item.SegDest();
                        if (otri2.triangle.infected)
                        {
                            osub.seg = this.subsegMap[otri2.triangle.hash];
                            if (!item.Equal(osub))
                            {
                                if (this.SegmentsIntersect(vertex4, vertex5, point1, point2, out point, true))
                                {
                                    point.id = count + this.segIndex;
                                    this.points[count + this.segIndex] = point;
                                    this.segIndex = this.segIndex + 1;
                                    points.Add(point);
                                }
                                if (this.SegmentsIntersect(osub.SegOrg(), osub.SegDest(), point1, point2, out point, true))
                                {
                                    point.id = count + this.segIndex;
                                    this.points[count + this.segIndex] = point;
                                    this.segIndex = this.segIndex + 1;
                                    points.Add(point);
                                }
                            }
                            else if (this.SegmentsIntersect(vertex4, vertex5, new Point((vertex2.X + vertex3.X) / 2, (vertex2.Y + vertex3.Y) / 2), point2, out point, false))
                            {
                                point.id = count + this.segIndex;
                                this.points[count + this.segIndex] = point;
                                this.segIndex = this.segIndex + 1;
                                points.Add(point);
                            }
                        }
                        else
                        {
                            vertex3 = otri.Dest();
                            vertex1 = otri.Apex();
                            if (this.SegmentsIntersect(vertex4, vertex5, new Point((vertex3.X + vertex1.X) / 2, (vertex3.Y + vertex1.Y) / 2), point1, out point, false))
                            {
                                point.id = count + this.segIndex;
                                this.points[count + this.segIndex] = point;
                                this.segIndex = this.segIndex + 1;
                                points.Add(point);
                            }
                            if (this.SegmentsIntersect(vertex4, vertex5, point1, point2, out point, true))
                            {
                                point.id = count + this.segIndex;
                                this.points[count + this.segIndex] = point;
                                this.segIndex = this.segIndex + 1;
                                points.Add(point);
                            }
                        }
                    }
                    else
                    {
                        points.Add(point1);
                        if (otri2.triangle.infected)
                        {
                            osub.seg = this.subsegMap[otri2.triangle.hash];
                            if (this.SegmentsIntersect(osub.SegOrg(), osub.SegDest(), point1, point2, out point, true))
                            {
                                point.id = count + this.segIndex;
                                this.points[count + this.segIndex] = point;
                                this.segIndex = this.segIndex + 1;
                                points.Add(point);
                            }
                        }
                    }
                    otri2.Copy(ref otri);
                    otri2.OnextSelf();
                }
                else
                {
                    if (!otri.triangle.infected)
                    {
                        points.Add(point1);
                    }
                    vertex2 = otri.Org();
                    vertex1 = otri.Apex();
                    point   = new Point((vertex2.X + vertex1.X) / 2, (vertex2.Y + vertex1.Y) / 2)
                    {
                        id = count + this.segIndex
                    };
                    this.points[count + this.segIndex] = point;
                    this.segIndex = this.segIndex + 1;
                    points.Add(point);
                    break;
                }
            }while (!otri.Equal(otri1));
            voronoiRegion.Add(points);
        }
Пример #12
0
        public void TestTriangle(ref Otri testtri)
        {
            Vertex vertex;
            Vertex vertex1;
            double num;
            double num1;
            double num2;
            Otri   otri    = new Otri();
            Otri   otri1   = new Otri();
            Osub   osub    = new Osub();
            Vertex vertex2 = testtri.Org();
            Vertex vertex3 = testtri.Dest();
            Vertex vertex4 = testtri.Apex();
            double num3    = vertex2.x - vertex3.x;
            double num4    = vertex2.y - vertex3.y;
            double num5    = vertex3.x - vertex4.x;
            double num6    = vertex3.y - vertex4.y;
            double num7    = vertex4.x - vertex2.x;
            double num8    = vertex4.y - vertex2.y;
            double num9    = num3 * num3;
            double num10   = num4 * num4;
            double num11   = num5 * num5;
            double num12   = num6 * num6;
            double num13   = num8 * num8;
            double num14   = num9 + num10;
            double num15   = num11 + num12;
            double num16   = num7 * num7 + num13;

            if (num14 < num15 && num14 < num16)
            {
                num     = num14;
                num1    = num5 * num7 + num6 * num8;
                num1    = num1 * num1 / (num15 * num16);
                vertex  = vertex2;
                vertex1 = vertex3;
                testtri.Copy(ref otri);
            }
            else if (num15 >= num16)
            {
                num     = num16;
                num1    = num3 * num5 + num4 * num6;
                num1    = num1 * num1 / (num14 * num15);
                vertex  = vertex4;
                vertex1 = vertex2;
                testtri.Lprev(ref otri);
            }
            else
            {
                num     = num15;
                num1    = num3 * num7 + num4 * num8;
                num1    = num1 * num1 / (num14 * num16);
                vertex  = vertex3;
                vertex1 = vertex4;
                testtri.Lnext(ref otri);
            }
            if (this.behavior.VarArea || this.behavior.fixedArea || this.behavior.Usertest)
            {
                double num17 = 0.5 * (num3 * num6 - num4 * num5);
                if (this.behavior.fixedArea && num17 > this.behavior.MaxArea)
                {
                    this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3);
                    return;
                }
                if (this.behavior.VarArea && num17 > testtri.triangle.area && testtri.triangle.area > 0)
                {
                    this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3);
                    return;
                }
                if (this.behavior.Usertest && this.userTest != null && this.userTest(vertex2, vertex3, vertex4, num17))
                {
                    this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3);
                    return;
                }
            }
            if (num14 <= num15 || num14 <= num16)
            {
                num2 = (num15 <= num16 ? (num14 + num15 - num16) / (2 * Math.Sqrt(num14 * num15)) : (num14 + num16 - num15) / (2 * Math.Sqrt(num14 * num16)));
            }
            else
            {
                num2 = (num15 + num16 - num14) / (2 * Math.Sqrt(num15 * num16));
            }
            if (num1 > this.behavior.goodAngle || num2 < this.behavior.maxGoodAngle && this.behavior.MaxAngle != 0)
            {
                if (vertex.type == VertexType.SegmentVertex && vertex1.type == VertexType.SegmentVertex)
                {
                    otri.SegPivot(ref osub);
                    if (osub.seg == Mesh.dummysub)
                    {
                        otri.Copy(ref otri1);
                        do
                        {
                            otri.OprevSelf();
                            otri.SegPivot(ref osub);
                        }while (osub.seg == Mesh.dummysub);
                        Vertex vertex5 = osub.SegOrg();
                        Vertex vertex6 = osub.SegDest();
                        do
                        {
                            otri1.DnextSelf();
                            otri1.SegPivot(ref osub);
                        }while (osub.seg == Mesh.dummysub);
                        Vertex vertex7 = osub.SegOrg();
                        Vertex vertex8 = osub.SegDest();
                        Vertex vertex9 = null;
                        if (vertex6.x == vertex7.x && vertex6.y == vertex7.y)
                        {
                            vertex9 = vertex6;
                        }
                        else if (vertex5.x == vertex8.x && vertex5.y == vertex8.y)
                        {
                            vertex9 = vertex5;
                        }
                        if (vertex9 != null)
                        {
                            double num18 = (vertex.x - vertex9.x) * (vertex.x - vertex9.x) + (vertex.y - vertex9.y) * (vertex.y - vertex9.y);
                            double num19 = (vertex1.x - vertex9.x) * (vertex1.x - vertex9.x) + (vertex1.y - vertex9.y) * (vertex1.y - vertex9.y);
                            if (num18 < 1.001 * num19 && num18 > 0.999 * num19)
                            {
                                return;
                            }
                        }
                    }
                }
                this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3);
            }
        }
Пример #13
0
        private void SplitTriangle(BadTriangle badtri)
        {
            Point  point;
            Otri   otri = new Otri();
            double num  = 0;
            double num1 = 0;

            otri = badtri.poortri;
            Vertex vertex  = otri.Org();
            Vertex vertex1 = otri.Dest();
            Vertex vertex2 = otri.Apex();

            if (!Otri.IsDead(otri.triangle) && vertex == badtri.triangorg && vertex1 == badtri.triangdest && vertex2 == badtri.triangapex)
            {
                bool flag = false;
                point = (this.behavior.fixedArea || this.behavior.VarArea ? Primitives.FindCircumcenter(vertex, vertex1, vertex2, ref num, ref num1, this.behavior.offconstant) : this.newLocation.FindLocation(vertex, vertex1, vertex2, ref num, ref num1, true, otri));
                if ((point.x != vertex.x || point.y != vertex.y) && (point.x != vertex1.x || point.y != vertex1.y) && (point.x != vertex2.x || point.y != vertex2.y))
                {
                    Vertex vertex3 = new Vertex(point.x, point.y, 0, this.mesh.nextras)
                    {
                        type = VertexType.FreeVertex
                    };
                    for (int i = 0; i < this.mesh.nextras; i++)
                    {
                        vertex3.attributes[i] = vertex.attributes[i] + num * (vertex1.attributes[i] - vertex.attributes[i]) + num1 * (vertex2.attributes[i] - vertex.attributes[i]);
                    }
                    if (num1 < num)
                    {
                        otri.LprevSelf();
                    }
                    Osub osub = new Osub();
                    InsertVertexResult insertVertexResult = this.mesh.InsertVertex(vertex3, ref otri, ref osub, true, true);
                    if (insertVertexResult == InsertVertexResult.Successful)
                    {
                        Mesh mesh    = this.mesh;
                        int  hashVtx = mesh.hash_vtx;
                        mesh.hash_vtx = hashVtx + 1;
                        vertex3.hash  = hashVtx;
                        vertex3.id    = vertex3.hash;
                        this.mesh.vertices.Add(vertex3.hash, vertex3);
                        if (this.mesh.steinerleft > 0)
                        {
                            Mesh mesh1 = this.mesh;
                            mesh1.steinerleft = mesh1.steinerleft - 1;
                        }
                    }
                    else if (insertVertexResult == InsertVertexResult.Encroaching)
                    {
                        this.mesh.UndoVertex();
                    }
                    else if (insertVertexResult != InsertVertexResult.Violating && Behavior.Verbose)
                    {
                        this.logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()");
                        flag = true;
                    }
                }
                else if (Behavior.Verbose)
                {
                    this.logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()");
                    flag = true;
                }
                if (flag)
                {
                    this.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.");
                }
            }
        }
Пример #14
0
        private void SplitEncSegs(bool triflaws)
        {
            Vertex vertex;
            double num;
            Otri   otri  = new Otri();
            Otri   otri1 = new Otri();
            Osub   osub  = new Osub();
            Osub   osub1 = new Osub();

            while (this.badsubsegs.Count > 0 && this.mesh.steinerleft != 0)
            {
                BadSubseg badSubseg = this.badsubsegs.Dequeue();
                osub1 = badSubseg.encsubseg;
                Vertex vertex1 = osub1.Org();
                Vertex vertex2 = osub1.Dest();
                if (!Osub.IsDead(osub1.seg) && vertex1 == badSubseg.subsegorg && vertex2 == badSubseg.subsegdest)
                {
                    osub1.TriPivot(ref otri);
                    otri.Lnext(ref otri1);
                    otri1.SegPivot(ref osub);
                    bool flag = osub.seg != Mesh.dummysub;
                    otri1.LnextSelf();
                    otri1.SegPivot(ref osub);
                    bool flag1 = osub.seg != Mesh.dummysub;
                    if (!this.behavior.ConformingDelaunay && !flag && !flag1)
                    {
                        vertex = otri.Apex();
                        while (vertex.type == VertexType.FreeVertex && (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y) < 0)
                        {
                            this.mesh.DeleteVertex(ref otri1);
                            osub1.TriPivot(ref otri);
                            vertex = otri.Apex();
                            otri.Lprev(ref otri1);
                        }
                    }
                    otri.Sym(ref otri1);
                    if (otri1.triangle != Mesh.dummytri)
                    {
                        otri1.LnextSelf();
                        otri1.SegPivot(ref osub);
                        bool flag2 = osub.seg != Mesh.dummysub;
                        flag1 = flag1 | flag2;
                        otri1.LnextSelf();
                        otri1.SegPivot(ref osub);
                        bool flag3 = osub.seg != Mesh.dummysub;
                        flag = flag | flag3;
                        if (!this.behavior.ConformingDelaunay && !flag3 && !flag2)
                        {
                            vertex = otri1.Org();
                            while (vertex.type == VertexType.FreeVertex && (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y) < 0)
                            {
                                this.mesh.DeleteVertex(ref otri1);
                                otri.Sym(ref otri1);
                                vertex = otri1.Apex();
                                otri1.LprevSelf();
                            }
                        }
                    }
                    if (!(flag | flag1))
                    {
                        num = 0.5;
                    }
                    else
                    {
                        double num1 = Math.Sqrt((vertex2.x - vertex1.x) * (vertex2.x - vertex1.x) + (vertex2.y - vertex1.y) * (vertex2.y - vertex1.y));
                        double num2 = 1;
                        while (num1 > 3 * num2)
                        {
                            num2 = num2 * 2;
                        }
                        while (num1 < 1.5 * num2)
                        {
                            num2 = num2 * 0.5;
                        }
                        num = num2 / num1;
                        if (flag1)
                        {
                            num = 1 - num;
                        }
                    }
                    Vertex vertex3 = new Vertex(vertex1.x + num * (vertex2.x - vertex1.x), vertex1.y + num * (vertex2.y - vertex1.y), osub1.Mark(), this.mesh.nextras)
                    {
                        type = VertexType.SegmentVertex
                    };
                    Mesh mesh    = this.mesh;
                    int  hashVtx = mesh.hash_vtx;
                    mesh.hash_vtx = hashVtx + 1;
                    vertex3.hash  = hashVtx;
                    vertex3.id    = vertex3.hash;
                    this.mesh.vertices.Add(vertex3.hash, vertex3);
                    for (int i = 0; i < this.mesh.nextras; i++)
                    {
                        vertex3.attributes[i] = vertex1.attributes[i] + num * (vertex2.attributes[i] - vertex1.attributes[i]);
                    }
                    if (!Behavior.NoExact)
                    {
                        double num3 = Primitives.CounterClockwise(vertex1, vertex2, vertex3);
                        double num4 = (vertex1.x - vertex2.x) * (vertex1.x - vertex2.x) + (vertex1.y - vertex2.y) * (vertex1.y - vertex2.y);
                        if (num3 != 0 && num4 != 0)
                        {
                            num3 = num3 / num4;
                            if (!double.IsNaN(num3))
                            {
                                Vertex vertex4 = vertex3;
                                vertex4.x = vertex4.x + num3 * (vertex2.y - vertex1.y);
                                Vertex vertex5 = vertex3;
                                vertex5.y = vertex5.y + num3 * (vertex1.x - vertex2.x);
                            }
                        }
                    }
                    if (vertex3.x == vertex1.x && vertex3.y == vertex1.y || vertex3.x == vertex2.x && vertex3.y == vertex2.y)
                    {
                        this.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");
                    }
                    InsertVertexResult insertVertexResult = this.mesh.InsertVertex(vertex3, ref otri, ref osub1, true, triflaws);
                    if (insertVertexResult != InsertVertexResult.Successful && insertVertexResult != InsertVertexResult.Encroaching)
                    {
                        this.logger.Error("Failure to split a segment.", "Quality.SplitEncSegs()");
                        throw new Exception("Failure to split a segment.");
                    }
                    if (this.mesh.steinerleft > 0)
                    {
                        Mesh mesh1 = this.mesh;
                        mesh1.steinerleft = mesh1.steinerleft - 1;
                    }
                    this.CheckSeg4Encroach(ref osub1);
                    osub1.NextSelf();
                    this.CheckSeg4Encroach(ref osub1);
                }
                badSubseg.subsegorg = null;
            }
        }
Пример #15
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;
            double 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();
            }
        }
Пример #16
0
        /// <summary>
        ///     Enforce the Delaunay condition at an edge, fanning out recursively from
        ///     an existing vertex. Pay special attention to stacking inverted triangles.
        /// </summary>
        /// <param name="fixuptri"></param>
        /// <param name="leftside">
        ///     Indicates whether or not fixuptri is to the left of
        ///     the segment being inserted. (Imagine that the segment is pointing up from
        ///     endpoint1 to endpoint2.)
        /// </param>
        /// <remarks>
        ///     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.
        /// </remarks>
        private void DelaunayFixup(ref Otri fixuptri, bool leftside)
        {
            Otri   neartri = default(Otri);
            Otri   fartri = default(Otri);
            Osub   faredge = default(Osub);
            Vertex nearvertex, leftvertex, rightvertex, farvertex;

            fixuptri.Lnext(ref neartri);
            neartri.Sym(ref fartri);
            // Check if the edge opposite the origin of fixuptri can be flipped.
            if (fartri.tri.Id == Mesh.DUMMY)
            {
                return;
            }
            neartri.Pivot(ref faredge);
            if (faredge.seg.hash != Mesh.DUMMY)
            {
                return;
            }
            // Find all the relevant vertices.
            nearvertex  = neartri.Apex();
            leftvertex  = neartri.Org();
            rightvertex = neartri.Dest();
            farvertex   = fartri.Apex();
            // Check whether the previous polygon vertex is a reflex vertex.
            if (leftside)
            {
                if (RobustPredicates.CounterClockwise(nearvertex, leftvertex, farvertex) <= 0.0)
                {
                    // leftvertex is a reflex vertex too. Nothing can
                    // be done until a convex section is found.
                    return;
                }
            }
            else
            {
                if (RobustPredicates.CounterClockwise(farvertex, rightvertex, nearvertex) <= 0.0)
                {
                    // rightvertex is a reflex vertex too.  Nothing can
                    // be done until a convex section is found.
                    return;
                }
            }
            if (RobustPredicates.CounterClockwise(rightvertex, leftvertex, farvertex) > 0.0)
            {
                // fartri is not an inverted triangle, and farvertex is not a reflex
                // vertex.  As there are no reflex vertices, fixuptri isn't an
                // inverted triangle, either.  Hence, test the edge between the
                // triangles to ensure it is locally Delaunay.
                if (RobustPredicates.InCircle(leftvertex, farvertex, rightvertex, nearvertex) <= 0.0)
                {
                    return;
                }
                // Not locally Delaunay; go on to an edge flip.
            }
            // else fartri is inverted; remove it from the stack by flipping.
            mesh.Flip(ref neartri);
            fixuptri.Lprev(); // Restore the origin of fixuptri after the flip.
            // Recursively process the two triangles that result from the flip.
            DelaunayFixup(ref fixuptri, leftside);
            DelaunayFixup(ref fartri, leftside);
        }
Пример #17
0
        /// <summary>
        /// Write the Voronoi diagram to a .voro file.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="filename"></param>
        /// <returns></returns>
        /// <remarks>
        /// The Voronoi diagram is the geometric dual of the Delaunay triangulation.
        /// Hence, the Voronoi vertices are listed by traversing the Delaunay
        /// triangles, and the Voronoi edges are listed by traversing the Delaunay
        /// edges.
        ///
        /// WARNING:  In order to assign numbers to the Voronoi vertices, this
        /// procedure messes up the subsegments or the extra nodes of every
        /// element.  Hence, you should call this procedure last.</remarks>
        public static void WriteVoronoi(Mesh mesh, string filename)
        {
            Otri   tri = default(Otri), trisym = default(Otri);
            Vertex torg, tdest, tapex;
            Point  circumcenter;
            double xi = 0, eta = 0;

            int p1, p2, index = 0;

            tri.orient = 0;

            using (StreamWriter writer = new StreamWriter(filename))
            {
                // Number of triangles, two dimensions, number of vertex attributes, no markers.
                writer.WriteLine("{0} 2 {1} 0", mesh.triangles.Count, mesh.nextras);

                foreach (var item in mesh.triangles.Values)
                {
                    tri.triangle = item;
                    torg         = tri.Org();
                    tdest        = tri.Dest();
                    tapex        = tri.Apex();
                    circumcenter = Primitives.FindCircumcenter(torg, tdest, tapex, ref xi, ref eta);

                    // X and y coordinates.
                    writer.Write("{0} {1} {2}", index, circumcenter.X.ToString(nfi),
                                 circumcenter.Y.ToString(nfi));

                    for (int i = 0; i < mesh.nextras; i++)
                    {
                        writer.Write(" 0");
                        // TODO
                        // Interpolate the vertex attributes at the circumcenter.
                        //writer.Write(" {0}", torg.attribs[i] + xi * (tdes.attribst[i] - torg.attribs[i]) +
                        //    eta * (tapex.attribs[i] - torg.attribs[i]));
                    }
                    writer.WriteLine();

                    tri.triangle.id = index++;
                }


                // Number of edges, zero boundary markers.
                writer.WriteLine("{0} 0", mesh.edges);

                index = 0;
                // To loop over the set of edges, loop over all triangles, and look at
                // the three edges of each triangle.  If there isn't another triangle
                // adjacent to the edge, operate on the edge.  If there is another
                // adjacent triangle, operate on the edge only if the current triangle
                // has a smaller pointer than its neighbor.  This way, each edge is
                // considered only once.
                foreach (var item in mesh.triangles.Values)
                {
                    tri.triangle = item;

                    for (tri.orient = 0; tri.orient < 3; tri.orient++)
                    {
                        tri.Sym(ref trisym);
                        if ((tri.triangle.id < trisym.triangle.id) || (trisym.triangle == Mesh.dummytri))
                        {
                            // Find the number of this triangle (and Voronoi vertex).
                            p1 = tri.triangle.id;

                            if (trisym.triangle == Mesh.dummytri)
                            {
                                torg  = tri.Org();
                                tdest = tri.Dest();

                                // Write an infinite ray. Edge number, index of one endpoint,
                                // -1, and x and y coordinates of a vector representing the
                                // direction of the ray.
                                writer.WriteLine("{0} {1} -1 {2} {3}", index, p1,
                                                 (tdest[1] - torg[1]).ToString(nfi),
                                                 (torg[0] - tdest[0]).ToString(nfi));
                            }
                            else
                            {
                                // Find the number of the adjacent triangle (and Voronoi vertex).
                                p2 = trisym.triangle.id;
                                // Finite edge.  Write indices of two endpoints.
                                writer.WriteLine("{0} {1} {2}", index, p1, p2);
                            }

                            index++;
                        }
                    }
                }
            }
        }
Пример #18
0
        private void ConstructVoronoiRegion(Vertex vertex)
        {
            Vertex        vertex1;
            Vertex        vertex2;
            VoronoiRegion voronoiRegion = new VoronoiRegion(vertex);

            this.regions.Add(voronoiRegion);
            List <Point> points = new List <Point>();
            Otri         otri   = new Otri();
            Otri         otri1  = new Otri();
            Otri         otri2  = new Otri();
            Otri         otri3  = new Otri();
            Osub         osub   = new Osub();

            vertex.tri.Copy(ref otri1);
            otri1.Copy(ref otri);
            otri1.Onext(ref otri2);
            if (otri2.triangle == Mesh.dummytri)
            {
                otri1.Oprev(ref otri3);
                if (otri3.triangle != Mesh.dummytri)
                {
                    otri1.Copy(ref otri2);
                    otri1.OprevSelf();
                    otri1.Copy(ref otri);
                }
            }
            while (otri2.triangle != Mesh.dummytri)
            {
                points.Add(this.points[otri.triangle.id]);
                if (otri2.Equal(otri1))
                {
                    voronoiRegion.Add(points);
                    return;
                }
                otri2.Copy(ref otri);
                otri2.OnextSelf();
            }
            voronoiRegion.Bounded = false;
            int count = this.mesh.triangles.Count;

            otri.Lprev(ref otri2);
            otri2.SegPivot(ref osub);
            int num = osub.seg.hash;

            points.Add(this.points[otri.triangle.id]);
            if (!this.rayPoints.ContainsKey(num))
            {
                vertex1 = otri.Org();
                Vertex vertex3 = otri.Apex();
                this.BoxRayIntersection(this.points[otri.triangle.id], vertex1.y - vertex3.y, vertex3.x - vertex1.x, out vertex2);
                vertex2.id = count + this.rayIndex;
                this.points[count + this.rayIndex] = vertex2;
                this.rayIndex = this.rayIndex + 1;
                points.Add(vertex2);
                this.rayPoints.Add(num, vertex2);
            }
            else
            {
                points.Add(this.rayPoints[num]);
            }
            points.Reverse();
            otri1.Copy(ref otri);
            otri.Oprev(ref otri3);
            while (otri3.triangle != Mesh.dummytri)
            {
                points.Add(this.points[otri3.triangle.id]);
                otri3.Copy(ref otri);
                otri3.OprevSelf();
            }
            otri.SegPivot(ref osub);
            num = osub.seg.hash;
            if (!this.rayPoints.ContainsKey(num))
            {
                vertex1 = otri.Org();
                Vertex vertex4 = otri.Dest();
                this.BoxRayIntersection(this.points[otri.triangle.id], vertex4.y - vertex1.y, vertex1.x - vertex4.x, out vertex2);
                vertex2.id = count + this.rayIndex;
                this.points[count + this.rayIndex] = vertex2;
                this.rayIndex = this.rayIndex + 1;
                points.Add(vertex2);
                this.rayPoints.Add(num, vertex2);
            }
            else
            {
                points.Add(this.rayPoints[num]);
            }
            points.Reverse();
            voronoiRegion.Add(points);
        }
Пример #19
0
        public int Triangulate(Mesh mesh)
        {
            SweepLine.SweepEvent[] sweepEventArray;
            SweepLine.SweepEvent   sweepEvent;
            Vertex vertex;
            Vertex vertex1;
            Vertex vertex2;
            Vertex vertex3;

            this.mesh        = mesh;
            this.xminextreme = 10 * mesh.bounds.Xmin - 9 * mesh.bounds.Xmax;
            Otri otri  = new Otri();
            Otri otri1 = new Otri();
            Otri otri2 = new Otri();
            Otri otri3 = new Otri();
            Otri otri4 = new Otri();
            Otri otri5 = new Otri();
            Otri otri6 = new Otri();
            bool i     = false;

            this.splaynodes = new List <SweepLine.SplayNode>();
            SweepLine.SplayNode splayNode = null;
            this.CreateHeap(out sweepEventArray);
            int num = mesh.invertices;

            mesh.MakeTriangle(ref otri2);
            mesh.MakeTriangle(ref otri3);
            otri2.Bond(ref otri3);
            otri2.LnextSelf();
            otri3.LprevSelf();
            otri2.Bond(ref otri3);
            otri2.LnextSelf();
            otri3.LprevSelf();
            otri2.Bond(ref otri3);
            Vertex vertex4 = sweepEventArray[0].vertexEvent;

            this.HeapDelete(sweepEventArray, num, 0);
            num--;
            do
            {
                if (num == 0)
                {
                    SimpleLog.Instance.Error("Input vertices are all identical.", "SweepLine.SweepLineDelaunay()");
                    throw new Exception("Input vertices are all identical.");
                }
                vertex = sweepEventArray[0].vertexEvent;
                this.HeapDelete(sweepEventArray, num, 0);
                num--;
                if (vertex4.x != vertex.x || vertex4.y != vertex.y)
                {
                    continue;
                }
                if (Behavior.Verbose)
                {
                    SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().1");
                }
                vertex.type = VertexType.UndeadVertex;
                Mesh mesh1 = mesh;
                mesh1.undeads = mesh1.undeads + 1;
            }while (vertex4.x == vertex.x && vertex4.y == vertex.y);
            otri2.SetOrg(vertex4);
            otri2.SetDest(vertex);
            otri3.SetOrg(vertex);
            otri3.SetDest(vertex4);
            otri2.Lprev(ref otri);
            Vertex vertex5 = vertex;

            while (num > 0)
            {
                SweepLine.SweepEvent sweepEvent1 = sweepEventArray[0];
                this.HeapDelete(sweepEventArray, num, 0);
                num--;
                bool flag = true;
                if (sweepEvent1.xkey >= mesh.bounds.Xmin)
                {
                    Vertex vertex6 = sweepEvent1.vertexEvent;
                    if (vertex6.x != vertex5.x || vertex6.y != vertex5.y)
                    {
                        vertex5   = vertex6;
                        splayNode = this.FrontLocate(splayNode, otri, vertex6, ref otri1, ref i);
                        otri.Copy(ref otri1);
                        for (i = false; !i && this.RightOfHyperbola(ref otri1, vertex6); i = otri1.Equal(otri))
                        {
                            otri1.OnextSelf();
                        }
                        this.Check4DeadEvent(ref otri1, sweepEventArray, ref num);
                        otri1.Copy(ref otri5);
                        otri1.Sym(ref otri4);
                        mesh.MakeTriangle(ref otri2);
                        mesh.MakeTriangle(ref otri3);
                        Vertex vertex7 = otri5.Dest();
                        otri2.SetOrg(vertex7);
                        otri2.SetDest(vertex6);
                        otri3.SetOrg(vertex6);
                        otri3.SetDest(vertex7);
                        otri2.Bond(ref otri3);
                        otri2.LnextSelf();
                        otri3.LprevSelf();
                        otri2.Bond(ref otri3);
                        otri2.LnextSelf();
                        otri3.LprevSelf();
                        otri2.Bond(ref otri4);
                        otri3.Bond(ref otri5);
                        if (!i && otri5.Equal(otri))
                        {
                            otri2.Copy(ref otri);
                        }
                        if (this.randomnation(SweepLine.SAMPLERATE) == 0)
                        {
                            splayNode = this.SplayInsert(splayNode, otri2, vertex6);
                        }
                        else if (this.randomnation(SweepLine.SAMPLERATE) == 0)
                        {
                            otri3.Lnext(ref otri6);
                            splayNode = this.SplayInsert(splayNode, otri6, vertex6);
                        }
                    }
                    else
                    {
                        if (Behavior.Verbose)
                        {
                            SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().2");
                        }
                        vertex6.type = VertexType.UndeadVertex;
                        Mesh mesh2 = mesh;
                        mesh2.undeads = mesh2.undeads + 1;
                        flag          = false;
                    }
                }
                else
                {
                    Otri otri7 = sweepEvent1.otriEvent;
                    otri7.Oprev(ref otri4);
                    this.Check4DeadEvent(ref otri4, sweepEventArray, ref num);
                    otri7.Onext(ref otri5);
                    this.Check4DeadEvent(ref otri5, sweepEventArray, ref num);
                    if (otri4.Equal(otri))
                    {
                        otri7.Lprev(ref otri);
                    }
                    mesh.Flip(ref otri7);
                    otri7.SetApex(null);
                    otri7.Lprev(ref otri2);
                    otri7.Lnext(ref otri3);
                    otri2.Sym(ref otri4);
                    if (this.randomnation(SweepLine.SAMPLERATE) == 0)
                    {
                        otri7.SymSelf();
                        vertex1   = otri7.Dest();
                        vertex2   = otri7.Apex();
                        vertex3   = otri7.Org();
                        splayNode = this.CircleTopInsert(splayNode, otri2, vertex1, vertex2, vertex3, sweepEvent1.ykey);
                    }
                }
                if (!flag)
                {
                    continue;
                }
                vertex1 = otri4.Apex();
                vertex2 = otri2.Dest();
                vertex3 = otri2.Apex();
                double num1 = Primitives.CounterClockwise(vertex1, vertex2, vertex3);
                if (num1 > 0)
                {
                    sweepEvent = new SweepLine.SweepEvent()
                    {
                        xkey      = this.xminextreme,
                        ykey      = this.CircleTop(vertex1, vertex2, vertex3, num1),
                        otriEvent = otri2
                    };
                    this.HeapInsert(sweepEventArray, num, sweepEvent);
                    num++;
                    otri2.SetOrg(new SweepLine.SweepEventVertex(sweepEvent));
                }
                vertex1 = otri3.Apex();
                vertex2 = otri3.Org();
                vertex3 = otri5.Apex();
                double num2 = Primitives.CounterClockwise(vertex1, vertex2, vertex3);
                if (num2 <= 0)
                {
                    continue;
                }
                sweepEvent = new SweepLine.SweepEvent()
                {
                    xkey      = this.xminextreme,
                    ykey      = this.CircleTop(vertex1, vertex2, vertex3, num2),
                    otriEvent = otri5
                };
                this.HeapInsert(sweepEventArray, num, sweepEvent);
                num++;
                otri5.SetOrg(new SweepLine.SweepEventVertex(sweepEvent));
            }
            this.splaynodes.Clear();
            otri.LprevSelf();
            return(this.RemoveGhosts(ref otri));
        }
Пример #20
0
        private void GetAspectHistogram(Mesh mesh)
        {
            int[]    aspecttable;
            double[] ratiotable;

            aspecttable = new int[16];
            ratiotable  = new double[] {
                1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 10.0, 15.0, 25.0, 50.0,
                100.0, 300.0, 1000.0, 10000.0, 100000.0, 0.0
            };


            Otri tri = default(Otri);

            Vertex[] p          = new Vertex[3];
            double[] dx         = new double[3], dy = new double[3];
            double[] edgelength = new double[3];
            double   triarea;
            double   trilongest2;
            double   triminaltitude2;
            double   triaspect2;

            int aspectindex;
            int i, j, k;

            tri.orient = 0;
            foreach (var t in mesh.triangles.Values)
            {
                tri.triangle = t;
                p[0]         = tri.Org();
                p[1]         = tri.Dest();
                p[2]         = tri.Apex();
                trilongest2  = 0.0;

                for (i = 0; i < 3; i++)
                {
                    j             = plus1Mod3[i];
                    k             = minus1Mod3[i];
                    dx[i]         = p[j].x - p[k].x;
                    dy[i]         = p[j].y - p[k].y;
                    edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
                    if (edgelength[i] > trilongest2)
                    {
                        trilongest2 = edgelength[i];
                    }
                }

                //triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
                triarea = Math.Abs((p[2].x - p[0].x) * (p[1].y - p[0].y) -
                                   (p[1].x - p[0].x) * (p[2].y - p[0].y)) / 2.0;

                triminaltitude2 = triarea * triarea / trilongest2;

                triaspect2 = trilongest2 / triminaltitude2;

                aspectindex = 0;
                while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15))
                {
                    aspectindex++;
                }
                aspecttable[aspectindex]++;
            }
        }
Пример #21
0
        public IMesh Triangulate(IList <Vertex> points, Configuration config)
        {
            this.predicates = config.Predicates();

            this.mesh = new Mesh(config);
            this.mesh.TransferNodes(points);

            // Nonexistent x value used as a flag to mark circle events in sweepline
            // Delaunay algorithm.
            xminextreme = 10 * mesh.bounds.Left - 9 * mesh.bounds.Right;

            SweepEvent[] eventheap;

            SweepEvent nextevent;
            SweepEvent newevent;
            SplayNode  splayroot;
            Otri       bottommost = default(Otri);
            Otri       searchtri  = default(Otri);
            Otri       fliptri;
            Otri       lefttri = default(Otri);
            Otri       righttri = default(Otri);
            Otri       farlefttri = default(Otri);
            Otri       farrighttri = default(Otri);
            Otri       inserttri = default(Otri);
            Vertex     firstvertex, secondvertex;
            Vertex     nextvertex, lastvertex;
            Vertex     connectvertex;
            Vertex     leftvertex, midvertex, rightvertex;
            double     lefttest, righttest;
            int        heapsize;
            bool       check4events, farrightflag = false;

            splaynodes = new List <SplayNode>();
            splayroot  = null;

            heapsize = points.Count;
            CreateHeap(out eventheap, heapsize);//, out events, out freeevents);

            mesh.MakeTriangle(ref lefttri);
            mesh.MakeTriangle(ref righttri);
            lefttri.Bond(ref righttri);
            lefttri.Lnext();
            righttri.Lprev();
            lefttri.Bond(ref righttri);
            lefttri.Lnext();
            righttri.Lprev();
            lefttri.Bond(ref righttri);
            firstvertex = eventheap[0].vertexEvent;

            HeapDelete(eventheap, heapsize, 0);
            heapsize--;
            do
            {
                if (heapsize == 0)
                {
                    Log.Instance.Error("Input vertices are all identical.", "SweepLine.Triangulate()");
                    throw new Exception("Input vertices are all identical.");
                }
                secondvertex = eventheap[0].vertexEvent;
                HeapDelete(eventheap, heapsize, 0);
                heapsize--;
                if ((firstvertex.x == secondvertex.x) &&
                    (firstvertex.y == secondvertex.y))
                {
                    if (Log.Verbose)
                    {
                        Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + secondvertex.id + ").",
                                             "SweepLine.Triangulate().1");
                    }
                    secondvertex.type = VertexType.UndeadVertex;
                    mesh.undeads++;
                }
            } while ((firstvertex.x == secondvertex.x) &&
                     (firstvertex.y == secondvertex.y));
            lefttri.SetOrg(firstvertex);
            lefttri.SetDest(secondvertex);
            righttri.SetOrg(secondvertex);
            righttri.SetDest(firstvertex);
            lefttri.Lprev(ref bottommost);
            lastvertex = secondvertex;

            while (heapsize > 0)
            {
                nextevent = eventheap[0];
                HeapDelete(eventheap, heapsize, 0);
                heapsize--;
                check4events = true;
                if (nextevent.xkey < mesh.bounds.Left)
                {
                    fliptri = nextevent.otriEvent;
                    fliptri.Oprev(ref farlefttri);
                    Check4DeadEvent(ref farlefttri, eventheap, ref heapsize);
                    fliptri.Onext(ref farrighttri);
                    Check4DeadEvent(ref farrighttri, eventheap, ref heapsize);

                    if (farlefttri.Equals(bottommost))
                    {
                        fliptri.Lprev(ref bottommost);
                    }
                    mesh.Flip(ref fliptri);
                    fliptri.SetApex(null);
                    fliptri.Lprev(ref lefttri);
                    fliptri.Lnext(ref righttri);
                    lefttri.Sym(ref farlefttri);

                    if (randomnation(SAMPLERATE) == 0)
                    {
                        fliptri.Sym();
                        leftvertex  = fliptri.Dest();
                        midvertex   = fliptri.Apex();
                        rightvertex = fliptri.Org();
                        splayroot   = CircleTopInsert(splayroot, lefttri, leftvertex, midvertex, rightvertex, nextevent.ykey);
                    }
                }
                else
                {
                    nextvertex = nextevent.vertexEvent;
                    if ((nextvertex.x == lastvertex.x) &&
                        (nextvertex.y == lastvertex.y))
                    {
                        if (Log.Verbose)
                        {
                            Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + nextvertex.id + ").",
                                                 "SweepLine.Triangulate().2");
                        }
                        nextvertex.type = VertexType.UndeadVertex;
                        mesh.undeads++;
                        check4events = false;
                    }
                    else
                    {
                        lastvertex = nextvertex;

                        splayroot = FrontLocate(splayroot, bottommost, nextvertex, ref searchtri, ref farrightflag);

                        //bottommost.Copy(ref searchtri);
                        //farrightflag = false;
                        //while (!farrightflag && RightOfHyperbola(ref searchtri, nextvertex))
                        //{
                        //    searchtri.OnextSelf();
                        //    farrightflag = searchtri.Equal(bottommost);
                        //}

                        Check4DeadEvent(ref searchtri, eventheap, ref heapsize);

                        searchtri.Copy(ref farrighttri);
                        searchtri.Sym(ref farlefttri);
                        mesh.MakeTriangle(ref lefttri);
                        mesh.MakeTriangle(ref righttri);
                        connectvertex = farrighttri.Dest();
                        lefttri.SetOrg(connectvertex);
                        lefttri.SetDest(nextvertex);
                        righttri.SetOrg(nextvertex);
                        righttri.SetDest(connectvertex);
                        lefttri.Bond(ref righttri);
                        lefttri.Lnext();
                        righttri.Lprev();
                        lefttri.Bond(ref righttri);
                        lefttri.Lnext();
                        righttri.Lprev();
                        lefttri.Bond(ref farlefttri);
                        righttri.Bond(ref farrighttri);
                        if (!farrightflag && farrighttri.Equals(bottommost))
                        {
                            lefttri.Copy(ref bottommost);
                        }

                        if (randomnation(SAMPLERATE) == 0)
                        {
                            splayroot = SplayInsert(splayroot, lefttri, nextvertex);
                        }
                        else if (randomnation(SAMPLERATE) == 0)
                        {
                            righttri.Lnext(ref inserttri);
                            splayroot = SplayInsert(splayroot, inserttri, nextvertex);
                        }
                    }
                }

                if (check4events)
                {
                    leftvertex  = farlefttri.Apex();
                    midvertex   = lefttri.Dest();
                    rightvertex = lefttri.Apex();
                    lefttest    = predicates.CounterClockwise(leftvertex, midvertex, rightvertex);
                    if (lefttest > 0.0)
                    {
                        newevent = new SweepEvent();

                        newevent.xkey      = xminextreme;
                        newevent.ykey      = CircleTop(leftvertex, midvertex, rightvertex, lefttest);
                        newevent.otriEvent = lefttri;
                        HeapInsert(eventheap, heapsize, newevent);
                        heapsize++;
                        lefttri.SetOrg(new SweepEventVertex(newevent));
                    }
                    leftvertex  = righttri.Apex();
                    midvertex   = righttri.Org();
                    rightvertex = farrighttri.Apex();
                    righttest   = predicates.CounterClockwise(leftvertex, midvertex, rightvertex);
                    if (righttest > 0.0)
                    {
                        newevent = new SweepEvent();

                        newevent.xkey      = xminextreme;
                        newevent.ykey      = CircleTop(leftvertex, midvertex, rightvertex, righttest);
                        newevent.otriEvent = farrighttri;
                        HeapInsert(eventheap, heapsize, newevent);
                        heapsize++;
                        farrighttri.SetOrg(new SweepEventVertex(newevent));
                    }
                }
            }

            splaynodes.Clear();
            bottommost.Lprev();

            this.mesh.hullsize = RemoveGhosts(ref bottommost);

            return(this.mesh);
        }
Пример #22
0
        /// <summary>
        /// Ensure that the mesh is (constrained) Delaunay.
        /// </summary>
        private static bool IsDelaunay(Mesh mesh, bool constrained)
        {
            Otri   loop = default(Otri);
            Otri   oppotri = default(Otri);
            Osub   opposubseg = default(Osub);
            Vertex org, dest, apex;
            Vertex oppoapex;

            bool shouldbedelaunay;

            var logger = Log.Instance;

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

            Behavior.NoExact = false;

            int horrors = 0;

            var inf1 = mesh.infvertex1;
            var inf2 = mesh.infvertex2;
            var inf3 = mesh.infvertex3;

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

                // Check all three edges of the triangle.
                for (loop.orient = 0; loop.orient < 3; loop.orient++)
                {
                    org  = loop.Org();
                    dest = loop.Dest();
                    apex = 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 = (loop.tri.id < oppotri.tri.id) &&
                                       !Otri.IsDead(oppotri.tri) && (oppotri.tri.id != Mesh.DUMMY) &&
                                       (org != inf1) && (org != inf2) && (org != inf3) &&
                                       (dest != inf1) && (dest != inf2) && (dest != inf3) &&
                                       (apex != inf1) && (apex != inf2) && (apex != inf3) &&
                                       (oppoapex != inf1) && (oppoapex != inf2) && (oppoapex != inf3);

                    if (constrained && mesh.checksegments && shouldbedelaunay)
                    {
                        // If a subsegment separates the triangles, then the edge is
                        // constrained, so no local Delaunay test should be done.
                        loop.Pivot(ref opposubseg);

                        if (opposubseg.seg.hash != Mesh.DUMMY)
                        {
                            shouldbedelaunay = false;
                        }
                    }

                    if (shouldbedelaunay)
                    {
                        if (predicates.NonRegular(org, dest, apex, oppoapex) > 0.0)
                        {
                            if (Log.Verbose)
                            {
                                logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).",
                                                             loop.tri.id, oppotri.tri.id), "MeshValidator.IsDelaunay()");
                            }

                            horrors++;
                        }
                    }
                }
            }

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

            return(horrors == 0);
        }
Пример #23
0
        private void WriteMesh(TriangleNetMesh triangleNetMesh, bool skip)
        {
            // Mesh may have changed, but we choose to skip
            if (triangles == triangleNetMesh.triangles.Count && skip)
            {
                return;
            }

            // Header line
            stream.WriteLine("#!M{0}", iteration++);

            Vertex p1, p2, p3;

            if (VerticesChanged(triangleNetMesh))
            {
                HashVertices(triangleNetMesh);

                // Number of vertices.
                stream.WriteLine("{0}", triangleNetMesh.vertices.Count);

                foreach (var v in triangleNetMesh.vertices.Values)
                {
                    // Vertex number, x and y coordinates and marker.
                    stream.WriteLine("{0} {1} {2} {3}", v.id, v.x.ToString(nfi), v.y.ToString(nfi), v.label);
                }
            }
            else
            {
                stream.WriteLine("0");
            }

            // Number of segments.
            stream.WriteLine("{0}", triangleNetMesh.subsegs.Count);

            Osub subseg = default(Osub);

            subseg.orient = 0;

            foreach (var item in triangleNetMesh.subsegs.Values)
            {
                if (item.hash <= 0)
                {
                    continue;
                }

                subseg.seg = item;

                p1 = subseg.Org();
                p2 = subseg.Dest();

                // Segment number, indices of its two endpoints, and marker.
                stream.WriteLine("{0} {1} {2} {3}", subseg.seg.hash, p1.id, p2.id, subseg.seg.boundary);
            }

            Otri tri = default(Otri), trisym = default(Otri);

            tri.orient = 0;

            int n1, n2, n3, h1, h2, h3;

            // Number of triangles.
            stream.WriteLine("{0}", triangleNetMesh.triangles.Count);

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

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

                h1 = (p1 == null) ? -1 : p1.id;
                h2 = (p2 == null) ? -1 : p2.id;
                h3 = (p3 == null) ? -1 : p3.id;

                // Triangle number, indices for three vertices.
                stream.Write("{0} {1} {2} {3}", tri.tri.hash, h1, h2, h3);

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

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

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

                // Neighboring triangle numbers.
                stream.WriteLine(" {0} {1} {2}", n1, n2, n3);
            }
        }
Пример #24
0
        /// <summary>
        /// Test the mesh for topological consistency.
        /// </summary>
        public static bool IsConsistent(Mesh mesh)
        {
            Otri   tri = default(Otri);
            Otri   oppotri = default(Otri), oppooppotri = default(Otri);
            Vertex org, dest, apex;
            Vertex oppoorg, oppodest;

            var logger = Log.Instance;

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

            Behavior.NoExact = false;

            int horrors = 0;

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

                // Check all three edges of the triangle.
                for (tri.orient = 0; tri.orient < 3; tri.orient++)
                {
                    org  = tri.Org();
                    dest = tri.Dest();
                    if (tri.orient == 0)
                    {
                        // Only test for inversion once.
                        // Test if the triangle is flat or inverted.
                        apex = tri.Apex();
                        if (predicates.CounterClockwise(org, dest, apex) <= 0.0)
                        {
                            if (Log.Verbose)
                            {
                                logger.Warning(String.Format("Triangle is flat or inverted (ID {0}).", t.id),
                                               "MeshValidator.IsConsistent()");
                            }

                            horrors++;
                        }
                    }

                    // Find the neighboring triangle on this edge.
                    tri.Sym(ref oppotri);
                    if (oppotri.tri.id != Mesh.DUMMY)
                    {
                        // Check that the triangle's neighbor knows it's a neighbor.
                        oppotri.Sym(ref oppooppotri);
                        if ((tri.tri != oppooppotri.tri) || (tri.orient != oppooppotri.orient))
                        {
                            if (tri.tri == oppooppotri.tri && Log.Verbose)
                            {
                                logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)",
                                               "MeshValidator.IsConsistent()");
                            }

                            horrors++;
                        }
                        // Check that both triangles agree on the identities
                        // of their shared vertices.
                        oppoorg  = oppotri.Org();
                        oppodest = oppotri.Dest();
                        if ((org != oppodest) || (dest != oppoorg))
                        {
                            if (Log.Verbose)
                            {
                                logger.Warning("Mismatched edge coordinates between two triangles.",
                                               "MeshValidator.IsConsistent()");
                            }

                            horrors++;
                        }
                    }
                }
            }

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

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

            return(horrors == 0);
        }
Пример #25
0
        /// <summary>
        /// Check a subsegment to see if it is encroached; add it to the list if it is.
        /// </summary>
        /// <param name="testsubseg">The subsegment to check.</param>
        /// <returns>Returns a nonzero value if the subsegment is encroached.</returns>
        /// <remarks>
        /// A subsegment is encroached if there is a vertex in its diametral lens.
        /// For Ruppert's algorithm (-D switch), the "diametral lens" is the
        /// diametral circle. For Chew's algorithm (default), the diametral lens is
        /// just big enough to enclose two isosceles triangles whose bases are the
        /// subsegment. Each of the two isosceles triangles has two angles equal
        /// to 'b.minangle'.
        ///
        /// Chew's algorithm does not require diametral lenses at all--but they save
        /// time. Any vertex inside a subsegment's diametral lens implies that the
        /// triangle adjoining the subsegment will be too skinny, so it's only a
        /// matter of time before the encroaching vertex is deleted by Chew's
        /// algorithm. It's faster to simply not insert the doomed vertex in the
        /// first place, which is why I use diametral lenses with Chew's algorithm.
        /// </remarks>
        public int CheckSeg4Encroach(ref Osub testsubseg)
        {
            Otri      neighbortri = default(Otri);
            Osub      testsym     = default(Osub);
            BadSubseg encroachedseg;
            float     dotproduct;
            int       encroached;
            int       sides;
            Vertex    eorg, edest, eapex;

            encroached = 0;
            sides      = 0;

            eorg  = testsubseg.Org();
            edest = testsubseg.Dest();
            // Check one neighbor of the subsegment.
            testsubseg.TriPivot(ref neighbortri);
            // Does the neighbor exist, or is this a boundary edge?
            if (neighbortri.triangle != Mesh.dummytri)
            {
                sides++;
                // Find a vertex opposite this subsegment.
                eapex = neighbortri.Apex();
                // Check whether the apex is in the diametral lens of the subsegment
                // (the diametral circle if 'conformdel' is set).  A dot product
                // of two sides of the triangle is used to check whether the angle
                // at the apex is greater than (180 - 2 'minangle') degrees (for
                // lenses; 90 degrees for diametral circles).
                dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) +
                             (eorg.y - eapex.y) * (edest.y - eapex.y);
                if (dotproduct < 0.0)
                {
                    if (behavior.ConformingDelaunay ||
                        (dotproduct * dotproduct >=
                         (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) *
                         ((eorg.x - eapex.x) * (eorg.x - eapex.x) +
                          (eorg.y - eapex.y) * (eorg.y - eapex.y)) *
                         ((edest.x - eapex.x) * (edest.x - eapex.x) +
                          (edest.y - eapex.y) * (edest.y - eapex.y))))
                    {
                        encroached = 1;
                    }
                }
            }
            // Check the other neighbor of the subsegment.
            testsubseg.Sym(ref testsym);
            testsym.TriPivot(ref neighbortri);
            // Does the neighbor exist, or is this a boundary edge?
            if (neighbortri.triangle != Mesh.dummytri)
            {
                sides++;
                // Find the other vertex opposite this subsegment.
                eapex = neighbortri.Apex();
                // Check whether the apex is in the diametral lens of the subsegment
                // (or the diametral circle, if 'conformdel' is set).
                dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) +
                             (eorg.y - eapex.y) * (edest.y - eapex.y);
                if (dotproduct < 0.0)
                {
                    if (behavior.ConformingDelaunay ||
                        (dotproduct * dotproduct >=
                         (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) *
                         ((eorg.x - eapex.x) * (eorg.x - eapex.x) +
                          (eorg.y - eapex.y) * (eorg.y - eapex.y)) *
                         ((edest.x - eapex.x) * (edest.x - eapex.x) +
                          (edest.y - eapex.y) * (edest.y - eapex.y))))
                    {
                        encroached += 2;
                    }
                }
            }

            if (encroached > 0 && (behavior.NoBisect == 0 || ((behavior.NoBisect == 1) && (sides == 2))))
            {
                // Add the subsegment to the list of encroached subsegments.
                // Be sure to get the orientation right.
                encroachedseg = new BadSubseg();
                if (encroached == 1)
                {
                    encroachedseg.encsubseg  = testsubseg;
                    encroachedseg.subsegorg  = eorg;
                    encroachedseg.subsegdest = edest;
                }
                else
                {
                    encroachedseg.encsubseg  = testsym;
                    encroachedseg.subsegorg  = edest;
                    encroachedseg.subsegdest = eorg;
                }

                badsubsegs.Enqueue(encroachedseg);
            }

            return(encroached);
        }
Пример #26
0
        private void WriteMesh(Mesh mesh, bool skip)
        {
            Vertex vertex;
            Vertex vertex1;

            if (this.triangles == mesh.triangles.Count & skip)
            {
                return;
            }
            StreamWriter streamWriter = this.stream;
            int          num          = this.iteration;

            this.iteration = num + 1;
            streamWriter.WriteLine("#!M{0}", num);
            if (!this.VerticesChanged(mesh))
            {
                this.stream.WriteLine("0");
            }
            else
            {
                this.HashVertices(mesh);
                this.stream.WriteLine("{0}", mesh.vertices.Count);
                foreach (Vertex value in mesh.vertices.Values)
                {
                    this.stream.WriteLine("{0} {1} {2} {3}", new object[] { value.hash, value.x.ToString(DebugWriter.nfi), value.y.ToString(DebugWriter.nfi), value.mark });
                }
            }
            this.stream.WriteLine("{0}", mesh.subsegs.Count);
            Osub osub = new Osub()
            {
                orient = 0
            };

            foreach (Segment segment in mesh.subsegs.Values)
            {
                if (segment.hash <= 0)
                {
                    continue;
                }
                osub.seg = segment;
                vertex   = osub.Org();
                vertex1  = osub.Dest();
                this.stream.WriteLine("{0} {1} {2} {3}", new object[] { osub.seg.hash, vertex.hash, vertex1.hash, osub.seg.boundary });
            }
            Otri otri  = new Otri();
            Otri otri1 = new Otri();

            otri.orient = 0;
            this.stream.WriteLine("{0}", mesh.triangles.Count);
            foreach (Triangle triangle in mesh.triangles.Values)
            {
                otri.triangle = triangle;
                vertex        = otri.Org();
                vertex1       = otri.Dest();
                Vertex vertex2 = otri.Apex();
                int    num1    = (vertex == null ? -1 : vertex.hash);
                int    num2    = (vertex1 == null ? -1 : vertex1.hash);
                int    num3    = (vertex2 == null ? -1 : vertex2.hash);
                this.stream.Write("{0} {1} {2} {3}", new object[] { otri.triangle.hash, num1, num2, num3 });
                otri.orient = 1;
                otri.Sym(ref otri1);
                int num4 = otri1.triangle.hash;
                otri.orient = 2;
                otri.Sym(ref otri1);
                int num5 = otri1.triangle.hash;
                otri.orient = 0;
                otri.Sym(ref otri1);
                int num6 = otri1.triangle.hash;
                this.stream.WriteLine(" {0} {1} {2}", num4, num5, num6);
            }
        }
Пример #27
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);
        }
Пример #28
0
        /// <summary>
        /// Merge two adjacent Delaunay triangulations into a single Delaunay triangulation.
        /// </summary>
        /// <param name="farleft">Bounding triangles of the left triangulation.</param>
        /// <param name="innerleft">Bounding triangles of the left triangulation.</param>
        /// <param name="innerright">Bounding triangles of the right triangulation.</param>
        /// <param name="farright">Bounding triangles of the right triangulation.</param>
        /// <param name="axis"></param>
        /// <remarks>
        /// This is similar to the algorithm given by Guibas and Stolfi, but uses
        /// a triangle-based, rather than edge-based, data structure.
        ///
        /// The algorithm walks up the gap between the two triangulations, knitting
        /// them together.  As they are merged, some of their bounding triangles
        /// are converted into real triangles of the triangulation.  The procedure
        /// pulls each hull's bounding triangles apart, then knits them together
        /// like the teeth of two gears.  The Delaunay property determines, at each
        /// step, whether the next "tooth" is a bounding triangle of the left hull
        /// or the right.  When a bounding triangle becomes real, its apex is
        /// changed from NULL to a real vertex.
        ///
        /// Only two new triangles need to be allocated.  These become new bounding
        /// triangles at the top and bottom of the seam.  They are used to connect
        /// the remaining bounding triangles (those that have not been converted
        /// into real triangles) into a single fan.
        ///
        /// On entry, 'farleft' and 'innerleft' are bounding triangles of the left
        /// triangulation.  The origin of 'farleft' is the leftmost vertex, and
        /// the destination of 'innerleft' is the rightmost vertex of the
        /// triangulation.  Similarly, 'innerright' and 'farright' are bounding
        /// triangles of the right triangulation.  The origin of 'innerright' and
        /// destination of 'farright' are the leftmost and rightmost vertices.
        ///
        /// On completion, the origin of 'farleft' is the leftmost vertex of the
        /// merged triangulation, and the destination of 'farright' is the rightmost
        /// vertex.
        /// </remarks>
        void MergeHulls(ref Otri farleft, ref Otri innerleft, ref Otri innerright,
                        ref Otri farright, int axis)
        {
            Otri   leftcand = default(Otri), rightcand = default(Otri);
            Otri   nextedge = default(Otri);
            Otri   sidecasing = default(Otri), topcasing = default(Otri), outercasing = default(Otri);
            Otri   checkedge = default(Otri);
            Otri   baseedge  = default(Otri);
            Vertex innerleftdest;
            Vertex innerrightorg;
            Vertex innerleftapex, innerrightapex;
            Vertex farleftpt, farrightpt;
            Vertex farleftapex, farrightapex;
            Vertex lowerleft, lowerright;
            Vertex upperleft, upperright;
            Vertex nextapex;
            Vertex checkvertex;
            bool   changemade;
            bool   badedge;
            bool   leftfinished, rightfinished;

            innerleftdest  = innerleft.Dest();
            innerleftapex  = innerleft.Apex();
            innerrightorg  = innerright.Org();
            innerrightapex = innerright.Apex();
            // Special treatment for horizontal cuts.
            if (useDwyer && (axis == 1))
            {
                farleftpt    = farleft.Org();
                farleftapex  = farleft.Apex();
                farrightpt   = farright.Dest();
                farrightapex = farright.Apex();
                // The pointers to the extremal vertices are shifted to point to the
                // topmost and bottommost vertex of each hull, rather than the
                // leftmost and rightmost vertices.
                while (farleftapex.y < farleftpt.y)
                {
                    farleft.LnextSelf();
                    farleft.SymSelf();
                    farleftpt   = farleftapex;
                    farleftapex = farleft.Apex();
                }

                innerleft.Sym(ref checkedge);
                checkvertex = checkedge.Apex();
                while (checkvertex.y > innerleftdest.y)
                {
                    checkedge.Lnext(ref innerleft);
                    innerleftapex = innerleftdest;
                    innerleftdest = checkvertex;
                    innerleft.Sym(ref checkedge);
                    checkvertex = checkedge.Apex();
                }

                while (innerrightapex.y < innerrightorg.y)
                {
                    innerright.LnextSelf();
                    innerright.SymSelf();
                    innerrightorg  = innerrightapex;
                    innerrightapex = innerright.Apex();
                }

                farright.Sym(ref checkedge);
                checkvertex = checkedge.Apex();
                while (checkvertex.y > farrightpt.y)
                {
                    checkedge.Lnext(ref farright);
                    farrightapex = farrightpt;
                    farrightpt   = checkvertex;
                    farright.Sym(ref checkedge);
                    checkvertex = checkedge.Apex();
                }
            }

            // Find a line tangent to and below both hulls.
            do
            {
                changemade = false;
                // Make innerleftdest the "bottommost" vertex of the left hull.
                if (Primitives.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0)
                {
                    innerleft.LprevSelf();
                    innerleft.SymSelf();
                    innerleftdest = innerleftapex;
                    innerleftapex = innerleft.Apex();
                    changemade    = true;
                }

                // Make innerrightorg the "bottommost" vertex of the right hull.
                if (Primitives.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0)
                {
                    innerright.LnextSelf();
                    innerright.SymSelf();
                    innerrightorg  = innerrightapex;
                    innerrightapex = innerright.Apex();
                    changemade     = true;
                }
            } while (changemade);

            // Find the two candidates to be the next "gear tooth."
            innerleft.Sym(ref leftcand);
            innerright.Sym(ref rightcand);
            // Create the bottom new bounding triangle.
            mesh.MakeTriangle(ref baseedge);
            // Connect it to the bounding boxes of the left and right triangulations.
            baseedge.Bond(ref innerleft);
            baseedge.LnextSelf();
            baseedge.Bond(ref innerright);
            baseedge.LnextSelf();
            baseedge.SetOrg(innerrightorg);
            baseedge.SetDest(innerleftdest);
            // Apex is intentionally left NULL.

            // Fix the extreme triangles if necessary.
            farleftpt = farleft.Org();
            if (innerleftdest == farleftpt)
            {
                baseedge.Lnext(ref farleft);
            }

            farrightpt = farright.Dest();
            if (innerrightorg == farrightpt)
            {
                baseedge.Lprev(ref farright);
            }

            // The vertices of the current knitting edge.
            lowerleft  = innerleftdest;
            lowerright = innerrightorg;
            // The candidate vertices for knitting.
            upperleft  = leftcand.Apex();
            upperright = rightcand.Apex();
            // Walk up the gap between the two triangulations, knitting them together.
            while (true)
            {
                // Have we reached the top? (This isn't quite the right question,
                // because even though the left triangulation might seem finished now,
                // moving up on the right triangulation might reveal a new vertex of
                // the left triangulation. And vice-versa.)
                leftfinished  = Primitives.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0;
                rightfinished = Primitives.CounterClockwise(upperright, lowerleft, lowerright) <= 0.0;
                if (leftfinished && rightfinished)
                {
                    // Create the top new bounding triangle.
                    mesh.MakeTriangle(ref nextedge);
                    nextedge.SetOrg(lowerleft);
                    nextedge.SetDest(lowerright);
                    // Apex is intentionally left NULL.
                    // Connect it to the bounding boxes of the two triangulations.
                    nextedge.Bond(ref baseedge);
                    nextedge.LnextSelf();
                    nextedge.Bond(ref rightcand);
                    nextedge.LnextSelf();
                    nextedge.Bond(ref leftcand);

                    // Special treatment for horizontal cuts.
                    if (useDwyer && (axis == 1))
                    {
                        farleftpt    = farleft.Org();
                        farleftapex  = farleft.Apex();
                        farrightpt   = farright.Dest();
                        farrightapex = farright.Apex();
                        farleft.Sym(ref checkedge);
                        checkvertex = checkedge.Apex();
                        // The pointers to the extremal vertices are restored to the
                        // leftmost and rightmost vertices (rather than topmost and
                        // bottommost).
                        while (checkvertex.x < farleftpt.x)
                        {
                            checkedge.Lprev(ref farleft);
                            farleftapex = farleftpt;
                            farleftpt   = checkvertex;
                            farleft.Sym(ref checkedge);
                            checkvertex = checkedge.Apex();
                        }

                        while (farrightapex.x > farrightpt.x)
                        {
                            farright.LprevSelf();
                            farright.SymSelf();
                            farrightpt   = farrightapex;
                            farrightapex = farright.Apex();
                        }
                    }

                    return;
                }

                // Consider eliminating edges from the left triangulation.
                if (!leftfinished)
                {
                    // What vertex would be exposed if an edge were deleted?
                    leftcand.Lprev(ref nextedge);
                    nextedge.SymSelf();
                    nextapex = nextedge.Apex();
                    // If nextapex is NULL, then no vertex would be exposed; the
                    // triangulation would have been eaten right through.
                    if (nextapex != null)
                    {
                        // Check whether the edge is Delaunay.
                        badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
                        while (badedge)
                        {
                            // Eliminate the edge with an edge flip.  As a result, the
                            // left triangulation will have one more boundary triangle.
                            nextedge.LnextSelf();
                            nextedge.Sym(ref topcasing);
                            nextedge.LnextSelf();
                            nextedge.Sym(ref sidecasing);
                            nextedge.Bond(ref topcasing);
                            leftcand.Bond(ref sidecasing);
                            leftcand.LnextSelf();
                            leftcand.Sym(ref outercasing);
                            nextedge.LprevSelf();
                            nextedge.Bond(ref outercasing);
                            // Correct the vertices to reflect the edge flip.
                            leftcand.SetOrg(lowerleft);
                            leftcand.SetDest(null);
                            leftcand.SetApex(nextapex);
                            nextedge.SetOrg(null);
                            nextedge.SetDest(upperleft);
                            nextedge.SetApex(nextapex);
                            // Consider the newly exposed vertex.
                            upperleft = nextapex;
                            // What vertex would be exposed if another edge were deleted?
                            sidecasing.Copy(ref nextedge);
                            nextapex = nextedge.Apex();
                            if (nextapex != null)
                            {
                                // Check whether the edge is Delaunay.
                                badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
                            }
                            else
                            {
                                // Avoid eating right through the triangulation.
                                badedge = false;
                            }
                        }
                    }
                }

                // Consider eliminating edges from the right triangulation.
                if (!rightfinished)
                {
                    // What vertex would be exposed if an edge were deleted?
                    rightcand.Lnext(ref nextedge);
                    nextedge.SymSelf();
                    nextapex = nextedge.Apex();
                    // If nextapex is NULL, then no vertex would be exposed; the
                    // triangulation would have been eaten right through.
                    if (nextapex != null)
                    {
                        // Check whether the edge is Delaunay.
                        badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
                        while (badedge)
                        {
                            // Eliminate the edge with an edge flip.  As a result, the
                            // right triangulation will have one more boundary triangle.
                            nextedge.LprevSelf();
                            nextedge.Sym(ref topcasing);
                            nextedge.LprevSelf();
                            nextedge.Sym(ref sidecasing);
                            nextedge.Bond(ref topcasing);
                            rightcand.Bond(ref sidecasing);
                            rightcand.LprevSelf();
                            rightcand.Sym(ref outercasing);
                            nextedge.LnextSelf();
                            nextedge.Bond(ref outercasing);
                            // Correct the vertices to reflect the edge flip.
                            rightcand.SetOrg(null);
                            rightcand.SetDest(lowerright);
                            rightcand.SetApex(nextapex);
                            nextedge.SetOrg(upperright);
                            nextedge.SetDest(null);
                            nextedge.SetApex(nextapex);
                            // Consider the newly exposed vertex.
                            upperright = nextapex;
                            // What vertex would be exposed if another edge were deleted?
                            sidecasing.Copy(ref nextedge);
                            nextapex = nextedge.Apex();
                            if (nextapex != null)
                            {
                                // Check whether the edge is Delaunay.
                                badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
                            }
                            else
                            {
                                // Avoid eating right through the triangulation.
                                badedge = false;
                            }
                        }
                    }
                }

                if (leftfinished || (!rightfinished &&
                                     (Primitives.InCircle(upperleft, lowerleft, lowerright, upperright) > 0.0)))
                {
                    // Knit the triangulations, adding an edge from 'lowerleft'
                    // to 'upperright'.
                    baseedge.Bond(ref rightcand);
                    rightcand.Lprev(ref baseedge);
                    baseedge.SetDest(lowerleft);
                    lowerright = upperright;
                    baseedge.Sym(ref rightcand);
                    upperright = rightcand.Apex();
                }
                else
                {
                    // Knit the triangulations, adding an edge from 'upperleft'
                    // to 'lowerright'.
                    baseedge.Bond(ref leftcand);
                    leftcand.Lnext(ref baseedge);
                    baseedge.SetOrg(lowerright);
                    lowerleft = upperleft;
                    baseedge.Sym(ref leftcand);
                    upperleft = leftcand.Apex();
                }
            }
        }
Пример #29
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.");
                }
            }
        }
Пример #30
0
        /// <summary>
        /// Finds the adjacencies between triangles by forming a stack of triangles for
        /// each vertex. Each triangle is on three different stacks simultaneously.
        /// </summary>
        private static List <Otri>[] SetNeighbors(Mesh mesh, ITriangle[] triangles)
        {
            Otri    tri          = default(Otri);
            Otri    triangleleft = default(Otri);
            Otri    checktri     = default(Otri);
            Otri    checkleft    = default(Otri);
            Otri    nexttri;
            TVertex tdest, tapex;
            TVertex checkdest, checkapex;

            int[] corner = new int[3];
            int   aroundvertex;
            int   i;

            // Allocate a temporary array that maps each vertex to some adjacent triangle.
            var vertexarray = new List <Otri> [mesh.vertices.Count];

            // Each vertex is initially unrepresented.
            for (i = 0; i < mesh.vertices.Count; i++)
            {
                Otri tmp = default(Otri);
                tmp.tri        = mesh.dummytri;
                vertexarray[i] = new List <Otri>(3);
                vertexarray[i].Add(tmp);
            }

            i = 0;

            // Read the triangles from the .ele file, and link
            // together those that share an edge.
            foreach (var item in mesh.triangles)
            {
                tri.tri = item;

                // Copy the triangle's three corners.
                for (int j = 0; j < 3; j++)
                {
                    corner[j] = triangles[i].GetVertexID(j);

                    if ((corner[j] < 0) || (corner[j] >= mesh.invertices))
                    {
                        Log.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()");
                        throw new Exception("Triangle has an invalid vertex index.");
                    }
                }

                // Read the triangle's attributes.
                tri.tri.label = triangles[i].Label;

                // TODO: VarArea
                if (mesh.behavior.VarArea)
                {
                    tri.tri.area = triangles[i].Area;
                }

                // Set the triangle's vertices.
                tri.orient = 0;
                tri.SetOrg(mesh.vertices[corner[0]]);
                tri.SetDest(mesh.vertices[corner[1]]);
                tri.SetApex(mesh.vertices[corner[2]]);

                // Try linking the triangle to others that share these vertices.
                for (tri.orient = 0; tri.orient < 3; tri.orient++)
                {
                    // Take the number for the origin of triangleloop.
                    aroundvertex = corner[tri.orient];

                    int index = vertexarray[aroundvertex].Count - 1;

                    // Look for other triangles having this vertex.
                    nexttri = vertexarray[aroundvertex][index];

                    // Push the current triangle onto the stack.
                    vertexarray[aroundvertex].Add(tri);

                    checktri = nexttri;

                    if (checktri.tri.id != Mesh.DUMMY)
                    {
                        tdest = tri.Dest();
                        tapex = tri.Apex();

                        // Look for other triangles that share an edge.
                        do
                        {
                            checkdest = checktri.Dest();
                            checkapex = checktri.Apex();

                            if (tapex == checkdest)
                            {
                                // The two triangles share an edge; bond them together.
                                tri.Lprev(ref triangleleft);
                                triangleleft.Bond(ref checktri);
                            }
                            if (tdest == checkapex)
                            {
                                // The two triangles share an edge; bond them together.
                                checktri.Lprev(ref checkleft);
                                tri.Bond(ref checkleft);
                            }
                            // Find the next triangle in the stack.
                            index--;
                            nexttri = vertexarray[aroundvertex][index];

                            checktri = nexttri;
                        }while (checktri.tri.id != Mesh.DUMMY);
                    }
                }

                i++;
            }

            return(vertexarray);
        }