/// <summary>
        /// Form a Delaunay triangulation by incrementally inserting vertices.
        /// </summary>
        /// <returns>Returns the number of edges on the convex hull of the 
        /// triangulation.</returns>
        public IMesh Triangulate(IList<Vertex> points, Configuration config)
        {
            this.mesh = new Mesh(config);
            this.mesh.TransferNodes(points);

            Otri starttri = new Otri();

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

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

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

            return this.mesh;
        }
        private void FormTopology_Load(object sender, EventArgs e)
        {
            mesh = (Mesh)GenericMesher.StructuredMesh(new Rectangle(0.0, 0.0, 4.0, 4.0), 4, 4);

            renderControl.Initialize(mesh);

            topoControlView.PrimitiveCommandInvoked += PrimitiveCommandHandler;

            current = default(Otri);
        }
Example #3
0
        /// <summary>
        /// Find a new location for a Steiner point.
        /// </summary>
        /// <param name="org"></param>
        /// <param name="dest"></param>
        /// <param name="apex"></param>
        /// <param name="xi"></param>
        /// <param name="eta"></param>
        /// <param name="offcenter"></param>
        /// <param name="badotri"></param>
        /// <returns></returns>
        public Point FindLocation(Vertex org, Vertex dest, Vertex apex,
            ref double xi, ref double eta, bool offcenter, Otri badotri)
        {
            // Based on using -U switch, call the corresponding function
            if (behavior.MaxAngle == 0.0)
            {
                return FindNewLocationWithoutMaxAngle(org, dest, apex, ref xi, ref eta, true, badotri);
            }

            // With max angle
            return FindNewLocation(org, dest, apex, ref xi, ref eta, true, badotri);
        }
Example #4
0
        /// <summary>
        /// Find the next edge clockwise with the same destination. [dprev(abc) -> cb*]
        /// </summary>
        public void Dprev(ref Otri ot)
        {
            //Lnext(ref ot);
            ot.tri    = tri;
            ot.orient = plus1Mod3[orient];

            //ot.SymSelf();
            int tmp = ot.orient;

            ot.orient = ot.tri.neighbors[tmp].orient;
            ot.tri    = ot.tri.neighbors[tmp].tri;
        }
        public void Update(Otri otri)
        {
            if (otri.Triangle == null || otri.Triangle.ID < 0)
            {
                renderer.SelectTriangle(null, null, null);
            }
            else
            {
                renderer.SelectTriangle(otri.Triangle, otri.Org(), otri.Dest());
            }

            this.Render();
        }
Example #6
0
        /// <summary>
        /// Find the previous edge (clockwise) of the adjacent triangle. [rprev(abc) -> b**]
        /// </summary>
        public void Rprev(ref Otri ot)
        {
            //Sym(ref ot);
            ot.tri    = tri.neighbors[orient].tri;
            ot.orient = tri.neighbors[orient].orient;

            //ot.LprevSelf();
            ot.orient = minus1Mod3[ot.orient];

            //ot.SymSelf();
            int tmp = ot.orient;

            ot.orient = ot.tri.neighbors[tmp].orient;
            ot.tri    = ot.tri.neighbors[tmp].tri;
        }
Example #7
0
 /// <summary>
 /// Find the next edge (counterclockwise) of a triangle. [lnext(abc) -> bca]
 /// </summary>
 public void Lnext(ref Otri ot)
 {
     ot.tri    = tri;
     ot.orient = plus1Mod3[orient];
 }
Example #8
0
 /// <summary>
 /// Finds a triangle abutting a subsegment.
 /// </summary>
 internal void Pivot(ref Otri ot)
 {
     ot = seg.triangles[orient];
 }
Example #9
0
 /// <summary>
 /// Test for equality of oriented triangles.
 /// </summary>
 public bool Equals(Otri ot)
 {
     return((tri == ot.tri) && (orient == ot.orient));
 }
Example #10
0
 /// <summary>
 /// Finds a triangle abutting a subsegment.
 /// </summary>
 internal void Pivot(ref Otri ot)
 {
     ot = seg.triangles[orient];
 }
        /// <summary>
        /// Find a triangle or edge containing a given point.
        /// </summary>
        /// <param name="searchpoint">The point to locate.</param>
        /// <param name="searchtri">The triangle to start the search at.</param>
        /// <returns>Location information.</returns>
        /// <remarks>
        /// Searching begins from one of:  the input 'searchtri', a recently
        /// encountered triangle 'recenttri', or from a triangle chosen from a
        /// random sample. The choice is made by determining which triangle's
        /// origin is closest to the point we are searching for. Normally,
        /// 'searchtri' should be a handle on the convex hull of the triangulation.
        ///
        /// Details on the random sampling method can be found in the Mucke, Saias,
        /// and Zhu paper cited in the header of this code.
        ///
        /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
        ///
        /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
        /// is a handle whose origin is the existing vertex.
        ///
        /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
        /// handle whose primary edge is the edge on which the point lies.
        ///
        /// Returns INTRIANGLE if the point lies strictly within a triangle.
        /// 'searchtri' is a handle on the triangle that contains the point.
        ///
        /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
        /// handle whose primary edge the point is to the right of.  This might
        /// occur when the circumcenter of a triangle falls just slightly outside
        /// the mesh due to floating-point roundoff error. It also occurs when
        /// seeking a hole or region point that a foolish user has placed outside
        /// the mesh.
        ///
        /// WARNING:  This routine is designed for convex triangulations, and will
        /// not generally work after the holes and concavities have been carved.
        /// </remarks>
        public LocateResult Locate(Point searchpoint, ref Otri searchtri)
        {
            Otri sampletri = default(Otri);
            Vertex torg, tdest;
            double searchdist, dist;
            double ahead;

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

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

            // TODO: Improve sampling.
            sampler.Update();

            foreach (var t in sampler)
            {
                sampletri.tri = t;
                if (!Otri.IsDead(sampletri.tri))
                {
                    torg = sampletri.Org();
                    dist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
                           (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
                    if (dist < searchdist)
                    {
                        sampletri.Copy(ref searchtri);
                        searchdist = dist;
                    }
                }
            }

            // Where are we?
            torg = searchtri.Org();
            tdest = searchtri.Dest();

            // Check the starting triangle's vertices.
            if ((torg.x == searchpoint.x) && (torg.y == searchpoint.y))
            {
                return LocateResult.OnVertex;
            }
            if ((tdest.x == searchpoint.x) && (tdest.y == searchpoint.y))
            {
                searchtri.Lnext();
                return LocateResult.OnVertex;
            }

            // Orient 'searchtri' to fit the preconditions of calling preciselocate().
            ahead = predicates.CounterClockwise(torg, tdest, searchpoint);
            if (ahead < 0.0)
            {
                // Turn around so that 'searchpoint' is to the left of the
                // edge specified by 'searchtri'.
                searchtri.Sym();
            }
            else if (ahead == 0.0)
            {
                // Check if 'searchpoint' is between 'torg' and 'tdest'.
                if (((torg.x < searchpoint.x) == (searchpoint.x < tdest.x)) &&
                    ((torg.y < searchpoint.y) == (searchpoint.y < tdest.y)))
                {
                    return LocateResult.OnEdge;
                }
            }

            return PreciseLocate(searchpoint, ref searchtri, false);
        }
Example #12
0
        /// <summary>
        /// Insert a vertex into a Delaunay triangulation, performing flips as necessary 
        /// to maintain the Delaunay property.
        /// </summary>
        /// <param name="newvertex">The point to be inserted.</param>
        /// <param name="searchtri">The triangle to start the search.</param>
        /// <param name="splitseg">Segment to split.</param>
        /// <param name="segmentflaws">Check for creation of encroached subsegments.</param>
        /// <param name="triflaws">Check for creation of bad quality triangles.</param>
        /// <returns>If a duplicate vertex or violated segment does not prevent the 
        /// vertex from being inserted, the return value will be ENCROACHINGVERTEX if 
        /// the vertex encroaches upon a subsegment (and checking is enabled), or
        /// SUCCESSFULVERTEX otherwise. In either case, 'searchtri' is set to a handle
        /// whose origin is the newly inserted vertex.</returns>
        /// <remarks>
        /// The point 'newvertex' is located. If 'searchtri.triangle' is not NULL,
        /// the search for the containing triangle begins from 'searchtri'.  If
        /// 'searchtri.triangle' is NULL, a full point location procedure is called.
        /// If 'insertvertex' is found inside a triangle, the triangle is split into
        /// three; if 'insertvertex' lies on an edge, the edge is split in two,
        /// thereby splitting the two adjacent triangles into four. Edge flips are
        /// used to restore the Delaunay property. If 'insertvertex' lies on an
        /// existing vertex, no action is taken, and the value DUPLICATEVERTEX is
        /// returned. On return, 'searchtri' is set to a handle whose origin is the
        /// existing vertex.
        ///
        /// InsertVertex() does not use flip() for reasons of speed; some
        /// information can be reused from edge flip to edge flip, like the
        /// locations of subsegments.
        /// 
        /// Param 'splitseg': Normally, the parameter 'splitseg' is set to NULL, 
        /// implying that no subsegment should be split. In this case, if 'insertvertex' 
        /// is found to lie on a segment, no action is taken, and the value VIOLATINGVERTEX 
        /// is returned. On return, 'searchtri' is set to a handle whose primary edge is the 
        /// violated subsegment.
        /// If the calling routine wishes to split a subsegment by inserting a vertex in it, 
        /// the parameter 'splitseg' should be that subsegment. In this case, 'searchtri' 
        /// MUST be the triangle handle reached by pivoting from that subsegment; no point 
        /// location is done.
        /// 
        /// Param 'segmentflaws': Flags that indicate whether or not there should
        /// be checks for the creation of encroached subsegments. If a newly inserted 
        /// vertex encroaches upon subsegments, these subsegments are added to the list 
        /// of subsegments to be split if 'segmentflaws' is set.
        /// 
        /// Param 'triflaws': Flags that indicate whether or not there should be
        /// checks for the creation of bad quality triangles. If bad triangles are 
        /// created, these are added to the queue if 'triflaws' is set.
        /// </remarks>
        internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri,
            ref Osub splitseg, bool segmentflaws, bool triflaws)
        {
            Otri horiz = default(Otri);
            Otri top = default(Otri);
            Otri botleft = default(Otri), botright = default(Otri);
            Otri topleft = default(Otri), topright = default(Otri);
            Otri newbotleft = default(Otri), newbotright = default(Otri);
            Otri newtopright = default(Otri);
            Otri botlcasing = default(Otri), botrcasing = default(Otri);
            Otri toplcasing = default(Otri), toprcasing = default(Otri);
            Otri testtri = default(Otri);
            Osub botlsubseg = default(Osub), botrsubseg = default(Osub);
            Osub toplsubseg = default(Osub), toprsubseg = default(Osub);
            Osub brokensubseg = default(Osub);
            Osub checksubseg = default(Osub);
            Osub rightsubseg = default(Osub);
            Osub newsubseg = default(Osub);
            BadSubseg encroached;
            //FlipStacker newflip;
            Vertex first;
            Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
            Vertex segmentorg, segmentdest;
            int region;
            double area;
            InsertVertexResult success;
            LocateResult intersect;
            bool doflip;
            bool mirrorflag;
            bool enq;

            if (splitseg.seg == null)
            {
                // Find the location of the vertex to be inserted.  Check if a good
                // starting triangle has already been provided by the caller.
                if (searchtri.tri.id == DUMMY)
                {
                    // Find a boundary triangle.
                    horiz.tri = dummytri;
                    horiz.orient = 0;
                    horiz.Sym();

                    // Search for a triangle containing 'newvertex'.
                    intersect = locator.Locate(newvertex, ref horiz);
                }
                else
                {
                    // Start searching from the triangle provided by the caller.
                    searchtri.Copy(ref horiz);
                    intersect = locator.PreciseLocate(newvertex, ref horiz, true);
                }
            }
            else
            {
                // The calling routine provides the subsegment in which
                // the vertex is inserted.
                searchtri.Copy(ref horiz);
                intersect = LocateResult.OnEdge;
            }

            if (intersect == LocateResult.OnVertex)
            {
                // There's already a vertex there.  Return in 'searchtri' a triangle
                // whose origin is the existing vertex.
                horiz.Copy(ref searchtri);
                locator.Update(ref horiz);
                return InsertVertexResult.Duplicate;
            }
            if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside))
            {
                // The vertex falls on an edge or boundary.
                if (checksegments && (splitseg.seg == null))
                {
                    // Check whether the vertex falls on a subsegment.
                    horiz.Pivot(ref brokensubseg);
                    if (brokensubseg.seg.hash != DUMMY)
                    {
                        // The vertex falls on a subsegment, and hence will not be inserted.
                        if (segmentflaws)
                        {
                            enq = behavior.NoBisect != 2;
                            if (enq && (behavior.NoBisect == 1))
                            {
                                // This subsegment may be split only if it is an
                                // internal boundary.
                                horiz.Sym(ref testtri);
                                enq = testtri.tri.id != DUMMY;
                            }
                            if (enq)
                            {
                                // Add the subsegment to the list of encroached subsegments.
                                encroached = new BadSubseg();
                                encroached.subseg = brokensubseg;
                                encroached.org = brokensubseg.Org();
                                encroached.dest = brokensubseg.Dest();

                                qualityMesher.AddBadSubseg(encroached);
                            }
                        }
                        // Return a handle whose primary edge contains the vertex,
                        //   which has not been inserted.
                        horiz.Copy(ref searchtri);
                        locator.Update(ref horiz);
                        return InsertVertexResult.Violating;
                    }
                }

                // Insert the vertex on an edge, dividing one triangle into two (if
                // the edge lies on a boundary) or two triangles into four.
                horiz.Lprev(ref botright);
                botright.Sym(ref botrcasing);
                horiz.Sym(ref topright);
                // Is there a second triangle?  (Or does this edge lie on a boundary?)
                mirrorflag = topright.tri.id != DUMMY;
                if (mirrorflag)
                {
                    topright.Lnext();
                    topright.Sym(ref toprcasing);
                    MakeTriangle(ref newtopright);
                }
                else
                {
                    // Splitting a boundary edge increases the number of boundary edges.
                    hullsize++;
                }
                MakeTriangle(ref newbotright);

                // Set the vertices of changed and new triangles.
                rightvertex = horiz.Org();
                leftvertex = horiz.Dest();
                botvertex = horiz.Apex();
                newbotright.SetOrg(botvertex);
                newbotright.SetDest(rightvertex);
                newbotright.SetApex(newvertex);
                horiz.SetOrg(newvertex);

                // Set the region of a new triangle.
                newbotright.tri.label = botright.tri.label;

                if (behavior.VarArea)
                {
                    // Set the area constraint of a new triangle.
                    newbotright.tri.area = botright.tri.area;
                }

                if (mirrorflag)
                {
                    topvertex = topright.Dest();
                    newtopright.SetOrg(rightvertex);
                    newtopright.SetDest(topvertex);
                    newtopright.SetApex(newvertex);
                    topright.SetOrg(newvertex);

                    // Set the region of another new triangle.
                    newtopright.tri.label = topright.tri.label;

                    if (behavior.VarArea)
                    {
                        // Set the area constraint of another new triangle.
                        newtopright.tri.area = topright.tri.area;
                    }
                }

                // There may be subsegments that need to be bonded
                // to the new triangle(s).
                if (checksegments)
                {
                    botright.Pivot(ref botrsubseg);

                    if (botrsubseg.seg.hash != DUMMY)
                    {
                        botright.SegDissolve(dummysub);
                        newbotright.SegBond(ref botrsubseg);
                    }

                    if (mirrorflag)
                    {
                        topright.Pivot(ref toprsubseg);
                        if (toprsubseg.seg.hash != DUMMY)
                        {
                            topright.SegDissolve(dummysub);
                            newtopright.SegBond(ref toprsubseg);
                        }
                    }
                }

                // Bond the new triangle(s) to the surrounding triangles.
                newbotright.Bond(ref botrcasing);
                newbotright.Lprev();
                newbotright.Bond(ref botright);
                newbotright.Lprev();

                if (mirrorflag)
                {
                    newtopright.Bond(ref toprcasing);
                    newtopright.Lnext();
                    newtopright.Bond(ref topright);
                    newtopright.Lnext();
                    newtopright.Bond(ref newbotright);
                }

                if (splitseg.seg != null)
                {
                    // Split the subsegment into two.
                    splitseg.SetDest(newvertex);
                    segmentorg = splitseg.SegOrg();
                    segmentdest = splitseg.SegDest();
                    splitseg.Sym();
                    splitseg.Pivot(ref rightsubseg);
                    InsertSubseg(ref newbotright, splitseg.seg.boundary);
                    newbotright.Pivot(ref newsubseg);
                    newsubseg.SetSegOrg(segmentorg);
                    newsubseg.SetSegDest(segmentdest);
                    splitseg.Bond(ref newsubseg);
                    newsubseg.Sym();
                    newsubseg.Bond(ref rightsubseg);
                    splitseg.Sym();

                    // Transfer the subsegment's boundary marker to the vertex if required.
                    if (newvertex.label == 0)
                    {
                        newvertex.label = splitseg.seg.boundary;
                    }
                }

                if (checkquality)
                {
                    flipstack.Clear();

                    flipstack.Push(default(Otri)); // Dummy flip (see UndoVertex)
                    flipstack.Push(horiz);
                }

                // Position 'horiz' on the first edge to check for
                // the Delaunay property.
                horiz.Lnext();
            }
            else
            {
                // Insert the vertex in a triangle, splitting it into three.
                horiz.Lnext(ref botleft);
                horiz.Lprev(ref botright);
                botleft.Sym(ref botlcasing);
                botright.Sym(ref botrcasing);
                MakeTriangle(ref newbotleft);
                MakeTriangle(ref newbotright);

                // Set the vertices of changed and new triangles.
                rightvertex = horiz.Org();
                leftvertex = horiz.Dest();
                botvertex = horiz.Apex();
                newbotleft.SetOrg(leftvertex);
                newbotleft.SetDest(botvertex);
                newbotleft.SetApex(newvertex);
                newbotright.SetOrg(botvertex);
                newbotright.SetDest(rightvertex);
                newbotright.SetApex(newvertex);
                horiz.SetApex(newvertex);

                // Set the region of the new triangles.
                newbotleft.tri.label = horiz.tri.label;
                newbotright.tri.label = horiz.tri.label;

                if (behavior.VarArea)
                {
                    // Set the area constraint of the new triangles.
                    area = horiz.tri.area;
                    newbotleft.tri.area = area;
                    newbotright.tri.area = area;
                }

                // There may be subsegments that need to be bonded
                // to the new triangles.
                if (checksegments)
                {
                    botleft.Pivot(ref botlsubseg);
                    if (botlsubseg.seg.hash != DUMMY)
                    {
                        botleft.SegDissolve(dummysub);
                        newbotleft.SegBond(ref botlsubseg);
                    }
                    botright.Pivot(ref botrsubseg);
                    if (botrsubseg.seg.hash != DUMMY)
                    {
                        botright.SegDissolve(dummysub);
                        newbotright.SegBond(ref botrsubseg);
                    }
                }

                // Bond the new triangles to the surrounding triangles.
                newbotleft.Bond(ref botlcasing);
                newbotright.Bond(ref botrcasing);
                newbotleft.Lnext();
                newbotright.Lprev();
                newbotleft.Bond(ref newbotright);
                newbotleft.Lnext();
                botleft.Bond(ref newbotleft);
                newbotright.Lprev();
                botright.Bond(ref newbotright);

                if (checkquality)
                {
                    flipstack.Clear();
                    flipstack.Push(horiz);
                }
            }

            // The insertion is successful by default, unless an encroached
            // subsegment is found.
            success = InsertVertexResult.Successful;

            if (newvertex.tri.tri != null)
            {
                // Store the coordinates of the triangle that contains newvertex.
                newvertex.tri.SetOrg(rightvertex);
                newvertex.tri.SetDest(leftvertex);
                newvertex.tri.SetApex(botvertex);
            }

            // Circle around the newly inserted vertex, checking each edge opposite it 
            // for the Delaunay property. Non-Delaunay edges are flipped. 'horiz' is 
            // always the edge being checked. 'first' marks where to stop circling.
            first = horiz.Org();
            rightvertex = first;
            leftvertex = horiz.Dest();
            // Circle until finished.
            while (true)
            {
                // By default, the edge will be flipped.
                doflip = true;

                if (checksegments)
                {
                    // Check for a subsegment, which cannot be flipped.
                    horiz.Pivot(ref checksubseg);
                    if (checksubseg.seg.hash != DUMMY)
                    {
                        // The edge is a subsegment and cannot be flipped.
                        doflip = false;

                        if (segmentflaws)
                        {
                            // Does the new vertex encroach upon this subsegment?
                            if (qualityMesher.CheckSeg4Encroach(ref checksubseg) > 0)
                            {
                                success = InsertVertexResult.Encroaching;
                            }
                        }
                    }
                }

                if (doflip)
                {
                    // Check if the edge is a boundary edge.
                    horiz.Sym(ref top);
                    if (top.tri.id == DUMMY)
                    {
                        // The edge is a boundary edge and cannot be flipped.
                        doflip = false;
                    }
                    else
                    {
                        // Find the vertex on the other side of the edge.
                        farvertex = top.Apex();
                        // In the incremental Delaunay triangulation algorithm, any of
                        // 'leftvertex', 'rightvertex', and 'farvertex' could be vertices
                        // of the triangular bounding box. These vertices must be
                        // treated as if they are infinitely distant, even though their
                        // "coordinates" are not.
                        if ((leftvertex == infvertex1) || (leftvertex == infvertex2) ||
                            (leftvertex == infvertex3))
                        {
                            // 'leftvertex' is infinitely distant. Check the convexity of
                            // the boundary of the triangulation. 'farvertex' might be
                            // infinite as well, but trust me, this same condition should
                            // be applied.
                            doflip = predicates.CounterClockwise(newvertex, rightvertex, farvertex) > 0.0;
                        }
                        else if ((rightvertex == infvertex1) ||
                                 (rightvertex == infvertex2) ||
                                 (rightvertex == infvertex3))
                        {
                            // 'rightvertex' is infinitely distant. Check the convexity of
                            // the boundary of the triangulation. 'farvertex' might be
                            // infinite as well, but trust me, this same condition should
                            // be applied.
                            doflip = predicates.CounterClockwise(farvertex, leftvertex, newvertex) > 0.0;
                        }
                        else if ((farvertex == infvertex1) ||
                                 (farvertex == infvertex2) ||
                                 (farvertex == infvertex3))
                        {
                            // 'farvertex' is infinitely distant and cannot be inside
                            // the circumcircle of the triangle 'horiz'.
                            doflip = false;
                        }
                        else
                        {
                            // Test whether the edge is locally Delaunay.
                            doflip = predicates.InCircle(leftvertex, newvertex, rightvertex, farvertex) > 0.0;
                        }
                        if (doflip)
                        {
                            // We made it! Flip the edge 'horiz' by rotating its containing
                            // quadrilateral (the two triangles adjacent to 'horiz').
                            // Identify the casing of the quadrilateral.
                            top.Lprev(ref topleft);
                            topleft.Sym(ref toplcasing);
                            top.Lnext(ref topright);
                            topright.Sym(ref toprcasing);
                            horiz.Lnext(ref botleft);
                            botleft.Sym(ref botlcasing);
                            horiz.Lprev(ref botright);
                            botright.Sym(ref botrcasing);
                            // Rotate the quadrilateral one-quarter turn counterclockwise.
                            topleft.Bond(ref botlcasing);
                            botleft.Bond(ref botrcasing);
                            botright.Bond(ref toprcasing);
                            topright.Bond(ref toplcasing);
                            if (checksegments)
                            {
                                // Check for subsegments and rebond them to the quadrilateral.
                                topleft.Pivot(ref toplsubseg);
                                botleft.Pivot(ref botlsubseg);
                                botright.Pivot(ref botrsubseg);
                                topright.Pivot(ref toprsubseg);
                                if (toplsubseg.seg.hash == DUMMY)
                                {
                                    topright.SegDissolve(dummysub);
                                }
                                else
                                {
                                    topright.SegBond(ref toplsubseg);
                                }
                                if (botlsubseg.seg.hash == DUMMY)
                                {
                                    topleft.SegDissolve(dummysub);
                                }
                                else
                                {
                                    topleft.SegBond(ref botlsubseg);
                                }
                                if (botrsubseg.seg.hash == DUMMY)
                                {
                                    botleft.SegDissolve(dummysub);
                                }
                                else
                                {
                                    botleft.SegBond(ref botrsubseg);
                                }
                                if (toprsubseg.seg.hash == DUMMY)
                                {
                                    botright.SegDissolve(dummysub);
                                }
                                else
                                {
                                    botright.SegBond(ref toprsubseg);
                                }
                            }
                            // New vertex assignments for the rotated quadrilateral.
                            horiz.SetOrg(farvertex);
                            horiz.SetDest(newvertex);
                            horiz.SetApex(rightvertex);
                            top.SetOrg(newvertex);
                            top.SetDest(farvertex);
                            top.SetApex(leftvertex);

                            // Assign region.
                            // TODO: check region ok (no Math.Min necessary)
                            region = Math.Min(top.tri.label, horiz.tri.label);
                            top.tri.label = region;
                            horiz.tri.label = region;

                            if (behavior.VarArea)
                            {
                                if ((top.tri.area <= 0.0) || (horiz.tri.area <= 0.0))
                                {
                                    area = -1.0;
                                }
                                else
                                {
                                    // Take the average of the two triangles' area constraints.
                                    // This prevents small area constraints from migrating a
                                    // long, long way from their original location due to flips.
                                    area = 0.5 * (top.tri.area + horiz.tri.area);
                                }

                                top.tri.area = area;
                                horiz.tri.area = area;
                            }

                            if (checkquality)
                            {
                                flipstack.Push(horiz);
                            }

                            // On the next iterations, consider the two edges that were exposed (this
                            // is, are now visible to the newly inserted vertex) by the edge flip.
                            horiz.Lprev();
                            leftvertex = farvertex;
                        }
                    }
                }
                if (!doflip)
                {
                    // The handle 'horiz' is accepted as locally Delaunay.
                    if (triflaws)
                    {
                        // Check the triangle 'horiz' for quality.
                        qualityMesher.TestTriangle(ref horiz);
                    }

                    // Look for the next edge around the newly inserted vertex.
                    horiz.Lnext();
                    horiz.Sym(ref testtri);
                    // Check for finishing a complete revolution about the new vertex, or
                    // falling outside of the triangulation. The latter will happen when
                    // a vertex is inserted at a boundary.
                    if ((leftvertex == first) || (testtri.tri.id == DUMMY))
                    {
                        // We're done. Return a triangle whose origin is the new vertex.
                        horiz.Lnext(ref searchtri);

                        Otri recenttri = default(Otri);
                        horiz.Lnext(ref recenttri);
                        locator.Update(ref recenttri);

                        return success;
                    }
                    // Finish finding the next edge around the newly inserted vertex.
                    testtri.Lnext(ref horiz);
                    rightvertex = leftvertex;
                    leftvertex = horiz.Dest();
                }
            }
        }
Example #13
0
        /// <summary>
        /// Delete a vertex from a Delaunay triangulation, ensuring that the 
        /// triangulation remains Delaunay.
        /// </summary>
        /// <param name="deltri"></param>
        /// <remarks>The origin of 'deltri' is deleted. The union of the triangles 
        /// adjacent to this vertex is a polygon, for which the Delaunay triangulation 
        /// is found. Two triangles are removed from the mesh.
        ///
        /// Only interior vertices that do not lie on segments or boundaries 
        /// may be deleted.
        /// </remarks>
        internal void DeleteVertex(ref Otri deltri)
        {
            Otri countingtri = default(Otri);
            Otri firstedge = default(Otri), lastedge = default(Otri);
            Otri deltriright = default(Otri);
            Otri lefttri = default(Otri), righttri = default(Otri);
            Otri leftcasing = default(Otri), rightcasing = default(Otri);
            Osub leftsubseg = default(Osub), rightsubseg = default(Osub);
            Vertex delvertex;
            Vertex neworg;
            int edgecount;

            delvertex = deltri.Org();

            VertexDealloc(delvertex);

            // Count the degree of the vertex being deleted.
            deltri.Onext(ref countingtri);
            edgecount = 1;
            while (!deltri.Equals(countingtri))
            {
                edgecount++;
                countingtri.Onext();
            }

            if (edgecount > 3)
            {
                // Triangulate the polygon defined by the union of all triangles
                // adjacent to the vertex being deleted.  Check the quality of
                // the resulting triangles.
                deltri.Onext(ref firstedge);
                deltri.Oprev(ref lastedge);
                TriangulatePolygon(firstedge, lastedge, edgecount, false, behavior.NoBisect == 0);
            }
            // Splice out two triangles.
            deltri.Lprev(ref deltriright);
            deltri.Dnext(ref lefttri);
            lefttri.Sym(ref leftcasing);
            deltriright.Oprev(ref righttri);
            righttri.Sym(ref rightcasing);
            deltri.Bond(ref leftcasing);
            deltriright.Bond(ref rightcasing);
            lefttri.Pivot(ref leftsubseg);
            if (leftsubseg.seg.hash != DUMMY)
            {
                deltri.SegBond(ref leftsubseg);
            }
            righttri.Pivot(ref rightsubseg);
            if (rightsubseg.seg.hash != DUMMY)
            {
                deltriright.SegBond(ref rightsubseg);
            }

            // Set the new origin of 'deltri' and check its quality.
            neworg = lefttri.Org();
            deltri.SetOrg(neworg);
            if (behavior.NoBisect == 0)
            {
                qualityMesher.TestTriangle(ref deltri);
            }

            // Delete the two spliced-out triangles.
            TriangleDealloc(lefttri.tri);
            TriangleDealloc(righttri.tri);
        }
Example #14
0
        SplayNode CircleTopInsert(SplayNode splayroot, Otri newkey,
                                  Vertex pa, Vertex pb, Vertex pc, double topy)
        {
            double ccwabc;
            double xac, yac, xbc, ybc;
            double aclen2, bclen2;
            Point searchpoint = new Point(); // TODO: mesh.nextras
            Otri dummytri = default(Otri);

            ccwabc = predicates.CounterClockwise(pa, pb, pc);
            xac = pa.x - pc.x;
            yac = pa.y - pc.y;
            xbc = pb.x - pc.x;
            ybc = pb.y - pc.y;
            aclen2 = xac * xac + yac * yac;
            bclen2 = xbc * xbc + ybc * ybc;
            searchpoint.x = pc.x - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
            searchpoint.y = topy;
            return SplayInsert(Splay(splayroot, searchpoint, ref dummytri), newkey, searchpoint);
        }
Example #15
0
        SplayNode FrontLocate(SplayNode splayroot, Otri bottommost, Vertex searchvertex,
                              ref Otri searchtri, ref bool farright)
        {
            bool farrightflag;

            bottommost.Copy(ref searchtri);
            splayroot = Splay(splayroot, searchvertex, ref searchtri);

            farrightflag = false;
            while (!farrightflag && RightOfHyperbola(ref searchtri, searchvertex))
            {
                searchtri.Onext();
                farrightflag = searchtri.Equals(bottommost);
            }
            farright = farrightflag;
            return splayroot;
        }
Example #16
0
        SplayNode SplayInsert(SplayNode splayroot, Otri newkey, Point searchpoint)
        {
            SplayNode newsplaynode;

            newsplaynode = new SplayNode(); //poolalloc(m.splaynodes);
            splaynodes.Add(newsplaynode);
            newkey.Copy(ref newsplaynode.keyedge);
            newsplaynode.keydest = newkey.Dest();
            if (splayroot == null)
            {
                newsplaynode.lchild = null;
                newsplaynode.rchild = null;
            }
            else if (RightOfHyperbola(ref splayroot.keyedge, searchpoint))
            {
                newsplaynode.lchild = splayroot;
                newsplaynode.rchild = splayroot.rchild;
                splayroot.rchild = null;
            }
            else
            {
                newsplaynode.lchild = splayroot.lchild;
                newsplaynode.rchild = splayroot;
                splayroot.lchild = null;
            }
            return newsplaynode;
        }
Example #17
0
        SplayNode Splay(SplayNode splaytree, Point searchpoint, ref Otri searchtri)
        {
            SplayNode child, grandchild;
            SplayNode lefttree, righttree;
            SplayNode leftright;
            Vertex checkvertex;
            bool rightofroot, rightofchild;

            if (splaytree == null)
            {
                return null;
            }
            checkvertex = splaytree.keyedge.Dest();
            if (checkvertex == splaytree.keydest)
            {
                rightofroot = RightOfHyperbola(ref splaytree.keyedge, searchpoint);
                if (rightofroot)
                {
                    splaytree.keyedge.Copy(ref searchtri);
                    child = splaytree.rchild;
                }
                else
                {
                    child = splaytree.lchild;
                }
                if (child == null)
                {
                    return splaytree;
                }
                checkvertex = child.keyedge.Dest();
                if (checkvertex != child.keydest)
                {
                    child = Splay(child, searchpoint, ref searchtri);
                    if (child == null)
                    {
                        if (rightofroot)
                        {
                            splaytree.rchild = null;
                        }
                        else
                        {
                            splaytree.lchild = null;
                        }
                        return splaytree;
                    }
                }
                rightofchild = RightOfHyperbola(ref child.keyedge, searchpoint);
                if (rightofchild)
                {
                    child.keyedge.Copy(ref searchtri);
                    grandchild = Splay(child.rchild, searchpoint, ref searchtri);
                    child.rchild = grandchild;
                }
                else
                {
                    grandchild = Splay(child.lchild, searchpoint, ref searchtri);
                    child.lchild = grandchild;
                }
                if (grandchild == null)
                {
                    if (rightofroot)
                    {
                        splaytree.rchild = child.lchild;
                        child.lchild = splaytree;
                    }
                    else
                    {
                        splaytree.lchild = child.rchild;
                        child.rchild = splaytree;
                    }
                    return child;
                }
                if (rightofchild)
                {
                    if (rightofroot)
                    {
                        splaytree.rchild = child.lchild;
                        child.lchild = splaytree;
                    }
                    else
                    {
                        splaytree.lchild = grandchild.rchild;
                        grandchild.rchild = splaytree;
                    }
                    child.rchild = grandchild.lchild;
                    grandchild.lchild = child;
                }
                else
                {
                    if (rightofroot)
                    {
                        splaytree.rchild = grandchild.lchild;
                        grandchild.lchild = splaytree;
                    }
                    else
                    {
                        splaytree.lchild = child.rchild;
                        child.rchild = splaytree;
                    }
                    child.lchild = grandchild.rchild;
                    grandchild.rchild = child;
                }
                return grandchild;
            }
            else
            {
                lefttree = Splay(splaytree.lchild, searchpoint, ref searchtri);
                righttree = Splay(splaytree.rchild, searchpoint, ref searchtri);

                splaynodes.Remove(splaytree);
                if (lefttree == null)
                {
                    return righttree;
                }
                else if (righttree == null)
                {
                    return lefttree;
                }
                else if (lefttree.rchild == null)
                {
                    lefttree.rchild = righttree.lchild;
                    righttree.lchild = lefttree;
                    return righttree;
                }
                else if (righttree.lchild == null)
                {
                    righttree.lchild = lefttree.rchild;
                    lefttree.rchild = righttree;
                    return lefttree;
                }
                else
                {
                    //      printf("Holy Toledo!!!\n");
                    leftright = lefttree.rchild;
                    while (leftright.rchild != null)
                    {
                        leftright = leftright.rchild;
                    }
                    leftright.rchild = righttree;
                    return lefttree;
                }
            }
        }
Example #18
0
        // The following primitives are all described by Guibas and Stolfi.
        // However, Guibas and Stolfi use an edge-based data structure,
        // whereas I use a triangle-based data structure.
        //
        // lnext: finds the next edge (counterclockwise) of a triangle.
        //
        // onext: spins counterclockwise around a vertex; that is, it finds
        // the next edge with the same origin in the counterclockwise direction. This
        // edge is part of a different triangle.
        //
        // oprev: spins clockwise around a vertex; that is, it finds the
        // next edge with the same origin in the clockwise direction.  This edge is
        // part of a different triangle.
        //
        // dnext: spins counterclockwise around a vertex; that is, it finds
        // the next edge with the same destination in the counterclockwise direction.
        // This edge is part of a different triangle.
        //
        // dprev: spins clockwise around a vertex; that is, it finds the
        // next edge with the same destination in the clockwise direction. This edge
        // is part of a different triangle.
        //
        // rnext: moves one edge counterclockwise about the adjacent
        // triangle. (It's best understood by reading Guibas and Stolfi. It
        // involves changing triangles twice.)
        //
        // rprev: moves one edge clockwise about the adjacent triangle.
        // (It's best understood by reading Guibas and Stolfi.  It involves
        // changing triangles twice.)

        /// <summary>
        /// Find the abutting triangle; same edge. [sym(abc) -> ba*]
        /// </summary>
        /// Note that the edge direction is necessarily reversed, because the handle specified
        /// by an oriented triangle is directed counterclockwise around the triangle.
        /// </remarks>
        public void Sym(ref Otri ot)
        {
            ot.tri    = tri.neighbors[orient].tri;
            ot.orient = tri.neighbors[orient].orient;
        }
Example #19
0
        /// <summary>
        /// Transform two triangles to two different triangles by flipping an edge 
        /// clockwise within a quadrilateral. Reverses the flip() operation so that 
        /// the data structures representing the triangles are back where they were 
        /// before the flip().
        /// </summary>
        /// <param name="flipedge"></param>
        /// <remarks>
        /// See above Flip() remarks for more information.
        ///
        /// Upon completion of this routine, the 'flipedge' handle holds the edge
        /// cd of triangle cdb, and is directed up, from vertex c to vertex d.
        /// (Hence, the two triangles have rotated clockwise.)
        /// </remarks>
        internal void Unflip(ref Otri flipedge)
        {
            Otri botleft = default(Otri), botright = default(Otri);
            Otri topleft = default(Otri), topright = default(Otri);
            Otri top = default(Otri);
            Otri botlcasing = default(Otri), botrcasing = default(Otri);
            Otri toplcasing = default(Otri), toprcasing = default(Otri);
            Osub botlsubseg = default(Osub), botrsubseg = default(Osub);
            Osub toplsubseg = default(Osub), toprsubseg = default(Osub);
            Vertex leftvertex, rightvertex, botvertex;
            Vertex farvertex;

            // Identify the vertices of the quadrilateral.
            rightvertex = flipedge.Org();
            leftvertex = flipedge.Dest();
            botvertex = flipedge.Apex();
            flipedge.Sym(ref top);

            farvertex = top.Apex();

            // Identify the casing of the quadrilateral.
            top.Lprev(ref topleft);
            topleft.Sym(ref toplcasing);
            top.Lnext(ref topright);
            topright.Sym(ref toprcasing);
            flipedge.Lnext(ref botleft);
            botleft.Sym(ref botlcasing);
            flipedge.Lprev(ref botright);
            botright.Sym(ref botrcasing);
            // Rotate the quadrilateral one-quarter turn clockwise.
            topleft.Bond(ref toprcasing);
            botleft.Bond(ref toplcasing);
            botright.Bond(ref botlcasing);
            topright.Bond(ref botrcasing);

            if (checksegments)
            {
                // Check for subsegments and rebond them to the quadrilateral.
                topleft.Pivot(ref toplsubseg);
                botleft.Pivot(ref botlsubseg);
                botright.Pivot(ref botrsubseg);
                topright.Pivot(ref toprsubseg);
                if (toplsubseg.seg.hash == DUMMY)
                {
                    botleft.SegDissolve(dummysub);
                }
                else
                {
                    botleft.SegBond(ref toplsubseg);
                }
                if (botlsubseg.seg.hash == DUMMY)
                {
                    botright.SegDissolve(dummysub);
                }
                else
                {
                    botright.SegBond(ref botlsubseg);
                }
                if (botrsubseg.seg.hash == DUMMY)
                {
                    topright.SegDissolve(dummysub);
                }
                else
                {
                    topright.SegBond(ref botrsubseg);
                }
                if (toprsubseg.seg.hash == DUMMY)
                {
                    topleft.SegDissolve(dummysub);
                }
                else
                {
                    topleft.SegBond(ref toprsubseg);
                }
            }

            // New vertex assignments for the rotated quadrilateral.
            flipedge.SetOrg(botvertex);
            flipedge.SetDest(farvertex);
            flipedge.SetApex(leftvertex);
            top.SetOrg(farvertex);
            top.SetDest(botvertex);
            top.SetApex(rightvertex);
        }
Example #20
0
        bool RightOfHyperbola(ref Otri fronttri, Point newsite)
        {
            Vertex leftvertex, rightvertex;
            double dxa, dya, dxb, dyb;

            Statistic.HyperbolaCount++;

            leftvertex = fronttri.Dest();
            rightvertex = fronttri.Apex();
            if ((leftvertex.y < rightvertex.y) ||
                ((leftvertex.y == rightvertex.y) &&
                 (leftvertex.x < rightvertex.x)))
            {
                if (newsite.x >= rightvertex.x)
                {
                    return true;
                }
            }
            else
            {
                if (newsite.x <= leftvertex.x)
                {
                    return false;
                }
            }
            dxa = leftvertex.x - newsite.x;
            dya = leftvertex.y - newsite.y;
            dxb = rightvertex.x - newsite.x;
            dyb = rightvertex.y - newsite.y;
            return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
        }
Example #21
0
        /// <summary>
        /// Find the Delaunay triangulation of a polygon that has a certain "nice" shape. 
        /// This includes the polygons that result from deletion of a vertex or insertion 
        /// of a segment.
        /// </summary>
        /// <param name="firstedge">The primary edge of the first triangle.</param>
        /// <param name="lastedge">The primary edge of the last triangle.</param>
        /// <param name="edgecount">The number of sides of the polygon, including its 
        /// base.</param>
        /// <param name="doflip">A flag, wether to perform the last flip.</param>
        /// <param name="triflaws">A flag that determines whether the new triangles should 
        /// be tested for quality, and enqueued if they are bad.</param>
        /// <remarks>
        //  This is a conceptually difficult routine. The starting assumption is
        //  that we have a polygon with n sides. n - 1 of these sides are currently
        //  represented as edges in the mesh. One side, called the "base", need not
        //  be.
        //
        //  Inside the polygon is a structure I call a "fan", consisting of n - 1
        //  triangles that share a common origin. For each of these triangles, the
        //  edge opposite the origin is one of the sides of the polygon. The
        //  primary edge of each triangle is the edge directed from the origin to
        //  the destination; note that this is not the same edge that is a side of
        //  the polygon. 'firstedge' is the primary edge of the first triangle.
        //  From there, the triangles follow in counterclockwise order about the
        //  polygon, until 'lastedge', the primary edge of the last triangle.
        //  'firstedge' and 'lastedge' are probably connected to other triangles
        //  beyond the extremes of the fan, but their identity is not important, as
        //  long as the fan remains connected to them.
        //
        //  Imagine the polygon oriented so that its base is at the bottom.  This
        //  puts 'firstedge' on the far right, and 'lastedge' on the far left.
        //  The right vertex of the base is the destination of 'firstedge', and the
        //  left vertex of the base is the apex of 'lastedge'.
        //
        //  The challenge now is to find the right sequence of edge flips to
        //  transform the fan into a Delaunay triangulation of the polygon.  Each
        //  edge flip effectively removes one triangle from the fan, committing it
        //  to the polygon.  The resulting polygon has one fewer edge. If 'doflip'
        //  is set, the final flip will be performed, resulting in a fan of one
        //  (useless?) triangle. If 'doflip' is not set, the final flip is not
        //  performed, resulting in a fan of two triangles, and an unfinished
        //  triangular polygon that is not yet filled out with a single triangle.
        //  On completion of the routine, 'lastedge' is the last remaining triangle,
        //  or the leftmost of the last two.
        //
        //  Although the flips are performed in the order described above, the
        //  decisions about what flips to perform are made in precisely the reverse
        //  order. The recursive triangulatepolygon() procedure makes a decision,
        //  uses up to two recursive calls to triangulate the "subproblems"
        //  (polygons with fewer edges), and then performs an edge flip.
        //
        //  The "decision" it makes is which vertex of the polygon should be
        //  connected to the base. This decision is made by testing every possible
        //  vertex.  Once the best vertex is found, the two edges that connect this
        //  vertex to the base become the bases for two smaller polygons. These
        //  are triangulated recursively. Unfortunately, this approach can take
        //  O(n^2) time not only in the worst case, but in many common cases. It's
        //  rarely a big deal for vertex deletion, where n is rarely larger than
        //  ten, but it could be a big deal for segment insertion, especially if
        //  there's a lot of long segments that each cut many triangles. I ought to
        //  code a faster algorithm some day.
        /// </remarks>
        private void TriangulatePolygon(Otri firstedge, Otri lastedge,
                                int edgecount, bool doflip, bool triflaws)
        {
            Otri testtri = default(Otri);
            Otri besttri = default(Otri);
            Otri tempedge = default(Otri);
            Vertex leftbasevertex, rightbasevertex;
            Vertex testvertex;
            Vertex bestvertex;

            int bestnumber = 1;

            // Identify the base vertices.
            leftbasevertex = lastedge.Apex();
            rightbasevertex = firstedge.Dest();

            // Find the best vertex to connect the base to.
            firstedge.Onext(ref besttri);
            bestvertex = besttri.Dest();
            besttri.Copy(ref testtri);

            for (int i = 2; i <= edgecount - 2; i++)
            {
                testtri.Onext();
                testvertex = testtri.Dest();
                // Is this a better vertex?
                if (predicates.InCircle(leftbasevertex, rightbasevertex, bestvertex, testvertex) > 0.0)
                {
                    testtri.Copy(ref besttri);
                    bestvertex = testvertex;
                    bestnumber = i;
                }
            }

            if (bestnumber > 1)
            {
                // Recursively triangulate the smaller polygon on the right.
                besttri.Oprev(ref tempedge);
                TriangulatePolygon(firstedge, tempedge, bestnumber + 1, true, triflaws);
            }

            if (bestnumber < edgecount - 2)
            {
                // Recursively triangulate the smaller polygon on the left.
                besttri.Sym(ref tempedge);
                TriangulatePolygon(besttri, lastedge, edgecount - bestnumber, true, triflaws);
                // Find 'besttri' again; it may have been lost to edge flips.
                tempedge.Sym(ref besttri);
            }

            if (doflip)
            {
                // Do one final edge flip.
                Flip(ref besttri);
                if (triflaws)
                {
                    // Check the quality of the newly committed triangle.
                    besttri.Sym(ref testtri);
                    qualityMesher.TestTriangle(ref testtri);
                }
            }
            // Return the base triangle.
            besttri.Copy(ref lastedge);
        }
Example #22
0
        void Check4DeadEvent(ref Otri checktri, SweepEvent[] eventheap, ref int heapsize)
        {
            SweepEvent deadevent;
            SweepEventVertex eventvertex;
            int eventnum = -1;

            eventvertex = checktri.Org() as SweepEventVertex;
            if (eventvertex != null)
            {
                deadevent = eventvertex.evt;
                eventnum = deadevent.heapposition;

                HeapDelete(eventheap, heapsize, eventnum);
                heapsize--;
                checktri.SetOrg(null);
            }
        }
Example #23
0
        /// <summary>
        /// Create a new triangle with orientation zero.
        /// </summary>
        /// <param name="newotri">Reference to the new triangle.</param>
        internal void MakeTriangle(ref Otri newotri)
        {
            Triangle tri = triangles.Get();

            //tri.id = tri.hash;

            tri.subsegs[0].seg = dummysub;
            tri.subsegs[1].seg = dummysub;
            tri.subsegs[2].seg = dummysub;

            tri.neighbors[0].tri = dummytri;
            tri.neighbors[1].tri = dummytri;
            tri.neighbors[2].tri = dummytri;

            newotri.tri = tri;
            newotri.orient = 0;
        }
Example #24
0
        /// <summary>
        /// Checks if smothing is possible for a given bad triangle.
        /// </summary>
        /// <param name="badotri"></param>
        /// <param name="torg"></param>
        /// <param name="tdest"></param>
        /// <param name="tapex"></param>
        /// <param name="newloc">The new location for the point, if somothing is possible.</param>
        /// <returns>Returns 1, 2 or 3 if smoothing will work, 0 otherwise.</returns>
        private int DoSmoothing(Otri badotri, Vertex torg, Vertex tdest, Vertex tapex,
            ref double[] newloc)
        {

            int numpoints_p = 0;// keeps the number of points in a star of point p, q, r
            int numpoints_q = 0;
            int numpoints_r = 0;
            //int i;	
            double[] possibilities = new double[6];//there can be more than one possibilities
            int num_pos = 0; // number of possibilities
            int flag1 = 0, flag2 = 0, flag3 = 0;
            bool newLocFound = false;

            //vertex v1, v2, v3;	// for ccw test
            //double p1[2], p2[2], p3[2];
            //double temp[2];

            //********************* TRY TO RELOCATE POINT "p" ***************

            // get the surrounding points of p, so this gives us the triangles
            numpoints_p = GetStarPoints(badotri, torg, tdest, tapex, 1, ref points_p);
            // check if the points in counterclockwise order
            // 	p1[0] = points_p[0];  p1[1] = points_p[1];
            // 	p2[0] = points_p[2];  p2[1] = points_p[3];
            // 	p3[0] = points_p[4];  p3[1] = points_p[5];
            // 	v1 = (vertex)p1; v2 = (vertex)p2; v3 = (vertex)p3; 
            // 	if(counterclockwise(m,b,v1,v2,v3) < 0){
            // 		// reverse the order to ccw
            // 		for(i = 0; i < numpoints_p/2; i++){
            // 			temp[0] = points_p[2*i];	
            // 			temp[1] = points_p[2*i+1];
            // 			points_p[2*i] = points_p[2*(numpoints_p-1)-2*i];
            // 			points_p[2*i+1] = points_p[2*(numpoints_p-1)+1-2*i];
            // 			points_p[2*(numpoints_p-1)-2*i] = temp[0];
            // 			points_p[2*(numpoints_p-1)+1-2*i] = temp[1];
            // 		}
            // 	}
            // 	m.counterclockcount--;
            // INTERSECTION OF PETALS
            // first check whether the star angles are appropriate for relocation
            if (torg.type == VertexType.FreeVertex && numpoints_p != 0 && ValidPolygonAngles(numpoints_p, points_p))
            {
                //newLocFound = getPetalIntersection(m, b, numpoints_p, points_p, newloc);
                //newLocFound = getPetalIntersectionBruteForce(m, b,numpoints_p, points_p, newloc,torg[0],torg[1]);
                if (behavior.MaxAngle == 0.0)
                {
                    newLocFound = GetWedgeIntersectionWithoutMaxAngle(numpoints_p, points_p, ref newloc);
                }
                else
                {
                    newLocFound = GetWedgeIntersection(numpoints_p, points_p, ref newloc);
                }
                //printf("call petal intersection for p\n");
                // make sure the relocated point is a free vertex	
                if (newLocFound)
                {
                    possibilities[0] = newloc[0];// something found
                    possibilities[1] = newloc[1];
                    num_pos++;// increase the number of possibilities
                    flag1 = 1;
                }
            }

            //********************* TRY TO RELOCATE POINT "q" ***************		

            // get the surrounding points of q, so this gives us the triangles
            numpoints_q = GetStarPoints(badotri, torg, tdest, tapex, 2, ref points_q);
            // 	// check if the points in counterclockwise order
            // 	v1[0] = points_q[0];  v1[1] = points_q[1];
            // 	v2[0] = points_q[2];  v2[1] = points_q[3];
            // 	v3[0] = points_q[4];  v3[1] = points_q[5];
            // 	if(counterclockwise(m,b,v1,v2,v3) < 0){
            // 		// reverse the order to ccw
            // 		for(i = 0; i < numpoints_q/2; i++){
            // 			temp[0] = points_q[2*i];	
            // 			temp[1] = points_q[2*i+1];
            // 			points_q[2*i] = points_q[2*(numpoints_q-1)-2*i];
            // 			points_q[2*i+1] = points_q[2*(numpoints_q-1)+1-2*i];
            // 			points_q[2*(numpoints_q-1)-2*i] = temp[0];
            // 			points_q[2*(numpoints_q-1)+1-2*i] = temp[1];
            // 		}
            // 	}
            // 	m.counterclockcount--;
            // INTERSECTION OF PETALS
            // first check whether the star angles are appropriate for relocation
            if (tdest.type == VertexType.FreeVertex && numpoints_q != 0 && ValidPolygonAngles(numpoints_q, points_q))
            {
                //newLocFound = getPetalIntersection(m, b,numpoints_q, points_q, newloc);
                //newLocFound = getPetalIntersectionBruteForce(m, b,numpoints_q, points_q, newloc,tapex[0],tapex[1]);
                if (behavior.MaxAngle == 0.0)
                {
                    newLocFound = GetWedgeIntersectionWithoutMaxAngle(numpoints_q, points_q, ref newloc);
                }
                else
                {
                    newLocFound = GetWedgeIntersection(numpoints_q, points_q, ref newloc);
                }
                //printf("call petal intersection for q\n");	

                // make sure the relocated point is a free vertex	
                if (newLocFound)
                {
                    possibilities[2] = newloc[0];// something found
                    possibilities[3] = newloc[1];
                    num_pos++;// increase the number of possibilities
                    flag2 = 2;
                }
            }


            //********************* TRY TO RELOCATE POINT "q" ***************		
            // get the surrounding points of r, so this gives us the triangles
            numpoints_r = GetStarPoints(badotri, torg, tdest, tapex, 3, ref points_r);
            // check if the points in counterclockwise order
            // 	v1[0] = points_r[0];  v1[1] = points_r[1];
            // 	v2[0] = points_r[2];  v2[1] = points_r[3];
            // 	v3[0] = points_r[4];  v3[1] = points_r[5];
            // 	if(counterclockwise(m,b,v1,v2,v3) < 0){
            // 		// reverse the order to ccw
            // 		for(i = 0; i < numpoints_r/2; i++){
            // 			temp[0] = points_r[2*i];	
            // 			temp[1] = points_r[2*i+1];
            // 			points_r[2*i] = points_r[2*(numpoints_r-1)-2*i];
            // 			points_r[2*i+1] = points_r[2*(numpoints_r-1)+1-2*i];
            // 			points_r[2*(numpoints_r-1)-2*i] = temp[0];
            // 			points_r[2*(numpoints_r-1)+1-2*i] = temp[1];
            // 		}
            // 	}
            // 	m.counterclockcount--;
            // INTERSECTION OF PETALS
            // first check whether the star angles are appropriate for relocation
            if (tapex.type == VertexType.FreeVertex && numpoints_r != 0 && ValidPolygonAngles(numpoints_r, points_r))
            {
                //newLocFound = getPetalIntersection(m, b,numpoints_r, points_r, newloc);
                //newLocFound = getPetalIntersectionBruteForce(m, b,numpoints_r, points_r, newloc,tdest[0],tdest[1]);
                if (behavior.MaxAngle == 0.0)
                {
                    newLocFound = GetWedgeIntersectionWithoutMaxAngle(numpoints_r, points_r, ref newloc);
                }
                else
                {
                    newLocFound = GetWedgeIntersection(numpoints_r, points_r, ref newloc);
                }

                //printf("call petal intersection for r\n");


                // make sure the relocated point is a free vertex	
                if (newLocFound)
                {
                    possibilities[4] = newloc[0];// something found
                    possibilities[5] = newloc[1];
                    num_pos++;// increase the number of possibilities
                    flag3 = 3;
                }
            }
            //printf("numpossibilities %d\n",num_pos);
            //////////// AFTER FINISH CHECKING EVERY POSSIBILITY, CHOOSE ANY OF THE AVAILABLE ONE //////////////////////	
            if (num_pos > 0)
            {
                if (flag1 > 0)
                { // suggest to relocate origin
                    newloc[0] = possibilities[0];
                    newloc[1] = possibilities[1];
                    return flag1;

                }
                else
                {
                    if (flag2 > 0)
                    { // suggest to relocate apex
                        newloc[0] = possibilities[2];
                        newloc[1] = possibilities[3];
                        return flag2;

                    }
                    else
                    {// suggest to relocate destination
                        if (flag3 > 0)
                        {
                            newloc[0] = possibilities[4];
                            newloc[1] = possibilities[5];
                            return flag3;

                        }
                    }
                }
            }

            return 0;// could not find any good relocation
        }
        /// <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.Lprev();
                    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 = predicates.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 = predicates.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.Lprev();
                            return LocateResult.OnEdge;
                        }
                        if (orgorient == 0.0)
                        {
                            searchtri.Lnext();
                            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.Pivot(ref checkedge);
                    if (checkedge.seg.hash != Mesh.DUMMY)
                    {
                        // Go back to the last triangle.
                        backtracktri.Copy(ref searchtri);
                        return LocateResult.Outside;
                    }
                }
                // Check for walking right out of the triangulation.
                if (searchtri.tri.id == Mesh.DUMMY)
                {
                    // Go back to the last triangle.
                    backtracktri.Copy(ref searchtri);
                    return LocateResult.Outside;
                }

                fapex = searchtri.Apex();
            }
        }
Example #26
0
        /// <summary>
        /// Gets a neighbours vertex.
        /// </summary>
        /// <param name="badotri"></param>
        /// <param name="first_x"></param>
        /// <param name="first_y"></param>
        /// <param name="second_x"></param>
        /// <param name="second_y"></param>
        /// <param name="thirdpoint">Neighbor's third vertex incident to given edge.</param>
        /// <param name="neighotri">Pointer for the neighbor triangle.</param>
        /// <returns>Returns true if vertex was found.</returns>
        private bool GetNeighborsVertex(Otri badotri,
                        double first_x, double first_y,
                        double second_x, double second_y,
                        ref double[] thirdpoint, ref Otri neighotri)
        {

            Otri neighbor = default(Otri); // keeps the neighbor triangles
            bool notFound = false;	// boolean variable if we can find that neighbor or not

            // for keeping the vertices of the neighbor triangle
            Vertex neighborvertex_1 = null;
            Vertex neighborvertex_2 = null;
            Vertex neighborvertex_3 = null;

            // used for finding neighbor triangle
            int firstVertexMatched = 0, secondVertexMatched = 0;	// to find the correct neighbor
            //triangle ptr;             // Temporary variable used by sym()
            //int i;	// index variable	
            // find neighbors
            // Check each of the triangle's three neighbors to find the correct one
            for (badotri.orient = 0; badotri.orient < 3; badotri.orient++)
            {
                // Find the neighbor.
                badotri.Sym(ref neighbor);
                // check if it is the one we are looking for by checking the corners			
                // first check if the neighbor is nonexistent, since it can be on the border
                if (neighbor.tri.id != Mesh.DUMMY)
                {
                    // then check if two wanted corners are also in this triangle
                    // take the vertices of the candidate neighbor		
                    neighborvertex_1 = neighbor.Org();
                    neighborvertex_2 = neighbor.Dest();
                    neighborvertex_3 = neighbor.Apex();

                    // check if it is really a triangle
                    if ((neighborvertex_1.x == neighborvertex_2.x && neighborvertex_1.y == neighborvertex_2.y)
                     || (neighborvertex_2.x == neighborvertex_3.x && neighborvertex_2.y == neighborvertex_3.y)
                     || (neighborvertex_1.x == neighborvertex_3.x && neighborvertex_1.y == neighborvertex_3.y))
                    {
                        //printf("Two vertices are the same!!!!!!!\n");
                    }
                    else
                    {
                        // begin searching for the correct neighbor triangle
                        firstVertexMatched = 0;
                        if ((Math.Abs(first_x - neighborvertex_1.x) < EPS) &&
                             (Math.Abs(first_y - neighborvertex_1.y) < EPS))
                        {
                            firstVertexMatched = 11; // neighbor's 1st vertex is matched to first vertex

                        }
                        else if ((Math.Abs(first_x - neighborvertex_2.x) < EPS) &&
                               (Math.Abs(first_y - neighborvertex_2.y) < EPS))
                        {
                            firstVertexMatched = 12; // neighbor's 2nd vertex is matched to first vertex

                        }
                        else if ((Math.Abs(first_x - neighborvertex_3.x) < EPS) &&
                                   (Math.Abs(first_y - neighborvertex_3.y) < EPS))
                        {
                            firstVertexMatched = 13; // neighbor's 3rd vertex is matched to first vertex

                        }/*else{	
                     // none of them matched
                } // end of first vertex matching */

                        secondVertexMatched = 0;
                        if ((Math.Abs(second_x - neighborvertex_1.x) < EPS) &&
                            (Math.Abs(second_y - neighborvertex_1.y) < EPS))
                        {
                            secondVertexMatched = 21; // neighbor's 1st vertex is matched to second vertex
                        }
                        else if ((Math.Abs(second_x - neighborvertex_2.x) < EPS) &&
                           (Math.Abs(second_y - neighborvertex_2.y) < EPS))
                        {
                            secondVertexMatched = 22; // neighbor's 2nd vertex is matched to second vertex
                        }
                        else if ((Math.Abs(second_x - neighborvertex_3.x) < EPS) &&
                               (Math.Abs(second_y - neighborvertex_3.y) < EPS))
                        {
                            secondVertexMatched = 23; // neighbor's 3rd vertex is matched to second vertex
                        }/*else{	
                    // none of them matched
                } // end of second vertex matching*/

                    }

                }// if neighbor exists or not

                if (((firstVertexMatched == 11) && (secondVertexMatched == 22 || secondVertexMatched == 23))
                 || ((firstVertexMatched == 12) && (secondVertexMatched == 21 || secondVertexMatched == 23))
                 || ((firstVertexMatched == 13) && (secondVertexMatched == 21 || secondVertexMatched == 22)))
                    break;
            }// end of for loop over all orientations

            switch (firstVertexMatched)
            {
                case 0:
                    notFound = true;
                    break;
                case 11:
                    if (secondVertexMatched == 22)
                    {
                        thirdpoint[0] = neighborvertex_3.x;
                        thirdpoint[1] = neighborvertex_3.y;
                    }
                    else if (secondVertexMatched == 23)
                    {
                        thirdpoint[0] = neighborvertex_2.x;
                        thirdpoint[1] = neighborvertex_2.y;
                    }
                    else { notFound = true; }
                    break;
                case 12:
                    if (secondVertexMatched == 21)
                    {
                        thirdpoint[0] = neighborvertex_3.x;
                        thirdpoint[1] = neighborvertex_3.y;
                    }
                    else if (secondVertexMatched == 23)
                    {
                        thirdpoint[0] = neighborvertex_1.x;
                        thirdpoint[1] = neighborvertex_1.y;
                    }
                    else { notFound = true; }
                    break;
                case 13:
                    if (secondVertexMatched == 21)
                    {
                        thirdpoint[0] = neighborvertex_2.x;
                        thirdpoint[1] = neighborvertex_2.y;
                    }
                    else if (secondVertexMatched == 22)
                    {
                        thirdpoint[0] = neighborvertex_1.x;
                        thirdpoint[1] = neighborvertex_1.y;
                    }
                    else { notFound = true; }
                    break;
                default:
                    if (secondVertexMatched == 0) { notFound = true; }
                    break;
            }
            // pointer of the neighbor triangle
            neighotri = neighbor;
            return notFound;

        }
 /// <summary>
 /// Suggest the given triangle as a starting triangle for point location.
 /// </summary>
 /// <param name="otri"></param>
 public void Update(ref Otri otri)
 {
     otri.Copy(ref recenttri);
 }
Example #28
0
 /// <summary>
 /// Find the previous edge (clockwise) of a triangle. [lprev(abc) -> cab]
 /// </summary>
 public void Lprev(ref Otri ot)
 {
     ot.tri    = tri;
     ot.orient = minus1Mod3[orient];
 }
Example #29
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.Lnext();
                    farleft.Sym();
                    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.Lnext();
                    innerright.Sym();
                    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 (predicates.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0)
                {
                    innerleft.Lprev();
                    innerleft.Sym();
                    innerleftdest = innerleftapex;
                    innerleftapex = innerleft.Apex();
                    changemade = true;
                }
                // Make innerrightorg the "bottommost" vertex of the right hull.
                if (predicates.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0)
                {
                    innerright.Lnext();
                    innerright.Sym();
                    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.Lnext();
            baseedge.Bond(ref innerright);
            baseedge.Lnext();
            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 = predicates.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0;
                rightfinished = predicates.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.Lnext();
                    nextedge.Bond(ref rightcand);
                    nextedge.Lnext();
                    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.Lprev();
                            farright.Sym();
                            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.Sym();
                    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 = predicates.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.Lnext();
                            nextedge.Sym(ref topcasing);
                            nextedge.Lnext();
                            nextedge.Sym(ref sidecasing);
                            nextedge.Bond(ref topcasing);
                            leftcand.Bond(ref sidecasing);
                            leftcand.Lnext();
                            leftcand.Sym(ref outercasing);
                            nextedge.Lprev();
                            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 = predicates.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.Sym();
                    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 = predicates.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.Lprev();
                            nextedge.Sym(ref topcasing);
                            nextedge.Lprev();
                            nextedge.Sym(ref sidecasing);
                            nextedge.Bond(ref topcasing);
                            rightcand.Bond(ref sidecasing);
                            rightcand.Lprev();
                            rightcand.Sym(ref outercasing);
                            nextedge.Lnext();
                            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 = predicates.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
                            }
                            else
                            {
                                // Avoid eating right through the triangulation.
                                badedge = false;
                            }
                        }
                    }
                }
                if (leftfinished || (!rightfinished &&
                       (predicates.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();
                }
            }
        }
Example #30
0
 /// <summary>
 /// Copy an oriented triangle.
 /// </summary>
 public void Copy(ref Otri ot)
 {
     ot.tri    = tri;
     ot.orient = orient;
 }
Example #31
0
        /// <summary>
        /// Finds the star of a given point.
        /// </summary>
        /// <param name="badotri"></param>
        /// <param name="p"></param>
        /// <param name="q"></param>
        /// <param name="r"></param>
        /// <param name="whichPoint"></param>
        /// <param name="points">List of points on the star of the given point.</param>
        /// <returns>Number of points on the star of the given point.</returns>
        private int GetStarPoints(Otri badotri, Vertex p, Vertex q, Vertex r,
                    int whichPoint, ref double[] points)
        {

            Otri neighotri = default(Otri);  // for return value of the function
            Otri tempotri;   // for temporary usage
            double first_x = 0, first_y = 0;	  // keeps the first point to be considered
            double second_x = 0, second_y = 0;  // for determining the edge we will begin
            double third_x = 0, third_y = 0;	  // termination
            double[] returnPoint = new double[2];	  // for keeping the returned point	
            int numvertices = 0;	  // for keeping number of surrounding vertices

            // first determine which point to be used to find its neighbor triangles
            switch (whichPoint)
            {
                case 1:
                    first_x = p.x;	// point at the center
                    first_y = p.y;
                    second_x = r.x; // second vertex of first edge to consider
                    second_y = r.y;
                    third_x = q.x;  // for terminating the search
                    third_y = q.y;
                    break;
                case 2:
                    first_x = q.x;  // point at the center
                    first_y = q.y;
                    second_x = p.x; // second vertex of first edge to consider
                    second_y = p.y;
                    third_x = r.x;	// for terminating the search
                    third_y = r.y;
                    break;
                case 3:
                    first_x = r.x;	// point at the center
                    first_y = r.y;
                    second_x = q.x; // second vertex of first edge to consider
                    second_y = q.y;
                    third_x = p.x;	// for terminating the search
                    third_y = p.y;
                    break;
            }
            tempotri = badotri;
            // add first point as the end of first edge
            points[numvertices] = second_x;
            numvertices++;
            points[numvertices] = second_y;
            numvertices++;
            // assign as dummy value
            returnPoint[0] = second_x; returnPoint[1] = second_y;
            // until we reach the third point of the beginning triangle	
            do
            {
                // find the neighbor's third point where it is incident to given edge
                if (!GetNeighborsVertex(tempotri, first_x, first_y, second_x, second_y, ref returnPoint, ref neighotri))
                {
                    // go to next triangle
                    tempotri = neighotri;
                    // now the second point is the neighbor's third vertex			
                    second_x = returnPoint[0];
                    second_y = returnPoint[1];
                    // add a new point to the list of surrounding points
                    points[numvertices] = returnPoint[0];
                    numvertices++;
                    points[numvertices] = returnPoint[1];
                    numvertices++;
                }
                else
                {
                    numvertices = 0;
                    break;
                }

            } while (!((Math.Abs(returnPoint[0] - third_x) <= EPS) &&
                     (Math.Abs(returnPoint[1] - third_y) <= EPS)));
            return numvertices / 2;

        }
Example #32
0
        /// <summary>
        /// Recursively form a Delaunay triangulation by the divide-and-conquer method.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="axis"></param>
        /// <param name="farleft"></param>
        /// <param name="farright"></param>
        /// <remarks>
        /// Recursively breaks down the problem into smaller pieces, which are
        /// knitted together by mergehulls(). The base cases (problems of two or
        /// three vertices) are handled specially here.
        ///
        /// On completion, 'farleft' and 'farright' are bounding triangles such that
        /// the origin of 'farleft' is the leftmost vertex (breaking ties by
        /// choosing the highest leftmost vertex), and the destination of
        /// 'farright' is the rightmost vertex (breaking ties by choosing the
        /// lowest rightmost vertex).
        /// </remarks>
        void DivconqRecurse(int left, int right, int axis,
                            ref Otri farleft, ref Otri farright)
        {
            Otri midtri = default(Otri);
            Otri tri1 = default(Otri);
            Otri tri2 = default(Otri);
            Otri tri3 = default(Otri);
            Otri innerleft = default(Otri), innerright = default(Otri);
            double area;
            int vertices = right - left + 1;
            int divider;

            if (vertices == 2)
            {
                // The triangulation of two vertices is an edge.  An edge is
                // represented by two bounding triangles.
                mesh.MakeTriangle(ref farleft);
                farleft.SetOrg(sortarray[left]);
                farleft.SetDest(sortarray[left + 1]);
                // The apex is intentionally left NULL.
                mesh.MakeTriangle(ref farright);
                farright.SetOrg(sortarray[left + 1]);
                farright.SetDest(sortarray[left]);
                // The apex is intentionally left NULL.
                farleft.Bond(ref farright);
                farleft.Lprev();
                farright.Lnext();
                farleft.Bond(ref farright);
                farleft.Lprev();
                farright.Lnext();
                farleft.Bond(ref farright);

                // Ensure that the origin of 'farleft' is sortarray[0].
                farright.Lprev(ref farleft);
                return;
            }
            else if (vertices == 3)
            {
                // The triangulation of three vertices is either a triangle (with
                // three bounding triangles) or two edges (with four bounding
                // triangles).  In either case, four triangles are created.
                mesh.MakeTriangle(ref midtri);
                mesh.MakeTriangle(ref tri1);
                mesh.MakeTriangle(ref tri2);
                mesh.MakeTriangle(ref tri3);
                area = predicates.CounterClockwise(sortarray[left], sortarray[left + 1], sortarray[left + 2]);
                if (area == 0.0)
                {
                    // Three collinear vertices; the triangulation is two edges.
                    midtri.SetOrg(sortarray[left]);
                    midtri.SetDest(sortarray[left + 1]);
                    tri1.SetOrg(sortarray[left + 1]);
                    tri1.SetDest(sortarray[left]);
                    tri2.SetOrg(sortarray[left + 2]);
                    tri2.SetDest(sortarray[left + 1]);
                    tri3.SetOrg(sortarray[left + 1]);
                    tri3.SetDest(sortarray[left + 2]);
                    // All apices are intentionally left NULL.
                    midtri.Bond(ref tri1);
                    tri2.Bond(ref tri3);
                    midtri.Lnext();
                    tri1.Lprev();
                    tri2.Lnext();
                    tri3.Lprev();
                    midtri.Bond(ref tri3);
                    tri1.Bond(ref tri2);
                    midtri.Lnext();
                    tri1.Lprev();
                    tri2.Lnext();
                    tri3.Lprev();
                    midtri.Bond(ref tri1);
                    tri2.Bond(ref tri3);
                    // Ensure that the origin of 'farleft' is sortarray[0].
                    tri1.Copy(ref farleft);
                    // Ensure that the destination of 'farright' is sortarray[2].
                    tri2.Copy(ref farright);
                }
                else
                {
                    // The three vertices are not collinear; the triangulation is one
                    // triangle, namely 'midtri'.
                    midtri.SetOrg(sortarray[left]);
                    tri1.SetDest(sortarray[left]);
                    tri3.SetOrg(sortarray[left]);
                    // Apices of tri1, tri2, and tri3 are left NULL.
                    if (area > 0.0)
                    {
                        // The vertices are in counterclockwise order.
                        midtri.SetDest(sortarray[left + 1]);
                        tri1.SetOrg(sortarray[left + 1]);
                        tri2.SetDest(sortarray[left + 1]);
                        midtri.SetApex(sortarray[left + 2]);
                        tri2.SetOrg(sortarray[left + 2]);
                        tri3.SetDest(sortarray[left + 2]);
                    }
                    else
                    {
                        // The vertices are in clockwise order.
                        midtri.SetDest(sortarray[left + 2]);
                        tri1.SetOrg(sortarray[left + 2]);
                        tri2.SetDest(sortarray[left + 2]);
                        midtri.SetApex(sortarray[left + 1]);
                        tri2.SetOrg(sortarray[left + 1]);
                        tri3.SetDest(sortarray[left + 1]);
                    }
                    // The topology does not depend on how the vertices are ordered.
                    midtri.Bond(ref tri1);
                    midtri.Lnext();
                    midtri.Bond(ref tri2);
                    midtri.Lnext();
                    midtri.Bond(ref tri3);
                    tri1.Lprev();
                    tri2.Lnext();
                    tri1.Bond(ref tri2);
                    tri1.Lprev();
                    tri3.Lprev();
                    tri1.Bond(ref tri3);
                    tri2.Lnext();
                    tri3.Lprev();
                    tri2.Bond(ref tri3);
                    // Ensure that the origin of 'farleft' is sortarray[0].
                    tri1.Copy(ref farleft);
                    // Ensure that the destination of 'farright' is sortarray[2].
                    if (area > 0.0)
                    {
                        tri2.Copy(ref farright);
                    }
                    else
                    {
                        farleft.Lnext(ref farright);
                    }
                }

                return;
            }
            else
            {
                // Split the vertices in half.
                divider = vertices >> 1;
                // Recursively triangulate each half.
                DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft);
                //DebugWriter.Session.Write(mesh, true);
                DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright);
                //DebugWriter.Session.Write(mesh, true);

                // Merge the two triangulations into one.
                MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis);
                //DebugWriter.Session.Write(mesh, true);
            }
        }
Example #33
0
        /// <summary>
        /// Given the triangulation, and a vertex returns the minimum distance to the 
        /// vertices of the triangle where the given vertex located.
        /// </summary>
        /// <param name="newlocX"></param>
        /// <param name="newlocY"></param>
        /// <param name="searchtri"></param>
        /// <returns></returns>
        private double MinDistanceToNeighbor(double newlocX, double newlocY, ref Otri searchtri)
        {
            Otri horiz = default(Otri);	// for search operation
            LocateResult intersect = LocateResult.Outside;
            Vertex v1, v2, v3, torg, tdest;
            double d1, d2, d3, ahead;
            //triangle ptr;                         // Temporary variable used by sym().

            Point newvertex = new Point(newlocX, newlocY);

            // 	printf("newvertex %f,%f\n", newvertex[0], newvertex[1]);
            // Find the location of the vertex to be inserted.  Check if a good
            //   starting triangle has already been provided by the caller.	
            // Find a boundary triangle.
            //horiz.tri = m.dummytri;
            //horiz.orient = 0;
            //horiz.symself();
            // Search for a triangle containing 'newvertex'.
            // Start searching from the triangle provided by the caller.
            // Where are we?
            torg = searchtri.Org();
            tdest = searchtri.Dest();
            // Check the starting triangle's vertices.
            if ((torg.x == newvertex.x) && (torg.y == newvertex.y))
            {
                intersect = LocateResult.OnVertex;
                searchtri.Copy(ref horiz);

            }
            else if ((tdest.x == newvertex.x) && (tdest.y == newvertex.y))
            {
                searchtri.Lnext();
                intersect = LocateResult.OnVertex;
                searchtri.Copy(ref horiz);
            }
            else
            {
                // Orient 'searchtri' to fit the preconditions of calling preciselocate().
                ahead = predicates.CounterClockwise(torg, tdest, newvertex);
                if (ahead < 0.0)
                {
                    // Turn around so that 'searchpoint' is to the left of the
                    // edge specified by 'searchtri'.
                    searchtri.Sym();
                    searchtri.Copy(ref horiz);
                    intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false);
                }
                else if (ahead == 0.0)
                {
                    // Check if 'searchpoint' is between 'torg' and 'tdest'.
                    if (((torg.x < newvertex.x) == (newvertex.x < tdest.x)) &&
                        ((torg.y < newvertex.y) == (newvertex.y < tdest.y)))
                    {
                        intersect = LocateResult.OnEdge;
                        searchtri.Copy(ref horiz);

                    }
                }
                else
                {
                    searchtri.Copy(ref horiz);
                    intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false);
                }
            }
            if (intersect == LocateResult.OnVertex || intersect == LocateResult.Outside)
            {
                // set distance to 0
                //m.VertexDealloc(newvertex);
                return 0.0;
            }
            else
            { // intersect == ONEDGE || intersect == INTRIANGLE
                // find the triangle vertices
                v1 = horiz.Org();
                v2 = horiz.Dest();
                v3 = horiz.Apex();
                d1 = (v1.x - newvertex.x) * (v1.x - newvertex.x) + (v1.y - newvertex.y) * (v1.y - newvertex.y);
                d2 = (v2.x - newvertex.x) * (v2.x - newvertex.x) + (v2.y - newvertex.y) * (v2.y - newvertex.y);
                d3 = (v3.x - newvertex.x) * (v3.x - newvertex.x) + (v3.y - newvertex.y) * (v3.y - newvertex.y);
                //m.VertexDealloc(newvertex);
                // find minimum of the distance
                if (d1 <= d2 && d1 <= d3)
                {
                    return d1;
                }
                else if (d2 <= d3)
                {
                    return d2;
                }
                else
                {
                    return d3;
                }
            }
        }
Example #34
0
        /// <summary>
        /// Removes ghost triangles.
        /// </summary>
        /// <param name="startghost"></param>
        /// <returns>Number of vertices on the hull.</returns>
        int RemoveGhosts(ref Otri startghost)
        {
            Otri searchedge = default(Otri);
            Otri dissolveedge = default(Otri);
            Otri deadtriangle = default(Otri);
            Vertex markorg;

            int hullsize;

            bool noPoly = !mesh.behavior.Poly;

            // Find an edge on the convex hull to start point location from.
            startghost.Lprev(ref searchedge);
            searchedge.Sym();
            mesh.dummytri.neighbors[0] = searchedge;

            // Remove the bounding box and count the convex hull edges.
            startghost.Copy(ref dissolveedge);
            hullsize = 0;
            do
            {
                hullsize++;
                dissolveedge.Lnext(ref deadtriangle);
                dissolveedge.Lprev();
                dissolveedge.Sym();

                // If no PSLG is involved, set the boundary markers of all the vertices
                // on the convex hull.  If a PSLG is used, this step is done later.
                if (noPoly)
                {
                    // Watch out for the case where all the input vertices are collinear.
                    if (dissolveedge.tri.id != Mesh.DUMMY)
                    {
                        markorg = dissolveedge.Org();
                        if (markorg.label == 0)
                        {
                            markorg.label = 1;
                        }
                    }
                }
                // Remove a bounding triangle from a convex hull triangle.
                dissolveedge.Dissolve(mesh.dummytri);
                // Find the next bounding triangle.
                deadtriangle.Sym(ref dissolveedge);

                // Delete the bounding triangle.
                mesh.TriangleDealloc(deadtriangle.tri);
            } while (!dissolveedge.Equals(startghost));

            return hullsize;
        }
Example #35
0
        /// <summary>
        /// Find a new location for a Steiner point.
        /// </summary>
        /// <param name="torg"></param>
        /// <param name="tdest"></param>
        /// <param name="tapex"></param>
        /// <param name="circumcenter"></param>
        /// <param name="xi"></param>
        /// <param name="eta"></param>
        /// <param name="offcenter"></param>
        /// <param name="badotri"></param>
        private Point FindNewLocationWithoutMaxAngle(Vertex torg, Vertex tdest, Vertex tapex,
            ref double xi, ref double eta, bool offcenter, Otri badotri)
        {
            double offconstant = behavior.offconstant;

            // for calculating the distances of the edges
            double xdo, ydo, xao, yao, xda, yda;
            double dodist, aodist, dadist;
            // for exact calculation
            double denominator;
            double dx, dy, dxoff, dyoff;

            ////////////////////////////// HALE'S VARIABLES //////////////////////////////
            // keeps the difference of coordinates edge 
            double xShortestEdge = 0, yShortestEdge = 0, xMiddleEdge, yMiddleEdge, xLongestEdge, yLongestEdge;

            // keeps the square of edge lengths
            double shortestEdgeDist = 0, middleEdgeDist = 0, longestEdgeDist = 0;

            // keeps the vertices according to the angle incident to that vertex in a triangle
            Point smallestAngleCorner, middleAngleCorner, largestAngleCorner;

            // keeps the type of orientation if the triangle
            int orientation = 0;
            // keeps the coordinates of circumcenter of itself and neighbor triangle circumcenter	
            Point myCircumcenter, neighborCircumcenter;

            // keeps if bad triangle is almost good or not
            int almostGood = 0;
            // keeps the cosine of the largest angle
            double cosMaxAngle;
            bool isObtuse; // 1: obtuse 0: nonobtuse
            // keeps the radius of petal
            double petalRadius;
            // for calculating petal center
            double xPetalCtr_1, yPetalCtr_1, xPetalCtr_2, yPetalCtr_2, xPetalCtr, yPetalCtr, xMidOfShortestEdge, yMidOfShortestEdge;
            double dxcenter1, dycenter1, dxcenter2, dycenter2;
            // for finding neighbor
            Otri neighborotri = default(Otri);
            double[] thirdPoint = new double[2];
            //int neighborNotFound = -1;
            bool neighborNotFound;
            // for keeping the vertices of the neighbor triangle
            Vertex neighborvertex_1;
            Vertex neighborvertex_2;
            Vertex neighborvertex_3;
            // dummy variables 
            double xi_tmp = 0, eta_tmp = 0;
            //vertex thirdVertex;
            // for petal intersection
            double vector_x, vector_y, xMidOfLongestEdge, yMidOfLongestEdge, inter_x, inter_y;
            double[] p = new double[5], voronoiOrInter = new double[4];
            bool isCorrect;

            // for vector calculations in perturbation
            double ax, ay, d;
            double pertConst = 0.06; // perturbation constant

            double lengthConst = 1; // used at comparing circumcenter's distance to proposed point's distance
            double justAcute = 1; // used for making the program working for one direction only
            // for smoothing
            int relocated = 0;// used to differentiate between calling the deletevertex and just proposing a steiner point
            double[] newloc = new double[2];   // new location suggested by smoothing
            double origin_x = 0, origin_y = 0; // for keeping torg safe
            Otri delotri; // keeping the original orientation for relocation process
            // keeps the first and second direction suggested points
            double dxFirstSuggestion, dyFirstSuggestion, dxSecondSuggestion, dySecondSuggestion;
            // second direction variables
            double xMidOfMiddleEdge, yMidOfMiddleEdge;
            ////////////////////////////// END OF HALE'S VARIABLES //////////////////////////////

            Statistic.CircumcenterCount++;

            // Compute the circumcenter of the triangle.
            xdo = tdest.x - torg.x;
            ydo = tdest.y - torg.y;
            xao = tapex.x - torg.x;
            yao = tapex.y - torg.y;
            xda = tapex.x - tdest.x;
            yda = tapex.y - tdest.y;
            // keeps the square of the distances
            dodist = xdo * xdo + ydo * ydo;
            aodist = xao * xao + yao * yao;
            dadist = (tdest.x - tapex.x) * (tdest.x - tapex.x) +
                (tdest.y - tapex.y) * (tdest.y - tapex.y);
            // checking if the user wanted exact arithmetic or not
            if (Behavior.NoExact)
            {
                denominator = 0.5 / (xdo * yao - xao * ydo);
            }
            else
            {
                // Use the counterclockwise() routine to ensure a positive (and
                //   reasonably accurate) result, avoiding any possibility of
                //   division by zero.
                denominator = 0.5 / predicates.CounterClockwise(tdest, tapex, torg);
                // Don't count the above as an orientation test.
                Statistic.CounterClockwiseCount--;
            }
            // calculate the circumcenter in terms of distance to origin point 
            dx = (yao * dodist - ydo * aodist) * denominator;
            dy = (xdo * aodist - xao * dodist) * denominator;
            // for debugging and for keeping circumcenter to use later
            // coordinate value of the circumcenter
            myCircumcenter = new Point(torg.x + dx, torg.y + dy);

            delotri = badotri; // save for later
            ///////////////// FINDING THE ORIENTATION OF TRIANGLE //////////////////
            // Find the (squared) length of the triangle's shortest edge.  This
            //   serves as a conservative estimate of the insertion radius of the
            //   circumcenter's parent.  The estimate is used to ensure that
            //   the algorithm terminates even if very small angles appear in
            //   the input PSLG. 						
            // find the orientation of the triangle, basically shortest and longest edges
            orientation = LongestShortestEdge(aodist, dadist, dodist);
            //printf("org: (%f,%f), dest: (%f,%f), apex: (%f,%f)\n",torg[0],torg[1],tdest[0],tdest[1],tapex[0],tapex[1]);
            /////////////////////////////////////////////////////////////////////////////////////////////
            // 123: shortest: aodist	// 213: shortest: dadist	// 312: shortest: dodist   //	
            //	middle: dadist 		//	middle: aodist 		//	middle: aodist     //
            //	longest: dodist		//	longest: dodist		//	longest: dadist    //
            // 132: shortest: aodist 	// 231: shortest: dadist 	// 321: shortest: dodist   //
            //	middle: dodist 		//	middle: dodist 		//	middle: dadist     //
            //	longest: dadist		//	longest: aodist		//	longest: aodist    //
            /////////////////////////////////////////////////////////////////////////////////////////////

            switch (orientation)
            {
                case 123: 	// assign necessary information
                    /// smallest angle corner: dest
                    /// largest angle corner: apex
                    xShortestEdge = xao; yShortestEdge = yao;
                    xMiddleEdge = xda; yMiddleEdge = yda;
                    xLongestEdge = xdo; yLongestEdge = ydo;

                    shortestEdgeDist = aodist;
                    middleEdgeDist = dadist;
                    longestEdgeDist = dodist;

                    smallestAngleCorner = tdest;
                    middleAngleCorner = torg;
                    largestAngleCorner = tapex;
                    break;

                case 132: 	// assign necessary information
                    /// smallest angle corner: dest
                    /// largest angle corner: org
                    xShortestEdge = xao; yShortestEdge = yao;
                    xMiddleEdge = xdo; yMiddleEdge = ydo;
                    xLongestEdge = xda; yLongestEdge = yda;

                    shortestEdgeDist = aodist;
                    middleEdgeDist = dodist;
                    longestEdgeDist = dadist;

                    smallestAngleCorner = tdest;
                    middleAngleCorner = tapex;
                    largestAngleCorner = torg;

                    break;
                case 213: 	// assign necessary information
                    /// smallest angle corner: org
                    /// largest angle corner: apex
                    xShortestEdge = xda; yShortestEdge = yda;
                    xMiddleEdge = xao; yMiddleEdge = yao;
                    xLongestEdge = xdo; yLongestEdge = ydo;

                    shortestEdgeDist = dadist;
                    middleEdgeDist = aodist;
                    longestEdgeDist = dodist;

                    smallestAngleCorner = torg;
                    middleAngleCorner = tdest;
                    largestAngleCorner = tapex;
                    break;
                case 231: 	// assign necessary information
                    /// smallest angle corner: org
                    /// largest angle corner: dest
                    xShortestEdge = xda; yShortestEdge = yda;
                    xMiddleEdge = xdo; yMiddleEdge = ydo;
                    xLongestEdge = xao; yLongestEdge = yao;

                    shortestEdgeDist = dadist;
                    middleEdgeDist = dodist;
                    longestEdgeDist = aodist;

                    smallestAngleCorner = torg;
                    middleAngleCorner = tapex;
                    largestAngleCorner = tdest;
                    break;
                case 312: 	// assign necessary information
                    /// smallest angle corner: apex
                    /// largest angle corner: org
                    xShortestEdge = xdo; yShortestEdge = ydo;
                    xMiddleEdge = xao; yMiddleEdge = yao;
                    xLongestEdge = xda; yLongestEdge = yda;

                    shortestEdgeDist = dodist;
                    middleEdgeDist = aodist;
                    longestEdgeDist = dadist;

                    smallestAngleCorner = tapex;
                    middleAngleCorner = tdest;
                    largestAngleCorner = torg;
                    break;
                case 321: 	// assign necessary information
                default: // TODO: is this safe?
                    /// smallest angle corner: apex
                    /// largest angle corner: dest
                    xShortestEdge = xdo; yShortestEdge = ydo;
                    xMiddleEdge = xda; yMiddleEdge = yda;
                    xLongestEdge = xao; yLongestEdge = yao;

                    shortestEdgeDist = dodist;
                    middleEdgeDist = dadist;
                    longestEdgeDist = aodist;

                    smallestAngleCorner = tapex;
                    middleAngleCorner = torg;
                    largestAngleCorner = tdest;
                    break;

            }// end of switch	
            // check for offcenter condition
            if (offcenter && (offconstant > 0.0))
            {
                // origin has the smallest angle
                if (orientation == 213 || orientation == 231)
                {
                    // Find the position of the off-center, as described by Alper Ungor.
                    dxoff = 0.5 * xShortestEdge - offconstant * yShortestEdge;
                    dyoff = 0.5 * yShortestEdge + offconstant * xShortestEdge;
                    // If the off-center is closer to destination than the
                    //   circumcenter, use the off-center instead.
                    /// doubleLY BAD CASE ///			
                    if (dxoff * dxoff + dyoff * dyoff <
                        (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo))
                    {
                        dx = xdo + dxoff;
                        dy = ydo + dyoff;
                    }
                    /// ALMOST GOOD CASE ///
                    else
                    {
                        almostGood = 1;
                    }
                    // destination has the smallest angle	
                }
                else if (orientation == 123 || orientation == 132)
                {
                    // Find the position of the off-center, as described by Alper Ungor.
                    dxoff = 0.5 * xShortestEdge + offconstant * yShortestEdge;
                    dyoff = 0.5 * yShortestEdge - offconstant * xShortestEdge;
                    // If the off-center is closer to the origin than the
                    //   circumcenter, use the off-center instead.
                    /// doubleLY BAD CASE ///
                    if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy)
                    {
                        dx = dxoff;
                        dy = dyoff;
                    }
                    /// ALMOST GOOD CASE ///		
                    else
                    {
                        almostGood = 1;
                    }
                    // apex has the smallest angle	
                }
                else
                {//orientation == 312 || orientation == 321 
                    // Find the position of the off-center, as described by Alper Ungor.
                    dxoff = 0.5 * xShortestEdge - offconstant * yShortestEdge;
                    dyoff = 0.5 * yShortestEdge + offconstant * xShortestEdge;
                    // If the off-center is closer to the origin than the
                    //   circumcenter, use the off-center instead.
                    /// doubleLY BAD CASE ///
                    if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy)
                    {
                        dx = dxoff;
                        dy = dyoff;
                    }
                    /// ALMOST GOOD CASE ///		
                    else
                    {
                        almostGood = 1;
                    }
                }
            }
            // if the bad triangle is almost good, apply our approach
            if (almostGood == 1)
            {

                /// calculate cosine of largest angle	///	
                cosMaxAngle = (middleEdgeDist + shortestEdgeDist - longestEdgeDist) / (2 * Math.Sqrt(middleEdgeDist) * Math.Sqrt(shortestEdgeDist));
                if (cosMaxAngle < 0.0)
                {
                    // obtuse
                    isObtuse = true;
                }
                else if (Math.Abs(cosMaxAngle - 0.0) <= EPS)
                {
                    // right triangle (largest angle is 90 degrees)
                    isObtuse = true;
                }
                else
                {
                    // nonobtuse
                    isObtuse = false;
                }
                /// RELOCATION	(LOCAL SMOOTHING) ///
                /// check for possible relocation of one of triangle's points ///				
                relocated = DoSmoothing(delotri, torg, tdest, tapex, ref newloc);
                /// if relocation is possible, delete that vertex and insert a vertex at the new location ///		
                if (relocated > 0)
                {
                    Statistic.RelocationCount++;

                    dx = newloc[0] - torg.x;
                    dy = newloc[1] - torg.y;
                    origin_x = torg.x;	// keep for later use
                    origin_y = torg.y;
                    switch (relocated)
                    {
                        case 1:
                            //printf("Relocate: (%f,%f)\n", torg[0],torg[1]);			
                            mesh.DeleteVertex(ref delotri);
                            break;
                        case 2:
                            //printf("Relocate: (%f,%f)\n", tdest[0],tdest[1]);			
                            delotri.Lnext();
                            mesh.DeleteVertex(ref delotri);
                            break;
                        case 3:
                            //printf("Relocate: (%f,%f)\n", tapex[0],tapex[1]);						
                            delotri.Lprev();
                            mesh.DeleteVertex(ref delotri);
                            break;

                    }
                }
                else
                {
                    // calculate radius of the petal according to angle constraint
                    // first find the visible region, PETAL
                    // find the center of the circle and radius
                    petalRadius = Math.Sqrt(shortestEdgeDist) / (2 * Math.Sin(behavior.MinAngle * Math.PI / 180.0));
                    /// compute two possible centers of the petal ///
                    // finding the center
                    // first find the middle point of smallest edge
                    xMidOfShortestEdge = (middleAngleCorner.x + largestAngleCorner.x) / 2.0;
                    yMidOfShortestEdge = (middleAngleCorner.y + largestAngleCorner.y) / 2.0;
                    // two possible centers
                    xPetalCtr_1 = xMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y -
                        largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist);
                    yPetalCtr_1 = yMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x -
                        middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist);

                    xPetalCtr_2 = xMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y -
                        largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist);
                    yPetalCtr_2 = yMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x -
                        middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist);
                    // find the correct circle since there will be two possible circles
                    // calculate the distance to smallest angle corner
                    dxcenter1 = (xPetalCtr_1 - smallestAngleCorner.x) * (xPetalCtr_1 - smallestAngleCorner.x);
                    dycenter1 = (yPetalCtr_1 - smallestAngleCorner.y) * (yPetalCtr_1 - smallestAngleCorner.y);
                    dxcenter2 = (xPetalCtr_2 - smallestAngleCorner.x) * (xPetalCtr_2 - smallestAngleCorner.x);
                    dycenter2 = (yPetalCtr_2 - smallestAngleCorner.y) * (yPetalCtr_2 - smallestAngleCorner.y);

                    // whichever is closer to smallest angle corner, it must be the center
                    if (dxcenter1 + dycenter1 <= dxcenter2 + dycenter2)
                    {
                        xPetalCtr = xPetalCtr_1; yPetalCtr = yPetalCtr_1;
                    }
                    else
                    {
                        xPetalCtr = xPetalCtr_2; yPetalCtr = yPetalCtr_2;
                    }

                    /// find the third point of the neighbor triangle  ///
                    neighborNotFound = GetNeighborsVertex(badotri, middleAngleCorner.x, middleAngleCorner.y,
                                smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri);
                    /// find the circumcenter of the neighbor triangle ///
                    dxFirstSuggestion = dx;	// if we cannot find any appropriate suggestion, we use circumcenter
                    dyFirstSuggestion = dy;
                    // if there is a neighbor triangle
                    if (!neighborNotFound)
                    {
                        neighborvertex_1 = neighborotri.Org();
                        neighborvertex_2 = neighborotri.Dest();
                        neighborvertex_3 = neighborotri.Apex();
                        // now calculate neighbor's circumcenter which is the voronoi site
                        neighborCircumcenter = predicates.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3,
                            ref xi_tmp, ref eta_tmp);

                        /// compute petal and Voronoi edge intersection ///
                        // in order to avoid degenerate cases, we need to do a vector based calculation for line		
                        vector_x = (middleAngleCorner.y - smallestAngleCorner.y);//(-y, x)
                        vector_y = smallestAngleCorner.x - middleAngleCorner.x;
                        vector_x = myCircumcenter.x + vector_x;
                        vector_y = myCircumcenter.y + vector_y;


                        // by intersecting bisectors you will end up with the one you want to walk on
                        // then this line and circle should be intersected
                        CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y,
                                xPetalCtr, yPetalCtr, petalRadius, ref p);
                        /// choose the correct intersection point ///
                        // calculate middle point of the longest edge(bisector)
                        xMidOfLongestEdge = (middleAngleCorner.x + smallestAngleCorner.x) / 2.0;
                        yMidOfLongestEdge = (middleAngleCorner.y + smallestAngleCorner.y) / 2.0;
                        // we need to find correct intersection point, since line intersects circle twice
                        isCorrect = ChooseCorrectPoint(xMidOfLongestEdge, yMidOfLongestEdge, p[3], p[4],
                                    myCircumcenter.x, myCircumcenter.y, isObtuse);
                        // make sure which point is the correct one to be considered
                        if (isCorrect)
                        {
                            inter_x = p[3];
                            inter_y = p[4];
                        }
                        else
                        {
                            inter_x = p[1];
                            inter_y = p[2];
                        }
                        /// check if there is a Voronoi vertex between before intersection ///
                        // check if the voronoi vertex is between the intersection and circumcenter
                        PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y,
                                neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter);

                        /// determine the point to be suggested ///
                        if (p[0] > 0.0)
                        { // there is at least one intersection point
                            // if it is between circumcenter and intersection	
                            // if it returns 1.0 this means we have a voronoi vertex within feasible region
                            if (Math.Abs(voronoiOrInter[0] - 1.0) <= EPS)
                            {
                                if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y))
                                {
                                    // go back to circumcenter
                                    dxFirstSuggestion = dx;
                                    dyFirstSuggestion = dy;

                                }
                                else
                                { // we are not creating a bad triangle
                                    // neighbor's circumcenter is suggested
                                    dxFirstSuggestion = voronoiOrInter[2] - torg.x;
                                    dyFirstSuggestion = voronoiOrInter[3] - torg.y;
                                }

                            }
                            else
                            { // there is no voronoi vertex between intersection point and circumcenter
                                if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, inter_x, inter_y))
                                {
                                    // if it is inside feasible region, then insert v2				
                                    // apply perturbation
                                    // find the distance between circumcenter and intersection point
                                    d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) +
                                        (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y));
                                    // then find the vector going from intersection point to circumcenter
                                    ax = myCircumcenter.x - inter_x;
                                    ay = myCircumcenter.y - inter_y;

                                    ax = ax / d;
                                    ay = ay / d;
                                    // now calculate the new intersection point which is perturbated towards the circumcenter
                                    inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist);
                                    inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist);
                                    if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y))
                                    {
                                        // go back to circumcenter
                                        dxFirstSuggestion = dx;
                                        dyFirstSuggestion = dy;

                                    }
                                    else
                                    {
                                        // intersection point is suggested
                                        dxFirstSuggestion = inter_x - torg.x;
                                        dyFirstSuggestion = inter_y - torg.y;

                                    }
                                }
                                else
                                {
                                    // intersection point is suggested
                                    dxFirstSuggestion = inter_x - torg.x;
                                    dyFirstSuggestion = inter_y - torg.y;
                                }
                            }
                            /// if it is an acute triangle, check if it is a good enough location ///
                            // for acute triangle case, we need to check if it is ok to use either of them
                            if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) +
                                (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) >
                                lengthConst * ((smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) *
                                        (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) +
                                        (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) *
                                        (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))))
                            {
                                // use circumcenter
                                dxFirstSuggestion = dx;
                                dyFirstSuggestion = dy;
                            }// else we stick to what we have found	
                        }// intersection point

                    }// if it is on the boundary, meaning no neighbor triangle in this direction, try other direction	

                    /// DO THE SAME THING FOR THE OTHER DIRECTION ///
                    /// find the third point of the neighbor triangle  ///
                    neighborNotFound = GetNeighborsVertex(badotri, largestAngleCorner.x, largestAngleCorner.y,
                                smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri);
                    /// find the circumcenter of the neighbor triangle ///
                    dxSecondSuggestion = dx;	// if we cannot find any appropriate suggestion, we use circumcenter
                    dySecondSuggestion = dy;
                    // if there is a neighbor triangle
                    if (!neighborNotFound)
                    {
                        neighborvertex_1 = neighborotri.Org();
                        neighborvertex_2 = neighborotri.Dest();
                        neighborvertex_3 = neighborotri.Apex();
                        // now calculate neighbor's circumcenter which is the voronoi site
                        neighborCircumcenter = predicates.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3,
                            ref xi_tmp, ref eta_tmp);

                        /// compute petal and Voronoi edge intersection ///
                        // in order to avoid degenerate cases, we need to do a vector based calculation for line		
                        vector_x = (largestAngleCorner.y - smallestAngleCorner.y);//(-y, x)
                        vector_y = smallestAngleCorner.x - largestAngleCorner.x;
                        vector_x = myCircumcenter.x + vector_x;
                        vector_y = myCircumcenter.y + vector_y;


                        // by intersecting bisectors you will end up with the one you want to walk on
                        // then this line and circle should be intersected
                        CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y,
                                xPetalCtr, yPetalCtr, petalRadius, ref p);

                        /// choose the correct intersection point ///
                        // calcuwedgeslate middle point of the longest edge(bisector)
                        xMidOfMiddleEdge = (largestAngleCorner.x + smallestAngleCorner.x) / 2.0;
                        yMidOfMiddleEdge = (largestAngleCorner.y + smallestAngleCorner.y) / 2.0;
                        // we need to find correct intersection point, since line intersects circle twice
                        // this direction is always ACUTE
                        isCorrect = ChooseCorrectPoint(xMidOfMiddleEdge, yMidOfMiddleEdge, p[3], p[4],
                                    myCircumcenter.x, myCircumcenter.y, false/*(isObtuse+1)%2*/);
                        // make sure which point is the correct one to be considered
                        if (isCorrect)
                        {
                            inter_x = p[3];
                            inter_y = p[4];
                        }
                        else
                        {
                            inter_x = p[1];
                            inter_y = p[2];
                        }

                        /// check if there is a Voronoi vertex between before intersection ///
                        // check if the voronoi vertex is between the intersection and circumcenter
                        PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y,
                                neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter);

                        /// determine the point to be suggested ///
                        if (p[0] > 0.0)
                        { // there is at least one intersection point
                            // if it is between circumcenter and intersection	
                            // if it returns 1.0 this means we have a voronoi vertex within feasible region
                            if (Math.Abs(voronoiOrInter[0] - 1.0) <= EPS)
                            {
                                if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y))
                                {
                                    // go back to circumcenter
                                    dxSecondSuggestion = dx;
                                    dySecondSuggestion = dy;

                                }
                                else
                                { // we are not creating a bad triangle
                                    // neighbor's circumcenter is suggested
                                    dxSecondSuggestion = voronoiOrInter[2] - torg.x;
                                    dySecondSuggestion = voronoiOrInter[3] - torg.y;

                                }

                            }
                            else
                            { // there is no voronoi vertex between intersection point and circumcenter
                                if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y))
                                {
                                    // if it is inside feasible region, then insert v2				
                                    // apply perturbation
                                    // find the distance between circumcenter and intersection point
                                    d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) +
                                        (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y));
                                    // then find the vector going from intersection point to circumcenter
                                    ax = myCircumcenter.x - inter_x;
                                    ay = myCircumcenter.y - inter_y;

                                    ax = ax / d;
                                    ay = ay / d;
                                    // now calculate the new intersection point which is perturbated towards the circumcenter
                                    inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist);
                                    inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist);
                                    if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y))
                                    {
                                        // go back to circumcenter
                                        dxSecondSuggestion = dx;
                                        dySecondSuggestion = dy;

                                    }
                                    else
                                    {
                                        // intersection point is suggested
                                        dxSecondSuggestion = inter_x - torg.x;
                                        dySecondSuggestion = inter_y - torg.y;
                                    }
                                }
                                else
                                {

                                    // intersection point is suggested
                                    dxSecondSuggestion = inter_x - torg.x;
                                    dySecondSuggestion = inter_y - torg.y;
                                }
                            }
                            /// if it is an acute triangle, check if it is a good enough location ///
                            // for acute triangle case, we need to check if it is ok to use either of them
                            if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) +
                                (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) >
                                lengthConst * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) *
                                        (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) +
                                        (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) *
                                        (smallestAngleCorner.y - (dySecondSuggestion + torg.y))))
                            {
                                // use circumcenter
                                dxSecondSuggestion = dx;
                                dySecondSuggestion = dy;
                            }// else we stick on what we have found	
                        }
                    }// if it is on the boundary, meaning no neighbor triangle in this direction, the other direction might be ok		
                    if (isObtuse)
                    {
                        //obtuse: do nothing					
                        dx = dxFirstSuggestion;
                        dy = dyFirstSuggestion;
                    }
                    else
                    { // acute : consider other direction				
                        if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) *
                                (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) +
                                (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) *
                                (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) >
                                (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) *
                                (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) +
                                (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) *
                                (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)))
                        {
                            dx = dxSecondSuggestion;
                            dy = dySecondSuggestion;
                        }
                        else
                        {
                            dx = dxFirstSuggestion;
                            dy = dyFirstSuggestion;
                        }

                    }// end if obtuse
                }// end of relocation				 
            }// end of almostGood	

            Point circumcenter = new Point();

            if (relocated <= 0)
            {
                circumcenter.x = torg.x + dx;
                circumcenter.y = torg.y + dy;
            }
            else
            {
                circumcenter.x = origin_x + dx;
                circumcenter.y = origin_y + dy;
            }

            xi = (yao * dx - xao * dy) * (2.0 * denominator);
            eta = (xdo * dy - ydo * dx) * (2.0 * denominator);

            return circumcenter;
        }
Example #36
0
        /// <summary>
        /// Create a new subsegment and inserts it between two triangles. Its 
        /// vertices are properly initialized.
        /// </summary>
        /// <param name="tri">The new subsegment is inserted at the edge 
        /// described by this handle.</param>
        /// <param name="subsegmark">The marker 'subsegmark' is applied to the 
        /// subsegment and, if appropriate, its vertices.</param>
        internal void InsertSubseg(ref Otri tri, int subsegmark)
        {
            Otri oppotri = default(Otri);
            Osub newsubseg = default(Osub);
            Vertex triorg, tridest;

            triorg = tri.Org();
            tridest = tri.Dest();
            // Mark vertices if possible.
            if (triorg.label == 0)
            {
                triorg.label = subsegmark;
            }
            if (tridest.label == 0)
            {
                tridest.label = subsegmark;
            }
            // Check if there's already a subsegment here.
            tri.Pivot(ref newsubseg);
            if (newsubseg.seg.hash == DUMMY)
            {
                // Make new subsegment and initialize its vertices.
                MakeSegment(ref newsubseg);
                newsubseg.SetOrg(tridest);
                newsubseg.SetDest(triorg);
                newsubseg.SetSegOrg(tridest);
                newsubseg.SetSegDest(triorg);
                // Bond new subsegment to the two triangles it is sandwiched between.
                // Note that the facing triangle 'oppotri' might be equal to 'dummytri'
                // (outer space), but the new subsegment is bonded to it all the same.
                tri.SegBond(ref newsubseg);
                tri.Sym(ref oppotri);
                newsubseg.Sym();
                oppotri.SegBond(ref newsubseg);
                newsubseg.seg.boundary = subsegmark;
            }
            else if (newsubseg.seg.boundary == 0)
            {
                newsubseg.seg.boundary = subsegmark;
            }
        }
        /// <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;
            double dxod, dyod, dxda, dyda, dxao, dyao;
            double dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
            double apexlen, orglen, destlen, minedge;
            double angle;
            double area;
            double dist1, dist2;

            double 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 != null))
            {
                // Check whether the area is larger than permitted.
                area = 0.5 * (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.tri.area) && (testtri.tri.area > 0.0))
                {
                    // Add this triangle to the list of bad triangles.
                    queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
                    return;
                }

                // Check whether the user thinks this triangle is too large.
                if ((behavior.UserTest != null) && behavior.UserTest(testtri.tri, area))
                {
                    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 * Math.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 * Math.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 * Math.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.Pivot(ref testsub);
                    if (testsub.seg.hash == Mesh.DUMMY)
                    {
                        // No common segment.  Find a subsegment that contains 'torg'.
                        tri1.Copy(ref tri2);
                        do
                        {
                            tri1.Oprev();
                            tri1.Pivot(ref testsub);
                        } while (testsub.seg.hash == Mesh.DUMMY);
                        // Find the endpoints of the containing segment.
                        org1 = testsub.SegOrg();
                        dest1 = testsub.SegDest();
                        // Find a subsegment that contains 'tdest'.
                        do
                        {
                            tri2.Dnext();
                            tri2.Pivot(ref testsub);
                        } while (testsub.seg.hash == Mesh.DUMMY);
                        // 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);
            }
        }
Example #38
0
        /// <summary>
        /// Transform two triangles to two different triangles by flipping an edge 
        /// counterclockwise within a quadrilateral.
        /// </summary>
        /// <param name="flipedge">Handle to the edge that will be flipped.</param>
        /// <remarks>Imagine the original triangles, abc and bad, oriented so that the
        /// shared edge ab lies in a horizontal plane, with the vertex b on the left
        /// and the vertex a on the right. The vertex c lies below the edge, and
        /// the vertex d lies above the edge. The 'flipedge' handle holds the edge
        /// ab of triangle abc, and is directed left, from vertex a to vertex b.
        ///
        /// The triangles abc and bad are deleted and replaced by the triangles cdb
        /// and dca.  The triangles that represent abc and bad are NOT deallocated;
        /// they are reused for dca and cdb, respectively.  Hence, any handles that
        /// may have held the original triangles are still valid, although not
        /// directed as they were before.
        ///
        /// Upon completion of this routine, the 'flipedge' handle holds the edge
        /// dc of triangle dca, and is directed down, from vertex d to vertex c.
        /// (Hence, the two triangles have rotated counterclockwise.)
        ///
        /// WARNING:  This transformation is geometrically valid only if the
        /// quadrilateral adbc is convex.  Furthermore, this transformation is
        /// valid only if there is not a subsegment between the triangles abc and
        /// bad.  This routine does not check either of these preconditions, and
        /// it is the responsibility of the calling routine to ensure that they are
        /// met.  If they are not, the streets shall be filled with wailing and
        /// gnashing of teeth.
        /// 
        /// Terminology
        ///
        /// A "local transformation" replaces a small set of triangles with another
        /// set of triangles.  This may or may not involve inserting or deleting a
        /// vertex.
        ///
        /// The term "casing" is used to describe the set of triangles that are
        /// attached to the triangles being transformed, but are not transformed
        /// themselves.  Think of the casing as a fixed hollow structure inside
        /// which all the action happens.  A "casing" is only defined relative to
        /// a single transformation; each occurrence of a transformation will
        /// involve a different casing.
        /// </remarks>
        internal void Flip(ref Otri flipedge)
        {
            Otri botleft = default(Otri), botright = default(Otri);
            Otri topleft = default(Otri), topright = default(Otri);
            Otri top = default(Otri);
            Otri botlcasing = default(Otri), botrcasing = default(Otri);
            Otri toplcasing = default(Otri), toprcasing = default(Otri);
            Osub botlsubseg = default(Osub), botrsubseg = default(Osub);
            Osub toplsubseg = default(Osub), toprsubseg = default(Osub);
            Vertex leftvertex, rightvertex, botvertex;
            Vertex farvertex;

            // Identify the vertices of the quadrilateral.
            rightvertex = flipedge.Org();
            leftvertex = flipedge.Dest();
            botvertex = flipedge.Apex();
            flipedge.Sym(ref top);

            // SELF CHECK

            //if (top.triangle.id == DUMMY)
            //{
            //    logger.Error("Attempt to flip on boundary.", "Mesh.Flip()");
            //    flipedge.LnextSelf();
            //    return;
            //}

            //if (checksegments)
            //{
            //    flipedge.SegPivot(ref toplsubseg);
            //    if (toplsubseg.ss != Segment.Empty)
            //    {
            //        logger.Error("Attempt to flip a segment.", "Mesh.Flip()");
            //        flipedge.LnextSelf();
            //        return;
            //    }
            //}

            farvertex = top.Apex();

            // Identify the casing of the quadrilateral.
            top.Lprev(ref topleft);
            topleft.Sym(ref toplcasing);
            top.Lnext(ref topright);
            topright.Sym(ref toprcasing);
            flipedge.Lnext(ref botleft);
            botleft.Sym(ref botlcasing);
            flipedge.Lprev(ref botright);
            botright.Sym(ref botrcasing);
            // Rotate the quadrilateral one-quarter turn counterclockwise.
            topleft.Bond(ref botlcasing);
            botleft.Bond(ref botrcasing);
            botright.Bond(ref toprcasing);
            topright.Bond(ref toplcasing);

            if (checksegments)
            {
                // Check for subsegments and rebond them to the quadrilateral.
                topleft.Pivot(ref toplsubseg);
                botleft.Pivot(ref botlsubseg);
                botright.Pivot(ref botrsubseg);
                topright.Pivot(ref toprsubseg);

                if (toplsubseg.seg.hash == DUMMY)
                {
                    topright.SegDissolve(dummysub);
                }
                else
                {
                    topright.SegBond(ref toplsubseg);
                }

                if (botlsubseg.seg.hash == DUMMY)
                {
                    topleft.SegDissolve(dummysub);
                }
                else
                {
                    topleft.SegBond(ref botlsubseg);
                }

                if (botrsubseg.seg.hash == DUMMY)
                {
                    botleft.SegDissolve(dummysub);
                }
                else
                {
                    botleft.SegBond(ref botrsubseg);
                }

                if (toprsubseg.seg.hash == DUMMY)
                {
                    botright.SegDissolve(dummysub);
                }
                else
                {
                    botright.SegBond(ref toprsubseg);
                }
            }

            // New vertex assignments for the rotated quadrilateral.
            flipedge.SetOrg(farvertex);
            flipedge.SetDest(botvertex);
            flipedge.SetApex(rightvertex);
            top.SetOrg(botvertex);
            top.SetDest(farvertex);
            top.SetApex(leftvertex);
        }