private void ComputeNormal(ref Vec3 norm)
        {
            var v = _mesh._vHead._next;

            var minVal = new Real[3] { v._coords.X, v._coords.Y, v._coords.Z };
            var minVert = new MeshUtils.Vertex[3] { v, v, v };
            var maxVal = new Real[3] { v._coords.X, v._coords.Y, v._coords.Z };
            var maxVert = new MeshUtils.Vertex[3] { v, v, v };

            for (; v != _mesh._vHead; v = v._next)
            {
                if (v._coords.X < minVal[0]) { minVal[0] = v._coords.X; minVert[0] = v; }
                if (v._coords.Y < minVal[1]) { minVal[1] = v._coords.Y; minVert[1] = v; }
                if (v._coords.Z < minVal[2]) { minVal[2] = v._coords.Z; minVert[2] = v; }
                if (v._coords.X > maxVal[0]) { maxVal[0] = v._coords.X; maxVert[0] = v; }
                if (v._coords.Y > maxVal[1]) { maxVal[1] = v._coords.Y; maxVert[1] = v; }
                if (v._coords.Z > maxVal[2]) { maxVal[2] = v._coords.Z; maxVert[2] = v; }
            }

            // Find two vertices separated by at least 1/sqrt(3) of the maximum
            // distance between any two vertices
            int i = 0;
            if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0]) { i = 1; }
            if (maxVal[2] - minVal[2] > maxVal[i] - minVal[i]) { i = 2; }
            if (minVal[i] >= maxVal[i])
            {
                // All vertices are the same -- normal doesn't matter
                norm = new Vec3 { X = 0, Y = 0, Z = 1 };
                return;
            }

            // Look for a third vertex which forms the triangle with maximum area
            // (Length of normal == twice the triangle area)
            Real maxLen2 = 0, tLen2;
            var v1 = minVert[i];
            var v2 = maxVert[i];
            Vec3 d1, d2, tNorm;
            Vec3.Sub(ref v1._coords, ref v2._coords, out d1);
            for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
            {
                Vec3.Sub(ref v._coords, ref v2._coords, out d2);
                tNorm.X = d1.Y * d2.Z - d1.Z * d2.Y;
                tNorm.Y = d1.Z * d2.X - d1.X * d2.Z;
                tNorm.Z = d1.X * d2.Y - d1.Y * d2.X;
                tLen2 = tNorm.X*tNorm.X + tNorm.Y*tNorm.Y + tNorm.Z*tNorm.Z;
                if (tLen2 > maxLen2)
                {
                    maxLen2 = tLen2;
                    norm = tNorm;
                }
            }

            if (maxLen2 <= 0.0f)
            {
                // All points lie on a single line -- any decent normal will do
                norm = Vec3.Zero;
                i = Vec3.LongAxis(ref d1);
                norm[i] = 1;
            }
        }
Exemple #2
0
        static void Swap(ref MeshUtils.Vertex a, ref MeshUtils.Vertex b)
        {
            var tmp = a;

            a = b;
            b = tmp;
        }
Exemple #3
0
    private ActiveRegion TopLeftRegion(ActiveRegion reg)
    {
        MeshUtils.Vertex org = reg._eUp._Org;

        // Find the region above the uppermost edge with the same origin
        do
        {
            reg = RegionAbove(reg);
        } while (reg._eUp._Org == org);

        switch (reg._fixUpperEdge)
        {
        // If the edge above was a temporary edge introduced by ConnectRightVertex,
        // now is the time to fix it.
        case true:
        {
            MeshUtils.Edge e = Mesh.Connect(_pool, RegionBelow(reg)._eUp._Sym, reg._eUp._Lnext);
            FixUpperEdge(reg, e);
            reg = RegionAbove(reg);
            break;
        }
        }

        return(reg);
    }
Exemple #4
0
    private void CheckOrientation()
    {
        // When we compute the normal automatically, we choose the orientation
        // so that the sum of the signed areas of all contours is non-negative.
        double area = 0.0f;

        for (MeshUtils.Face f = _mesh._fHead._next; f != _mesh._fHead; f = f._next)
        {
            switch (f._anEdge._winding)
            {
            case <= 0:
                continue;

            default:
                area += MeshUtils.FaceArea(f);
                break;
            }
        }
        switch (area)
        {
        case < 0.0f:
        {
            // Reverse the orientation by flipping all the t-coordinates
            for (MeshUtils.Vertex v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
            {
                v._t = -v._t;
            }
            Vec3.Neg(ref _tUnit);
            break;
        }
        }
    }
Exemple #5
0
        public void Check()
        {
            MeshUtils.Edge e;

            MeshUtils.Face fPrev = _fHead, f;
            for (fPrev = _fHead; (f = fPrev._next) != _fHead; fPrev = f)
            {
                e = f._anEdge;
                do
                {
                    Debug.Assert(e._Sym != e);
                    Debug.Assert(e._Sym._Sym == e);
                    Debug.Assert(e._Lnext._Onext._Sym == e);
                    Debug.Assert(e._Onext._Sym._Lnext == e);
                    Debug.Assert(e._Lface == f);
                    e = e._Lnext;
                } while (e != f._anEdge);
            }
            Debug.Assert(f._prev == fPrev && f._anEdge == null);

            MeshUtils.Vertex vPrev = _vHead, v;
            for (vPrev = _vHead; (v = vPrev._next) != _vHead; vPrev = v)
            {
                Debug.Assert(v._prev == vPrev);
                e = v._anEdge;
                do
                {
                    Debug.Assert(e._Sym != e);
                    Debug.Assert(e._Sym._Sym == e);
                    Debug.Assert(e._Lnext._Onext._Sym == e);
                    Debug.Assert(e._Onext._Sym._Lnext == e);
                    Debug.Assert(e._Org == v);
                    e = e._Onext;
                } while (e != v._anEdge);
            }
            Debug.Assert(v._prev == vPrev && v._anEdge == null);

            MeshUtils.Edge ePrev = _eHead;
            for (ePrev = _eHead; (e = ePrev._next) != _eHead; ePrev = e)
            {
                Debug.Assert(e._Sym._next == ePrev._Sym);
                Debug.Assert(e._Sym != e);
                Debug.Assert(e._Sym._Sym == e);
                Debug.Assert(e._Org != null);
                Debug.Assert(e._Dst != null);
                Debug.Assert(e._Lnext._Onext._Sym == e);
                Debug.Assert(e._Onext._Sym._Lnext == e);
            }
            Debug.Assert(e._Sym._next == ePrev._Sym &&
                         e._Sym == _eHeadSym &&
                         e._Sym._Sym == e &&
                         e._Org == null && e._Dst == null &&
                         e._Lface == null && e._Rface == null);
        }
Exemple #6
0
    /// <summary>
    /// Find some weights which describe how the intersection vertex is
    /// a linear combination of "org" and "dest".  Each of the two edges
    /// which generated "isect" is allocated 50% of the weight; each edge
    /// splits the weight between its org and dst according to the
    /// relative distance to "isect".
    /// </summary>
    private static void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUtils.Vertex dst, out double w0, out double w1)
    {
        double t1 = Geom.VertL1dist(org, isect);
        double t2 = Geom.VertL1dist(dst, isect);

        w0 = t2 / (t1 + t2) / 2.0f;
        w1 = t1 / (t1 + t2) / 2.0f;

        isect._coords.X += w0 * org._coords.X + w1 * dst._coords.X;
        isect._coords.Y += w0 * org._coords.Y + w1 * dst._coords.Y;
        isect._coords.Z += w0 * org._coords.Z + w1 * dst._coords.Z;
    }
Exemple #7
0
    private ActiveRegion TopRightRegion(ActiveRegion reg)
    {
        MeshUtils.Vertex dst = reg._eUp._Dst;

        // Find the region above the uppermost edge with the same destination
        do
        {
            reg = RegionAbove(reg);
        } while (reg._eUp._Dst == dst);

        return(reg);
    }
Exemple #8
0
        /// <summary>
        /// Returns a number whose sign matches EdgeEval(u,v,w) but which
        /// is cheaper to evaluate. Returns > 0, == 0 , or < 0
        /// as v is above, on, or below the edge uw.
        /// </summary>
        public static Real EdgeSign(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w)
        {
            Debug.Assert(VertLeq(u, v) && VertLeq(v, w));

            var gapL = v._s - u._s;
            var gapR = w._s - v._s;

            if (gapL + gapR > 0.0f)
            {
                return (v._t - w._t) * gapL + (v._t - u._t) * gapR;
            }
            /* vertical line */
            return 0;
        }
Exemple #9
0
        public override void Reset()
        {
            MeshUtils.Face f = _fHead._next;
            while (true)
            {
                MeshUtils.Face fNext = f._next;
                f.Free();
                if (f == _fHead)
                {
                    break;
                }
                f = fNext;
            }

            MeshUtils.Vertex v = _vHead._next;
            while (true)
            {
                MeshUtils.Vertex vNext = v._next;
                v.Free();
                if (v == _vHead)
                {
                    break;
                }
                v = vNext;
            }

            for (int i = 0; i < _allEdgePairs.Count; i++)
            {
                MeshUtils.EdgePair pair = _allEdgePairs[i];

                MeshUtils.Edge e = pair._e;
                if (!e.IsReturnedToPool()) // (can be Free in KillEdge)
                {
                    e.Free();
                }

                MeshUtils.Edge eSym = pair._eSym;
                if (!eSym.IsReturnedToPool()) // (can be Free in KillEdge)
                {
                    eSym.Free();
                }

                pair.Free();
            }
            _allEdgePairs.Clear();

            _vHead = null;
            _fHead = null;
            _eHead = _eHeadSym = null;
        }
Exemple #10
0
        public static Real TransSign(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w)
        {
            Debug.Assert(TransLeq(u, v) && TransLeq(v, w));

            var gapL = v._t - u._t;
            var gapR = w._t - v._t;

            if (gapL + gapR > 0.0f)
            {
                return (v._s - w._s) * gapL + (v._s - u._s) * gapR;
            }
            /* vertical line */
            return 0;
        }
Exemple #11
0
    /// <summary>
    /// We've computed a new intersection point, now we need a "data" pointer
    /// from the user so that we can refer to this new vertex in the
    /// rendering callbacks.
    /// </summary>
    private void GetIntersectData(MeshUtils.Vertex isect, MeshUtils.Vertex orgUp, MeshUtils.Vertex dstUp, MeshUtils.Vertex orgLo, MeshUtils.Vertex dstLo)
    {
        isect._coords = Vec3.Zero;
        VertexWeights(isect, orgUp, dstUp, out double w0, out double w1);
        VertexWeights(isect, orgLo, dstLo, out double w2, out double w3);

        if (_combineCallback != null)
        {
            isect._data = _combineCallback(
                isect._coords,
                new [] { orgUp._data, dstUp._data, orgLo._data, dstLo._data },
                new [] { w0, w1, w2, w3 }
                );
        }
    }
Exemple #12
0
 public override void OnFree()
 {
     for (MeshUtils.Face f = _fHead._next, fNext = _fHead; f != _fHead; f = fNext)
     {
         fNext = f._next;
         f.Free();
     }
     for (MeshUtils.Vertex v = _vHead._next, vNext = _vHead; v != _vHead; v = vNext)
     {
         vNext = v._next;
         v.Free();
     }
     for (MeshUtils.Edge e = _eHead._next, eNext = _eHead; e != _eHead; e = eNext)
     {
         eNext = e._next;
         e.Free();
     }
 }
Exemple #13
0
    public void MergeConvexFaces(IPool pool, int maxVertsPerFace)
    {
        for (MeshUtils.Face f = _fHead._next; f != _fHead; f = f._next)
        {
            switch (f._inside)
            {
            // Skip faces which are outside the result
            case false:
                continue;
            }

            MeshUtils.Edge   eCur   = f._anEdge;
            MeshUtils.Vertex vStart = eCur._Org;

            while (true)
            {
                MeshUtils.Edge eNext = eCur._Lnext;
                MeshUtils.Edge eSym  = eCur._Sym;

                if (eSym is { _Lface._inside: true })
Exemple #14
0
        /// <summary>
        /// Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
        /// evaluates the t-coord of the edge uw at the s-coord of the vertex v.
        /// Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
        /// If uw is vertical (and thus passes thru v), the result is zero.
        /// 
        /// The calculation is extremely accurate and stable, even when v
        /// is very close to u or w.  In particular if we set v->t = 0 and
        /// let r be the negated result (this evaluates (uw)(v->s)), then
        /// r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
        /// </summary>
        public static Real EdgeEval(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w)
        {
            Debug.Assert(VertLeq(u, v) && VertLeq(v, w));

            var gapL = v._s - u._s;
            var gapR = w._s - v._s;

            if (gapL + gapR > 0.0f)
            {
                if (gapL < gapR)
                {
                    return (v._t - u._t) + (u._t - w._t) * (gapL / (gapL + gapR));
                }
                else
                {
                    return (v._t - w._t) + (w._t - u._t) * (gapR / (gapL + gapR));
                }
            }
            /* vertical line */
            return 0;
        }
Exemple #15
0
        public static Real TransEval(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w)
        {
            Debug.Assert(TransLeq(u, v) && TransLeq(v, w));

            var gapL = v._t - u._t;
            var gapR = w._t - v._t;

            if (gapL + gapR > 0.0f)
            {
                if (gapL < gapR)
                {
                    return((v._s - u._s) + (u._s - w._s) * (gapL / (gapL + gapR)));
                }
                else
                {
                    return((v._s - w._s) + (w._s - u._s) * (gapR / (gapL + gapR)));
                }
            }
            /* vertical line */
            return(0);
        }
Exemple #16
0
        public void Reset(IPool pool)
        {
            for (MeshUtils.Face f = _fHead, fNext = _fHead; f._next != null; f = fNext)
            {
                fNext = f._next;
                pool.Return(f);
            }
            for (MeshUtils.Vertex v = _vHead, vNext = _vHead; v._next != null; v = vNext)
            {
                vNext = v._next;
                pool.Return(v);
            }
            for (MeshUtils.Edge e = _eHead, eNext = _eHead; e._next != null; e = eNext)
            {
                eNext = e._next;
                pool.Return(e._Sym);
                pool.Return(e);
            }

            _vHead = null;
            _fHead = null;
            _eHead = _eHeadSym = null;
        }
Exemple #17
0
        public void Init(IPool pool)
        {
            var v = _vHead = pool.Get <MeshUtils.Vertex>();
            var f = _fHead = pool.Get <MeshUtils.Face>();

            var pair = MeshUtils.EdgePair.Create(pool);
            var e    = _eHead = pair._e;
            var eSym = _eHeadSym = pair._eSym;

            v._next   = v._prev = v;
            v._anEdge = null;

            f._next   = f._prev = f;
            f._anEdge = null;
            f._trail  = null;
            f._marked = false;
            f._inside = false;

            e._next         = e;
            e._Sym          = eSym;
            e._Onext        = null;
            e._Lnext        = null;
            e._Org          = null;
            e._Lface        = null;
            e._winding      = 0;
            e._activeRegion = null;

            eSym._next         = eSym;
            eSym._Sym          = e;
            eSym._Onext        = null;
            eSym._Lnext        = null;
            eSym._Org          = null;
            eSym._Lface        = null;
            eSym._winding      = 0;
            eSym._activeRegion = null;
        }
Exemple #18
0
        // ReSharper restore InconsistentNaming
        public Mesh()
        {
            var v = _vHead = new MeshUtils.Vertex();
            var f = _fHead = new MeshUtils.Face();

            var pair = MeshUtils.EdgePair.Create();
            var e = _eHead = pair._e;
            var eSym = _eHeadSym = pair._eSym;

            v._next = v._prev = v;
            v._anEdge = null;

            f._next = f._prev = f;
            f._anEdge = null;
            f._trail = null;
            f._marked = false;
            f._inside = false;

            e._next = e;
            e._Sym = eSym;
            e._Onext = null;
            e._Lnext = null;
            e._Org = null;
            e._Lface = null;
            e._winding = 0;
            e._activeRegion = null;

            eSym._next = eSym;
            eSym._Sym = e;
            eSym._Onext = null;
            eSym._Lnext = null;
            eSym._Org = null;
            eSym._Lface = null;
            eSym._winding = 0;
            eSym._activeRegion = null;
        }
Exemple #19
0
    private void ProjectPolygon()
    {
        Vec3 norm = _normal;

        bool computedNormal = false;

        switch (norm.X)
        {
        case 0.0f when norm.Y == 0.0f && norm.Z == 0.0f:
            ComputeNormal(ref norm);
            _normal        = norm;
            computedNormal = true;
            break;
        }

        int i = Vec3.LongAxis(ref norm);

        _sUnit[i]           = 0;
        _sUnit[(i + 1) % 3] = SUnitX;
        _sUnit[(i + 2) % 3] = SUnitY;

        _tUnit[i]           = 0;
        _tUnit[(i + 1) % 3] = norm[i] > 0.0f ? -SUnitY : SUnitY;
        _tUnit[(i + 2) % 3] = norm[i] > 0.0f ? SUnitX : -SUnitX;

        // Project the vertices onto the sweep plane
        for (MeshUtils.Vertex v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
        {
            Vec3.Dot(ref v._coords, ref _sUnit, out v._s);
            Vec3.Dot(ref v._coords, ref _tUnit, out v._t);
        }
        switch (computedNormal)
        {
        case true:
            CheckOrientation();
            break;
        }

        // Compute ST bounds.
        bool first = true;

        for (MeshUtils.Vertex v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
        {
            switch (first)
            {
            case true:
                _bminX = _bmaxX = v._s;
                _bminY = _bmaxY = v._t;
                first  = false;
                break;

            default:
            {
                if (v._s < _bminX)
                {
                    _bminX = v._s;
                }

                if (v._s > _bmaxX)
                {
                    _bmaxX = v._s;
                }

                if (v._t < _bminY)
                {
                    _bminY = v._t;
                }

                if (v._t > _bmaxY)
                {
                    _bmaxY = v._t;
                }

                break;
            }
            }
        }
    }
Exemple #20
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)
        {
            var regLo = RegionBelow(regUp);
            var eUp = regUp.EUp;
            var eLo = regLo.EUp;
            var orgUp = eUp._Org;
            var orgLo = eLo._Org;
            var dstUp = eUp.Dst;
            var 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;
            }

            var tMinUp = Math.Min(orgUp._t, dstUp._t);
            var 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

            var isect = new 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).
            var 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);
                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(eUp._Sym);
                    _mesh.Splice(eLo._Sym, eUp);
                    regUp = TopLeftRegion(regUp);
                    eUp = RegionBelow(regUp).EUp;
                    FinishLeftRegions(RegionBelow(regUp), regLo);
                    AddRightEdges(regUp, eUp.Oprev, eUp, eUp, true);
                    return true;
                }
                if( dstUp == _event ) {
                    /* Splice dstUp into eLo, and process the new region(s) */
                    _mesh.SplitEdge(eLo._Sym);
                    _mesh.Splice(eUp._Lnext, eLo.Oprev);
                    regLo = regUp;
                    regUp = TopRightRegion(regUp);
                    var e = RegionBelow(regUp).EUp.Rprev;
                    regLo.EUp = eLo.Oprev;
                    eLo = FinishLeftRegions(regLo, null);
                    AddRightEdges(regUp, eLo._Onext, eUp.Rprev, e, true);
                    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(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(eLo._Sym);
                    eLo._Org._s = _event._s;
                    eLo._Org._t = _event._t;
                }
                // leave the rest for ConnectRightVertex
                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(eUp._Sym);
            _mesh.SplitEdge(eLo._Sym);
            _mesh.Splice(eLo.Oprev, eUp);
            eUp._Org._s = isect._s;
            eUp._Org._t = isect._t;
            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;
        }
Exemple #21
0
    private void ComputeNormal(ref Vec3 norm)
    {
        MeshUtils.Vertex v = _mesh._vHead._next;

        double[]           minVal  = { v._coords.X, v._coords.Y, v._coords.Z };
        MeshUtils.Vertex[] minVert = { v, v, v };
        double[]           maxVal  = { v._coords.X, v._coords.Y, v._coords.Z };
        MeshUtils.Vertex[] maxVert = { v, v, v };

        for (; v != _mesh._vHead; v = v._next)
        {
            if (v._coords.X < minVal[0])
            {
                minVal[0] = v._coords.X; minVert[0] = v;
            }
            if (v._coords.Y < minVal[1])
            {
                minVal[1] = v._coords.Y; minVert[1] = v;
            }
            if (v._coords.Z < minVal[2])
            {
                minVal[2] = v._coords.Z; minVert[2] = v;
            }
            if (v._coords.X > maxVal[0])
            {
                maxVal[0] = v._coords.X; maxVert[0] = v;
            }
            if (v._coords.Y > maxVal[1])
            {
                maxVal[1] = v._coords.Y; maxVert[1] = v;
            }

            if (!(v._coords.Z > maxVal[2]))
            {
                continue;
            }

            maxVal[2] = v._coords.Z; maxVert[2] = v;
        }

        // Find two vertices separated by at least 1/sqrt(3) of the maximum
        // distance between any two vertices
        int i = 0;

        if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0])
        {
            i = 1;
        }
        if (maxVal[2] - minVal[2] > maxVal[i] - minVal[i])
        {
            i = 2;
        }
        if (minVal[i] >= maxVal[i])
        {
            // All vertices are the same -- normal doesn't matter
            norm = new Vec3(0, 0, 1);
            return;
        }

        // Look for a third vertex which forms the triangle with maximum area
        // (Length of normal == twice the triangle area)
        double maxLen2 = 0;

        MeshUtils.Vertex v1 = minVert[i];
        MeshUtils.Vertex v2 = maxVert[i];
        Vec3.Sub(ref v1._coords, ref v2._coords, out Vec3 d1);
        for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
        {
            Vec3.Sub(ref v._coords, ref v2._coords, out Vec3 d2);
            Vec3 tNorm;
            tNorm.X = d1.Y * d2.Z - d1.Z * d2.Y;
            tNorm.Y = d1.Z * d2.X - d1.X * d2.Z;
            tNorm.Z = d1.X * d2.Y - d1.Y * d2.X;
            double tLen2 = tNorm.X * tNorm.X + tNorm.Y * tNorm.Y + tNorm.Z * tNorm.Z;
            if (!(tLen2 > maxLen2))
            {
                continue;
            }

            maxLen2 = tLen2;
            norm    = tNorm;
        }

        switch (maxLen2)
        {
        case <= 0.0f:
            // All points lie on a single line -- any decent normal will do
            norm    = Vec3.Zero;
            i       = Vec3.LongAxis(ref d1);
            norm[i] = 1;
            break;
        }
    }
Exemple #22
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);
    }
Exemple #23
0
 public static bool VertLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs)
 {
     return (lhs._s < rhs._s) || (lhs._s == rhs._s && lhs._t <= rhs._t);
 }
Exemple #24
0
 public static bool VertEq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs)
 {
     return lhs._s == rhs._s && lhs._t == rhs._t;
 }
Exemple #25
0
 public static bool VertCCW(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w)
 {
     return (u._s * (v._t - w._t) + v._s * (w._t - u._t) + w._s * (u._t - v._t)) >= 0.0f;
 }
Exemple #26
0
        /// <summary>
        /// Given edges (o1,d1) and (o2,d2), compute their point of intersection.
        /// The computed point is guaranteed to lie in the intersection of the
        /// bounding rectangles defined by each edge.
        /// </summary>
        public static void EdgeIntersect(MeshUtils.Vertex o1, MeshUtils.Vertex d1, MeshUtils.Vertex o2, MeshUtils.Vertex d2, MeshUtils.Vertex v)
        {
            // This is certainly not the most efficient way to find the intersection
            // of two line segments, but it is very numerically stable.
            // 
            // Strategy: find the two middle vertices in the VertLeq ordering,
            // and interpolate the intersection s-value from these.  Then repeat
            // using the TransLeq ordering to find the intersection t-value.

            if (!VertLeq(o1, d1)) { Swap(ref o1, ref d1); }
            if (!VertLeq(o2, d2)) { Swap(ref o2, ref d2); }
            if (!VertLeq(o1, o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); }

            if (!VertLeq(o2, d1))
            {
                // Technically, no intersection -- do our best
                v._s = (o2._s + d1._s) / 2.0f;
            }
            else if (VertLeq(d1, d2))
            {
                // Interpolate between o2 and d1
                var z1 = EdgeEval(o1, o2, d1);
                var z2 = EdgeEval(o2, d1, d2);
                if (z1 + z2 < 0.0f)
                {
                    z1 = -z1;
                    z2 = -z2;
                }
                v._s = Interpolate(z1, o2._s, z2, d1._s);
            }
            else
            {
                // Interpolate between o2 and d2
                var z1 = EdgeSign(o1, o2, d1);
                var z2 = -EdgeSign(o1, d2, d1);
                if (z1 + z2 < 0.0f)
                {
                    z1 = -z1;
                    z2 = -z2;
                }
                v._s = Interpolate(z1, o2._s, z2, d2._s);
            }

            // Now repeat the process for t

            if (!TransLeq(o1, d1)) { Swap(ref o1, ref d1); }
            if (!TransLeq(o2, d2)) { Swap(ref o2, ref d2); }
            if (!TransLeq(o1, o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); }

            if (!TransLeq(o2, d1))
            {
                // Technically, no intersection -- do our best
                v._t = (o2._t + d1._t) / 2.0f;
            }
            else if (TransLeq(d1, d2))
            {
                // Interpolate between o2 and d1
                var z1 = TransEval(o1, o2, d1);
                var z2 = TransEval(o2, d1, d2);
                if (z1 + z2 < 0.0f)
                {
                    z1 = -z1;
                    z2 = -z2;
                }
                v._t = Interpolate(z1, o2._t, z2, d1._t);
            }
            else
            {
                // Interpolate between o2 and d2
                var z1 = TransSign(o1, o2, d1);
                var z2 = -TransSign(o1, d2, d1);
                if (z1 + z2 < 0.0f)
                {
                    z1 = -z1;
                    z2 = -z2;
                }
                v._t = Interpolate(z1, o2._t, z2, d2._t);
            }
        }
Exemple #27
0
 public static Real VertL1dist(MeshUtils.Vertex u, MeshUtils.Vertex v)
 {
     return Math.Abs(u._s - v._s) + Math.Abs(u._t - v._t);
 }
Exemple #28
0
 public override void Reset()
 {
     _vHead = null;
     _fHead = null;
     _eHead = _eHeadSym = null;
 }
Exemple #29
0
 public static bool TransLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs)
 {
     return (lhs._t < rhs._t) || (lhs._t == rhs._t && lhs._s <= rhs._s);
 }