Exemplo n.º 1
0
    /// <summary>
    /// Both edges must be directed from right to left (this is the canonical
    /// direction for the upper edge of each region).
    ///
    /// The strategy is to evaluate a "t" value for each edge at the
    /// current sweep line position, given by tess->event. The calculations
    /// are designed to be very stable, but of course they are not perfect.
    ///
    /// Special case: if both edge destinations are at the sweep event,
    /// we sort the edges by slope (they would otherwise compare equally).
    /// </summary>
    private bool EdgeLeq(ActiveRegion reg1, ActiveRegion reg2)
    {
        MeshUtils.Edge e1 = reg1._eUp;
        MeshUtils.Edge e2 = reg2._eUp;

        if (e1._Dst == _event)
        {
            if (e2._Dst != _event)
            {
                return(Geom.EdgeSign(e2._Dst, _event, e2._Org) <= 0.0f);
            }

            // Two edges right of the sweep line which meet at the sweep event.
            // Sort them by slope.
            if (Geom.VertLeq(e1._Org, e2._Org))
            {
                return(Geom.EdgeSign(e2._Dst, e1._Org, e2._Org) <= 0.0f);
            }
            return(Geom.EdgeSign(e1._Dst, e2._Org, e1._Org) >= 0.0f);
        }
        if (e2._Dst == _event)
        {
            return(Geom.EdgeSign(e1._Dst, _event, e1._Org) >= 0.0f);
        }

        // General case - compute signed distance *from* e1, e2 to event
        double t1 = Geom.EdgeEval(e1._Dst, _event, e1._Org);
        double t2 = Geom.EdgeEval(e2._Dst, _event, e2._Org);

        return(t1 >= t2);
    }
Exemplo n.º 2
0
    /// <summary>
    /// TessellateMonoRegion( face ) tessellates a monotone region
    /// (what else would it do??)  The region must consist of a single
    /// loop of half-edges (see mesh.h) oriented CCW.  "Monotone" in this
    /// case means that any vertical line intersects the interior of the
    /// region in a single interval.
    ///
    /// Tessellation consists of adding interior edges (actually pairs of
    /// half-edges), to split the region into non-overlapping triangles.
    ///
    /// The basic idea is explained in Preparata and Shamos (which I don't
    /// have handy right now), although their implementation is more
    /// complicated than this one.  The are two edge chains, an upper chain
    /// and a lower chain.  We process all vertices from both chains in order,
    /// from right to left.
    ///
    /// The algorithm ensures that the following invariant holds after each
    /// vertex is processed: the untessellated region consists of two
    /// chains, where one chain (say the upper) is a single edge, and
    /// the other chain is concave.  The left vertex of the single edge
    /// is always to the left of all vertices in the concave chain.
    ///
    /// Each step consists of adding the rightmost unprocessed vertex to one
    /// of the two chains, and forming a fan of triangles from the rightmost
    /// of two chain endpoints.  Determining whether we can add each triangle
    /// to the fan is a simple orientation test.  By making the fan as large
    /// as possible, we restore the invariant (check it yourself).
    /// </summary>
    private void TessellateMonoRegion(MeshUtils.Face face)
    {
        // All edges are oriented CCW around the boundary of the region.
        // First, find the half-edge whose origin vertex is rightmost.
        // Since the sweep goes from left to right, face->anEdge should
        // be close to the edge we want.
        MeshUtils.Edge up = face._anEdge;
        Debug.Assert(up._Lnext != up && up._Lnext._Lnext != up);

        while (Geom.VertLeq(up._Dst, up._Org))
        {
            up = up._Lprev;
        }

        while (Geom.VertLeq(up._Org, up._Dst))
        {
            up = up._Lnext;
        }

        MeshUtils.Edge lo = up._Lprev;

        while (up._Lnext != lo)
        {
            if (Geom.VertLeq(up._Dst, lo._Org))
            {
                // up.Dst is on the left. It is safe to form triangles from lo.Org.
                // The EdgeGoesLeft test guarantees progress even when some triangles
                // are CW, given that the upper and lower chains are truly monotone.
                while (lo._Lnext != up && (Geom.EdgeGoesLeft(lo._Lnext) ||
                                           Geom.EdgeSign(lo._Org, lo._Dst, lo._Lnext._Dst) <= 0.0f))
                {
                    lo = Mesh.Connect(_pool, lo._Lnext, lo)._Sym;
                }
                lo = lo._Lprev;
            }
            else
            {
                // lo.Org is on the left.  We can make CCW triangles from up.Dst.
                while (lo._Lnext != up && (Geom.EdgeGoesRight(up._Lprev) ||
                                           Geom.EdgeSign(up._Dst, up._Org, up._Lprev._Org) >= 0.0f))
                {
                    up = Mesh.Connect(_pool, up, up._Lprev)._Sym;
                }
                up = up._Lnext;
            }
        }

        // Now lo.Org == up.Dst == the leftmost vertex.  The remaining region
        // can be tessellated in a fan from this leftmost vertex.
        Debug.Assert(lo._Lnext != up);
        while (lo._Lnext._Lnext != up)
        {
            lo = Mesh.Connect(_pool, lo._Lnext, lo)._Sym;
        }
    }
Exemplo n.º 3
0
    /// <summary>
    /// Check the upper and lower edge of "regUp", to make sure that the
    /// eUp->Org is above eLo, or eLo->Org is below eUp (depending on which
    /// origin is leftmost).
    ///
    /// The main purpose is to splice right-going edges with the same
    /// dest vertex and nearly identical slopes (ie. we can't distinguish
    /// the slopes numerically).  However the splicing can also help us
    /// to recover from numerical errors.  For example, suppose at one
    /// point we checked eUp and eLo, and decided that eUp->Org is barely
    /// above eLo.  Then later, we split eLo into two edges (eg. from
    /// a splice operation like this one).  This can change the result of
    /// our test so that now eUp->Org is incident to eLo, or barely below it.
    /// We must correct this condition to maintain the dictionary invariants.
    ///
    /// One possibility is to check these edges for intersection again
    /// (ie. CheckForIntersect).  This is what we do if possible.  However
    /// CheckForIntersect requires that tess->event lies between eUp and eLo,
    /// so that it has something to fall back on when the intersection
    /// calculation gives us an unusable answer.  So, for those cases where
    /// we can't check for intersection, this routine fixes the problem
    /// by just splicing the offending vertex into the other edge.
    /// This is a guaranteed solution, no matter how degenerate things get.
    /// Basically this is a combinatorial solution to a numerical problem.
    /// </summary>
    private bool CheckForRightSplice(ActiveRegion regUp)
    {
        ActiveRegion regLo = RegionBelow(regUp);

        MeshUtils.Edge eUp = regUp._eUp;
        MeshUtils.Edge eLo = regLo._eUp;

        if (Geom.VertLeq(eUp._Org, eLo._Org))
        {
            if (Geom.EdgeSign(eLo._Dst, eUp._Org, eLo._Org) > 0.0f)
            {
                return(false);
            }

            // eUp.Org appears to be below eLo
            if (!Geom.VertEq(eUp._Org, eLo._Org))
            {
                // Splice eUp._Org into eLo
                _mesh.SplitEdge(_pool, eLo._Sym);
                Mesh.Splice(_pool, eUp, eLo._Oprev);
                regUp._dirty = regLo._dirty = true;
            }
            else if (eUp._Org != eLo._Org)
            {
                // merge the two vertices, discarding eUp.Org
                _pq.Remove(eUp._Org._pqHandle);
                SpliceMergeVertices(eLo._Oprev, eUp);
            }
        }
        else
        {
            if (Geom.EdgeSign(eUp._Dst, eLo._Org, eUp._Org) < 0.0f)
            {
                return(false);
            }

            // eLo.Org appears to be above eUp, so splice eLo.Org into eUp
            RegionAbove(regUp)._dirty = regUp._dirty = true;
            _mesh.SplitEdge(_pool, eUp._Sym);
            Mesh.Splice(_pool, eLo._Oprev, eUp);
        }
        return(true);
    }
Exemplo n.º 4
0
    /// <summary>
    /// Check the upper and lower edge of "regUp", to make sure that the
    /// eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which
    /// destination is rightmost).
    ///
    /// Theoretically, this should always be true.  However, splitting an edge
    /// into two pieces can change the results of previous tests.  For example,
    /// suppose at one point we checked eUp and eLo, and decided that eUp->Dst
    /// is barely above eLo.  Then later, we split eLo into two edges (eg. from
    /// a splice operation like this one).  This can change the result of
    /// the test so that now eUp->Dst is incident to eLo, or barely below it.
    /// We must correct this condition to maintain the dictionary invariants
    /// (otherwise new edges might get inserted in the wrong place in the
    /// dictionary, and bad stuff will happen).
    ///
    /// We fix the problem by just splicing the offending vertex into the
    /// other edge.
    /// </summary>
    private bool CheckForLeftSplice(ActiveRegion regUp)
    {
        ActiveRegion regLo = RegionBelow(regUp);

        MeshUtils.Edge eUp = regUp._eUp;
        MeshUtils.Edge eLo = regLo._eUp;

        Debug.Assert(!Geom.VertEq(eUp._Dst, eLo._Dst));

        if (Geom.VertLeq(eUp._Dst, eLo._Dst))
        {
            if (Geom.EdgeSign(eUp._Dst, eLo._Dst, eUp._Org) < 0.0f)
            {
                return(false);
            }

            // eLo.Dst is above eUp, so splice eLo.Dst into eUp
            RegionAbove(regUp)._dirty = regUp._dirty = true;
            MeshUtils.Edge e = _mesh.SplitEdge(_pool, eUp);
            Mesh.Splice(_pool, eLo._Sym, e);
            e._Lface._inside = regUp._inside;
        }
        else
        {
            if (Geom.EdgeSign(eLo._Dst, eUp._Dst, eLo._Org) > 0.0f)
            {
                return(false);
            }

            // eUp.Dst is below eLo, so splice eUp.Dst into eLo
            regUp._dirty = regLo._dirty = true;
            MeshUtils.Edge e = _mesh.SplitEdge(_pool, eLo);
            Mesh.Splice(_pool, eUp._Lnext, eLo._Sym);
            e._Rface._inside = regUp._inside;
        }
        return(true);
    }
Exemplo n.º 5
0
    /// <summary>
    /// Check the upper and lower edges of the given region to see if
    /// they intersect.  If so, create the intersection and add it
    /// to the data structures.
    ///
    /// Returns TRUE if adding the new intersection resulted in a recursive
    /// call to AddRightEdges(); in this case all "dirty" regions have been
    /// checked for intersections, and possibly regUp has been deleted.
    /// </summary>
    private bool CheckForIntersect(ActiveRegion regUp)
    {
        ActiveRegion regLo = RegionBelow(regUp);

        MeshUtils.Edge   eUp   = regUp._eUp;
        MeshUtils.Edge   eLo   = regLo._eUp;
        MeshUtils.Vertex orgUp = eUp._Org;
        MeshUtils.Vertex orgLo = eLo._Org;
        MeshUtils.Vertex dstUp = eUp._Dst;
        MeshUtils.Vertex dstLo = eLo._Dst;

        Debug.Assert(!Geom.VertEq(dstLo, dstUp));
        Debug.Assert(Geom.EdgeSign(dstUp, _event, orgUp) <= 0.0f);
        Debug.Assert(Geom.EdgeSign(dstLo, _event, orgLo) >= 0.0f);
        Debug.Assert(orgUp != _event && orgLo != _event);
        Debug.Assert(!regUp._fixUpperEdge && !regLo._fixUpperEdge);

        if (orgUp == orgLo)
        {
            // right endpoints are the same
            return(false);
        }

        double tMinUp = Math.Min(orgUp._t, dstUp._t);
        double tMaxLo = Math.Max(orgLo._t, dstLo._t);

        if (tMinUp > tMaxLo)
        {
            // t ranges do not overlap
            return(false);
        }

        if (Geom.VertLeq(orgUp, orgLo))
        {
            if (Geom.EdgeSign(dstLo, orgUp, orgLo) > 0.0f)
            {
                return(false);
            }
        }
        else
        {
            if (Geom.EdgeSign(dstUp, orgLo, orgUp) < 0.0f)
            {
                return(false);
            }
        }

        // At this point the edges intersect, at least marginally

        MeshUtils.Vertex isect = _pool.Get <MeshUtils.Vertex>();
        Geom.EdgeIntersect(dstUp, orgUp, dstLo, orgLo, isect);
        // The following properties are guaranteed:
        Debug.Assert(Math.Min(orgUp._t, dstUp._t) <= isect._t);
        Debug.Assert(isect._t <= Math.Max(orgLo._t, dstLo._t));
        Debug.Assert(Math.Min(dstLo._s, dstUp._s) <= isect._s);
        Debug.Assert(isect._s <= Math.Max(orgLo._s, orgUp._s));

        if (Geom.VertLeq(isect, _event))
        {
            // The intersection point lies slightly to the left of the sweep line,
            // so move it until it's slightly to the right of the sweep line.
            // (If we had perfect numerical precision, this would never happen
            // in the first place). The easiest and safest thing to do is
            // replace the intersection by tess._event.
            isect._s = _event._s;
            isect._t = _event._t;
        }
        // Similarly, if the computed intersection lies to the right of the
        // rightmost origin (which should rarely happen), it can cause
        // unbelievable inefficiency on sufficiently degenerate inputs.
        // (If you have the test program, try running test54.d with the
        // "X zoom" option turned on).
        MeshUtils.Vertex orgMin = Geom.VertLeq(orgUp, orgLo) ? orgUp : orgLo;
        if (Geom.VertLeq(orgMin, isect))
        {
            isect._s = orgMin._s;
            isect._t = orgMin._t;
        }

        if (Geom.VertEq(isect, orgUp) || Geom.VertEq(isect, orgLo))
        {
            // Easy case -- intersection at one of the right endpoints
            CheckForRightSplice(regUp);
            _pool.Return(isect);
            return(false);
        }

        if (!Geom.VertEq(dstUp, _event) &&
            Geom.EdgeSign(dstUp, _event, isect) >= 0.0f ||
            !Geom.VertEq(dstLo, _event) &&
            Geom.EdgeSign(dstLo, _event, isect) <= 0.0f)
        {
            // Very unusual -- the new upper or lower edge would pass on the
            // wrong side of the sweep event, or through it. This can happen
            // due to very small numerical errors in the intersection calculation.
            if (dstLo == _event)
            {
                // Splice dstLo into eUp, and process the new region(s)
                _mesh.SplitEdge(_pool, eUp._Sym);
                Mesh.Splice(_pool, eLo._Sym, eUp);
                regUp = TopLeftRegion(regUp);
                eUp   = RegionBelow(regUp)._eUp;
                FinishLeftRegions(RegionBelow(regUp), regLo);
                AddRightEdges(regUp, eUp._Oprev, eUp, eUp, true);
                _pool.Return(isect);
                return(true);
            }
            if (dstUp == _event)
            {
                /* Splice dstUp into eLo, and process the new region(s) */
                _mesh.SplitEdge(_pool, eLo._Sym);
                Mesh.Splice(_pool, eUp._Lnext, eLo._Oprev);
                regLo = regUp;
                regUp = TopRightRegion(regUp);
                MeshUtils.Edge e = RegionBelow(regUp)._eUp._Rprev;
                regLo._eUp = eLo._Oprev;
                eLo        = FinishLeftRegions(regLo, null);
                AddRightEdges(regUp, eLo._Onext, eUp._Rprev, e, true);
                _pool.Return(isect);
                return(true);
            }
            // Special case: called from ConnectRightVertex. If either
            // edge passes on the wrong side of tess._event, split it
            // (and wait for ConnectRightVertex to splice it appropriately).
            if (Geom.EdgeSign(dstUp, _event, isect) >= 0.0f)
            {
                RegionAbove(regUp)._dirty = regUp._dirty = true;
                _mesh.SplitEdge(_pool, eUp._Sym);
                eUp._Org._s = _event._s;
                eUp._Org._t = _event._t;
            }
            if (Geom.EdgeSign(dstLo, _event, isect) <= 0.0f)
            {
                regUp._dirty = regLo._dirty = true;
                _mesh.SplitEdge(_pool, eLo._Sym);
                eLo._Org._s = _event._s;
                eLo._Org._t = _event._t;
            }
            // leave the rest for ConnectRightVertex
            _pool.Return(isect);
            return(false);
        }

        // General case -- split both edges, splice into new vertex.
        // When we do the splice operation, the order of the arguments is
        // arbitrary as far as correctness goes. However, when the operation
        // creates a new face, the work done is proportional to the size of
        // the new face.  We expect the faces in the processed part of
        // the mesh (ie. eUp._Lface) to be smaller than the faces in the
        // unprocessed original contours (which will be eLo._Oprev._Lface).
        _mesh.SplitEdge(_pool, eUp._Sym);
        _mesh.SplitEdge(_pool, eLo._Sym);
        Mesh.Splice(_pool, eLo._Oprev, eUp);
        eUp._Org._s = isect._s;
        eUp._Org._t = isect._t;
        _pool.Return(isect);
        isect = null;
        eUp._Org._pqHandle = _pq.Insert(eUp._Org);
        if (eUp._Org._pqHandle._handle == PQHandle.Invalid)
        {
            throw new InvalidOperationException("PQHandle should not be invalid");
        }
        GetIntersectData(eUp._Org, orgUp, dstUp, orgLo, dstLo);
        RegionAbove(regUp)._dirty = regUp._dirty = regLo._dirty = true;
        return(false);
    }