Example #1
0
 private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge)
 {
     _mesh.Delete(reg._eUp);
     reg._fixUpperEdge     = false;
     reg._eUp              = newEdge;
     newEdge._activeRegion = reg;
 }
Example #2
0
        private void DeleteRegion(ActiveRegion reg)
        {
            bool fixUpperEdge = reg._fixUpperEdge;

            reg._eUp._activeRegion = null;
            _dict.Remove(reg._nodeUp);
        }
Example #3
0
        private void SweepEvent(MeshUtils.Vertex vEvent)
        {
            _event = vEvent;
            MeshUtils.Edge edge = vEvent._anEdge;
            while (edge._activeRegion == null)
            {
                edge = edge._Onext;
                if (edge == vEvent._anEdge)
                {
                    ConnectLeftVertex(vEvent);
                    return;
                }
            }
            ActiveRegion activeRegion  = TopLeftRegion(edge._activeRegion);
            ActiveRegion activeRegion2 = RegionBelow(activeRegion);

            MeshUtils.Edge eUp   = activeRegion2._eUp;
            MeshUtils.Edge edge2 = FinishLeftRegions(activeRegion2, null);
            if (edge2._Onext == eUp)
            {
                ConnectRightVertex(activeRegion, edge2);
            }
            else
            {
                ActiveRegion   regUp = activeRegion;
                MeshUtils.Edge onext = edge2._Onext;
                MeshUtils.Edge edge3 = eUp;
                AddRightEdges(regUp, onext, edge3, edge3, true);
            }
        }
Example #4
0
    /// <summary>
    /// When the upper or lower edge of any region changes, the region is
    /// marked "dirty".  This routine walks through all the dirty regions
    /// and makes sure that the dictionary invariants are satisfied
    /// (see the comments at the beginning of this file).  Of course
    /// new dirty regions can be created as we make changes to restore
    /// the invariants.
    /// </summary>
    private void WalkDirtyRegions(ActiveRegion regUp)
    {
        ActiveRegion regLo = RegionBelow(regUp);

        while (true)
        {
            // Find the lowest dirty region (we walk from the bottom up).
            while (regLo._dirty)
            {
                regUp = regLo;
                regLo = RegionBelow(regLo);
            }
            switch (regUp._dirty)
            {
            case false:
            {
                regLo = regUp;
                regUp = RegionAbove(regUp);
                if (regUp is not {
                        _dirty : true
                    })
                {
                    // We've walked all the dirty regions
                    return;
                }

                break;
            }
            }
Example #5
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);
    }
Example #6
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);
    }
Example #7
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)
        {
            var e1 = reg1._eUp;
            var e2 = reg2._eUp;

            if (e1._Dst == _event)
            {
                if (e2._Dst == _event)
                {
                    // 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;
                }
                return Geom.EdgeSign(e2._Dst, _event, e2._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
            var t1 = Geom.EdgeEval(e1._Dst, _event, e1._Org);
            var t2 = Geom.EdgeEval(e2._Dst, _event, e2._Org);
            return (t1 >= t2);
        }
Example #8
0
        private bool CheckForLeftSplice(ActiveRegion regUp)
        {
            ActiveRegion activeRegion = RegionBelow(regUp);

            MeshUtils.Edge eUp  = regUp._eUp;
            MeshUtils.Edge eUp2 = activeRegion._eUp;
            if (Geom.VertLeq(eUp._Dst, eUp2._Dst))
            {
                if (Geom.EdgeSign(eUp._Dst, eUp2._Dst, eUp._Org) < 0f)
                {
                    return(false);
                }
                RegionAbove(regUp)._dirty = (regUp._dirty = true);
                MeshUtils.Edge edge = _mesh.SplitEdge(eUp);
                _mesh.Splice(eUp2._Sym, edge);
                edge._Lface._inside = regUp._inside;
            }
            else
            {
                if (Geom.EdgeSign(eUp2._Dst, eUp._Dst, eUp2._Org) > 0f)
                {
                    return(false);
                }
                regUp._dirty = (activeRegion._dirty = true);
                MeshUtils.Edge edge2 = _mesh.SplitEdge(eUp2);
                _mesh.Splice(eUp._Lnext, eUp2._Sym);
                edge2._Rface._inside = regUp._inside;
            }
            return(true);
        }
Example #9
0
        private MeshUtils.Edge FinishLeftRegions(ActiveRegion regFirst, ActiveRegion regLast)
        {
            ActiveRegion activeRegion = regFirst;

            MeshUtils.Edge eUp = regFirst._eUp;
            while (activeRegion != regLast)
            {
                activeRegion._fixUpperEdge = false;
                ActiveRegion   activeRegion2 = RegionBelow(activeRegion);
                MeshUtils.Edge edge          = activeRegion2._eUp;
                if (edge._Org != eUp._Org)
                {
                    if (!activeRegion2._fixUpperEdge)
                    {
                        FinishRegion(activeRegion);
                        break;
                    }
                    edge = _mesh.Connect(eUp._Lprev, edge._Sym);
                    FixUpperEdge(activeRegion2, edge);
                }
                if (eUp._Onext != edge)
                {
                    _mesh.Splice(edge._Oprev, edge);
                    _mesh.Splice(eUp, edge);
                }
                FinishRegion(activeRegion);
                eUp          = activeRegion2._eUp;
                activeRegion = activeRegion2;
            }
            return(eUp);
        }
Example #10
0
        private bool EdgeLeq(ActiveRegion reg1, ActiveRegion reg2)
        {
            MeshUtils.Edge eUp  = reg1._eUp;
            MeshUtils.Edge eUp2 = reg2._eUp;
            if (eUp._Dst == _event)
            {
                if (eUp2._Dst == _event)
                {
                    if (Geom.VertLeq(eUp._Org, eUp2._Org))
                    {
                        return(Geom.EdgeSign(eUp2._Dst, eUp._Org, eUp2._Org) <= 0f);
                    }
                    return(Geom.EdgeSign(eUp._Dst, eUp2._Org, eUp._Org) >= 0f);
                }
                return(Geom.EdgeSign(eUp2._Dst, _event, eUp2._Org) <= 0f);
            }
            if (eUp2._Dst == _event)
            {
                return(Geom.EdgeSign(eUp._Dst, _event, eUp._Org) >= 0f);
            }
            float num  = Geom.EdgeEval(eUp._Dst, _event, eUp._Org);
            float num2 = Geom.EdgeEval(eUp2._Dst, _event, eUp2._Org);

            return(num >= num2);
        }
Example #11
0
        public Form1()
        {
            InitializeComponent();

            for (int x = 0; x < world.TotalWorldWidthCells; x++)
            {
                Rock cell = new Rock();
                cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance);
                world.GetCellAtPosition(x, 0).BaseSand = cell;

                cell       = new Rock();
                cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance);
                world.GetCellAtPosition(x, world.TotalWorldHeightCells - 1).BaseSand = cell;

                for (int xx = 0; xx < world.TotalWorldWidthCells - 2; xx++)
                {
                    for (int y = 1; y < 5; y++)
                    {
                        BaseSand sCell = world.RNG.Next(0, 2) == 0 ? (BaseSand) new Sand() : new BlackSand();
                        sCell.Color = RandomColorOffset(sCell.Color, rng, colorVariance, colorVariance, colorVariance);
                        world.GetCellAtPosition(xx, y).BaseSand = sCell;
                    }
                }
            }

            for (int y = 0; y < world.TotalWorldHeightCells; y++)
            {
                Rock cell = new Rock();
                cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance);
                world.GetCellAtPosition(0, y).BaseSand = cell;

                cell       = new Rock();
                cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance);
                world.GetCellAtPosition(world.TotalWorldWidthCells - 1, y).BaseSand = cell;
            }

            ImageBuffer        = new Bitmap((int)(world.TotalWorldWidthCells * xResolution), (int)(world.TotalWorldHeightCells * yResolution));
            backBufferGraphics = Graphics.FromImage(ImageBuffer);
            graphics           = drawArea.CreateGraphics();

            drawArea.MouseClick += DrawArea_MouseClick;

            for (int i = 0; i < SandTypes.Count; i++)
            {
                BaseSand type = SandTypes[i];
                Button   b    = new Button();
                b.Name = "bs:" + i;
                b.Text = type.Name;
                buttonLayout.Controls.Add(b);
                b.Click += B_Click;
            }

            ActiveRegion ar = new ActiveRegion(world, 0, 0, 8, 3);

            world.ActiveRegions.Add(ar);

            drawThread = new Thread(Draw);
            drawThread.Start();
        }
Example #12
0
 /// <summary>
 /// Replace an upper edge which needs fixing (see ConnectRightVertex).
 /// </summary>
 private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge)
 {
     Debug.Assert(reg._fixUpperEdge);
     Mesh.Delete(_pool, reg._eUp);
     reg._fixUpperEdge     = false;
     reg._eUp              = newEdge;
     newEdge._activeRegion = reg;
 }
Example #13
0
 private void FinishRegion(ActiveRegion reg)
 {
     MeshUtils.Edge eUp   = reg._eUp;
     MeshUtils.Face lface = eUp._Lface;
     lface._inside = reg._inside;
     lface._anEdge = eUp;
     DeleteRegion(reg);
 }
Example #14
0
    /// <summary>
    /// Delete a region from the sweep line. This happens when the upper
    /// and lower chains of a region meet (at a vertex on the sweep line).
    /// The "inside" flag is copied to the appropriate mesh face (we could
    /// not do this before -- since the structure of the mesh is always
    /// changing, this face may not have even existed until now).
    /// </summary>
    private void FinishRegion(ActiveRegion reg)
    {
        MeshUtils.Edge e = reg._eUp;
        MeshUtils.Face f = e._Lface;

        f._inside = reg._inside;
        f._anEdge = e;
        DeleteRegion(reg);
    }
Example #15
0
 private ActiveRegion TopRightRegion(ActiveRegion reg)
 {
     MeshUtils.Vertex dst = reg._eUp._Dst;
     do
     {
         reg = RegionAbove(reg);
     }while (reg._eUp._Dst == dst);
     return(reg);
 }
Example #16
0
        public static Node dictSearch(Dictionary dict, DictKey key)
        {
            Node node = dict._head;

            do
            {
                node = node.next;
            } while (node.Key != null && !ActiveRegion.EdgeLeq(dict._tess.currentSweepVertex, key, node.Key));
            return(node);
        }
Example #17
0
        public static Node dictSearch(Dictionary dict, DictKey key)
        {
            Node node = dict.head;

            do
            {
                node = node.next;
            } while (node.Key != null && !ActiveRegion.EdgeLeq(dict.tesseator, key, node.Key));
            return(node);
        }
Example #18
0
        private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp)
        {
            ActiveRegion activeRegion = new ActiveRegion();

            activeRegion._eUp          = eNewUp;
            activeRegion._nodeUp       = _dict.InsertBefore(regAbove._nodeUp, activeRegion);
            activeRegion._fixUpperEdge = false;
            activeRegion._sentinel     = false;
            activeRegion._dirty        = false;
            eNewUp._activeRegion       = activeRegion;
            return(activeRegion);
        }
Example #19
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);
    }
Example #20
0
    /// <summary>
    /// Add a new active region to the sweep line, *somewhere* below "regAbove"
    /// (according to where the new edge belongs in the sweep-line dictionary).
    /// The upper edge of the new region will be "eNewUp".
    /// Winding number and "inside" flag are not updated.
    /// </summary>
    private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp)
    {
        ActiveRegion regNew = _pool.Get <ActiveRegion>();

        regNew._eUp          = eNewUp;
        regNew._nodeUp       = _dict.InsertBefore(regAbove._nodeUp, regNew);
        regNew._fixUpperEdge = false;
        regNew._sentinel     = false;
        regNew._dirty        = false;

        eNewUp._activeRegion = regNew;

        return(regNew);
    }
Example #21
0
        private void AddRightEdges(ActiveRegion regUp, MeshUtils.Edge eFirst, MeshUtils.Edge eLast, MeshUtils.Edge eTopLeft, bool cleanUp)
        {
            bool flag = true;

            MeshUtils.Edge edge = eFirst;
            do
            {
                AddRegionBelow(regUp, edge._Sym);
                edge = edge._Onext;
            }while (edge != eLast);
            if (eTopLeft == null)
            {
                eTopLeft = RegionBelow(regUp)._eUp._Rprev;
            }
            ActiveRegion activeRegion = regUp;

            MeshUtils.Edge edge2 = eTopLeft;
            while (true)
            {
                ActiveRegion activeRegion2 = RegionBelow(activeRegion);
                edge = activeRegion2._eUp._Sym;
                if (edge._Org != edge2._Org)
                {
                    break;
                }
                if (edge._Onext != edge2)
                {
                    _mesh.Splice(edge._Oprev, edge);
                    _mesh.Splice(edge2._Oprev, edge);
                }
                activeRegion2._windingNumber = activeRegion._windingNumber - edge._winding;
                activeRegion2._inside        = Geom.IsWindingInside(_windingRule, activeRegion2._windingNumber);
                activeRegion._dirty          = true;
                if (!flag && CheckForRightSplice(activeRegion))
                {
                    Geom.AddWinding(edge, edge2);
                    DeleteRegion(activeRegion);
                    _mesh.Delete(edge2);
                }
                flag         = false;
                activeRegion = activeRegion2;
                edge2        = edge;
            }
            activeRegion._dirty = true;
            if (cleanUp)
            {
                WalkDirtyRegions(activeRegion);
            }
        }
Example #22
0
 private ActiveRegion TopLeftRegion(ActiveRegion reg)
 {
     MeshUtils.Vertex org = reg._eUp._Org;
     do
     {
         reg = RegionAbove(reg);
     }while (reg._eUp._Org == org);
     if (reg._fixUpperEdge)
     {
         MeshUtils.Edge newEdge = _mesh.Connect(RegionBelow(reg)._eUp._Sym, reg._eUp._Lnext);
         FixUpperEdge(reg, newEdge);
         reg = RegionAbove(reg);
     }
     return(reg);
 }
Example #23
0
 private void DeleteRegion(ActiveRegion reg)
 {
     switch (reg._fixUpperEdge)
     {
     case true:
         // It was created with zero winding number, so it better be
         // deleted with zero winding number (ie. it better not get merged
         // with a real edge).
         Debug.Assert(reg._eUp._winding == 0);
         break;
     }
     reg._eUp._activeRegion = null;
     _dict.Remove(reg._nodeUp);
     _pool.Return(reg);
 }
Example #24
0
        public Node InsertBefore(Node node, DictKey key)
        {
            do
            {
                node = node.prev;
            } while (node.Key != null && !ActiveRegion.EdgeLeq(_tess.currentSweepVertex, node.Key, key));

            var newNode = new Node();

            newNode.Key    = key;
            newNode.next   = node.next;
            node.next.prev = newNode;
            newNode.prev   = node;
            node.next      = newNode;
            return(newNode);
        }
Example #25
0
        public Node InsertBefore(Node node, DictKey key)
        {
            Node newNode;

            do
            {
                node = node.prev;
            } while (node.Key != null &&
                     !ActiveRegion.EdgeLeq(this.tesseator, node.Key, key));
            newNode        = new Node();
            newNode.Key    = key;
            newNode.next   = node.next;
            node.next.prev = newNode;
            newNode.prev   = node;
            node.next      = newNode;
            return(newNode);
        }
Example #26
0
        private void ConnectRightVertex(ActiveRegion regUp, MeshUtils.Edge eBottomLeft)
        {
            MeshUtils.Edge edge         = eBottomLeft._Onext;
            ActiveRegion   activeRegion = RegionBelow(regUp);

            MeshUtils.Edge eUp  = regUp._eUp;
            MeshUtils.Edge eUp2 = activeRegion._eUp;
            bool           flag = false;

            if (eUp._Dst != eUp2._Dst)
            {
                CheckForIntersect(regUp);
            }
            if (Geom.VertEq(eUp._Org, _event))
            {
                _mesh.Splice(edge._Oprev, eUp);
                regUp = TopLeftRegion(regUp);
                edge  = RegionBelow(regUp)._eUp;
                FinishLeftRegions(RegionBelow(regUp), activeRegion);
                flag = true;
            }
            if (Geom.VertEq(eUp2._Org, _event))
            {
                _mesh.Splice(eBottomLeft, eUp2._Oprev);
                eBottomLeft = FinishLeftRegions(activeRegion, null);
                flag        = true;
            }
            if (flag)
            {
                ActiveRegion   regUp2 = regUp;
                MeshUtils.Edge onext  = eBottomLeft._Onext;
                MeshUtils.Edge edge2  = edge;
                AddRightEdges(regUp2, onext, edge2, edge2, true);
            }
            else
            {
                MeshUtils.Edge eDst = (!Geom.VertLeq(eUp2._Org, eUp._Org)) ? eUp : eUp2._Oprev;
                eDst = _mesh.Connect(eBottomLeft._Lprev, eDst);
                ActiveRegion   regUp3 = regUp;
                MeshUtils.Edge edge3  = eDst;
                AddRightEdges(regUp3, edge3, edge3._Onext, eDst._Onext, false);
                eDst._Sym._activeRegion._fixUpperEdge = true;
                WalkDirtyRegions(regUp);
            }
        }
Example #27
0
        private void AddSentinel(float smin, float smax, float t)
        {
            MeshUtils.Edge edge = _mesh.MakeEdge();
            edge._Org._s = smax;
            edge._Org._t = t;
            edge._Dst._s = smin;
            edge._Dst._t = t;
            _event       = edge._Dst;
            ActiveRegion activeRegion = new ActiveRegion();

            activeRegion._eUp           = edge;
            activeRegion._windingNumber = 0;
            activeRegion._inside        = false;
            activeRegion._fixUpperEdge  = false;
            activeRegion._sentinel      = true;
            activeRegion._dirty         = false;
            activeRegion._nodeUp        = _dict.Insert(activeRegion);
        }
Example #28
0
 private void Awake()
 {
     Instance = this;
     grid     = GetComponent <Grid>();
     for (int i = radius; i > -radius; i--)
     {
         for (int j = radius; j > -radius; j--)
         {
             if (Math.Abs(i) < startRadius && Math.Abs(j) < startRadius)
             {
                 activeCells.Add(new Vector3Int(i, j, 0));
                 continue;
             }
             Vector3Int cell = new Vector3Int(i, j, 0);
             tilemap.SetTile(cell, shadeTile);
         }
     }
 }
Example #29
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);
    }
Example #30
0
    /// <summary>
    /// We are given a vertex with one or more left-going edges.  All affected
    /// edges should be in the edge dictionary.  Starting at regFirst->eUp,
    /// we walk down deleting all regions where both edges have the same
    /// origin vOrg.  At the same time we copy the "inside" flag from the
    /// active region to the face, since at this point each face will belong
    /// to at most one region (this was not necessarily true until this point
    /// in the sweep).  The walk stops at the region above regLast; if regLast
    /// is null we walk as far as possible.  At the same time we relink the
    /// mesh if necessary, so that the ordering of edges around vOrg is the
    /// same as in the dictionary.
    /// </summary>
    private MeshUtils.Edge FinishLeftRegions(ActiveRegion regFirst, ActiveRegion regLast)
    {
        ActiveRegion regPrev = regFirst;

        MeshUtils.Edge ePrev = regFirst._eUp;

        while (regPrev != regLast)
        {
            regPrev._fixUpperEdge = false;      // placement was OK
            ActiveRegion   reg = RegionBelow(regPrev);
            MeshUtils.Edge e   = reg._eUp;
            if (e._Org != ePrev._Org)
            {
                if (!reg._fixUpperEdge)
                {
                    // Remove the last left-going edge.  Even though there are no further
                    // edges in the dictionary with this origin, there may be further
                    // such edges in the mesh (if we are adding left edges to a vertex
                    // that has already been processed).  Thus it is important to call
                    // FinishRegion rather than just DeleteRegion.
                    FinishRegion(regPrev);
                    break;
                }
                // If the edge below was a temporary edge introduced by
                // ConnectRightVertex, now is the time to fix it.
                e = Mesh.Connect(_pool, ePrev._Lprev, e._Sym);
                FixUpperEdge(reg, e);
            }

            // Relink edges so that ePrev.Onext == e
            if (ePrev._Onext != e)
            {
                Mesh.Splice(_pool, e._Oprev, e);
                Mesh.Splice(_pool, ePrev, e);
            }
            FinishRegion(regPrev); // may change reg.eUp
            ePrev   = reg._eUp;
            regPrev = reg;
        }

        return(ePrev);
    }
Example #31
0
 private void ConnectLeftDegenerate(ActiveRegion regUp, MeshUtils.Vertex vEvent)
 {
     MeshUtils.Edge eUp = regUp._eUp;
     if (Geom.VertEq(eUp._Org, vEvent))
     {
         throw new InvalidOperationException("Vertices should have been merged before");
     }
     if (!Geom.VertEq(eUp._Dst, vEvent))
     {
         _mesh.SplitEdge(eUp._Sym);
         if (regUp._fixUpperEdge)
         {
             _mesh.Delete(eUp._Onext);
             regUp._fixUpperEdge = false;
         }
         _mesh.Splice(vEvent._anEdge, eUp);
         SweepEvent(vEvent);
         return;
     }
     throw new InvalidOperationException("Vertices should have been merged before");
 }
Example #32
0
        /// <summary>
        /// The event vertex lies exacty on an already-processed edge or vertex.
        /// Adding the new vertex involves splicing it into the already-processed
        /// part of the mesh.
        /// </summary>
        private void ConnectLeftDegenerate(ActiveRegion regUp, MeshUtils.Vertex vEvent)
        {
            var e = regUp._eUp;
            if (Geom.VertEq(e._Org, vEvent))
            {
                // e.Org is an unprocessed vertex - just combine them, and wait
                // for e.Org to be pulled from the queue
                // C# : in the C version, there is a flag but it was never implemented
                // the vertices are before beginning the tesselation
                throw new InvalidOperationException("Vertices should have been merged before");
            }

            if (!Geom.VertEq(e._Dst, vEvent))
            {
                // General case -- splice vEvent into edge e which passes through it
                _mesh.SplitEdge(e._Sym);
                if (regUp._fixUpperEdge)
                {
                    // This edge was fixable -- delete unused portion of original edge
                    _mesh.Delete(e._Onext);
                    regUp._fixUpperEdge = false;
                }
                _mesh.Splice(vEvent._anEdge, e);
                SweepEvent(vEvent);	// recurse
                return;
            }

            // See above
            throw new InvalidOperationException("Vertices should have been merged before");
        }
Example #33
0
 private void ComputeWinding(ActiveRegion reg)
 {
     reg._windingNumber = RegionAbove(reg)._windingNumber + reg._eUp._winding;
     reg._inside = Geom.IsWindingInside(_windingRule, reg._windingNumber);
 }
Example #34
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)
        {
            var regLo = RegionBelow(regUp);
            var eUp = regUp._eUp;
            var 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(eLo._Sym);
                    _mesh.Splice(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(eUp._Sym);
                _mesh.Splice(eLo._Oprev, eUp);
            }
            return true;
        }
Example #35
0
 private ActiveRegion RegionBelow(ActiveRegion reg)
 {
     return reg._nodeUp.Prev.Key;
 }
Example #36
0
 private ActiveRegion RegionAbove(ActiveRegion reg)
 {
     return reg._nodeUp._next._key;
 }
Example #37
0
        /// <summary>
        /// Delete a region from the sweep line. This happens when the upper
        /// and lower chains of a region meet (at a vertex on the sweep line).
        /// The "inside" flag is copied to the appropriate mesh face (we could
        /// not do this before -- since the structure of the mesh is always
        /// changing, this face may not have even existed until now).
        /// </summary>
        private void FinishRegion(ActiveRegion reg)
        {
            var e = reg._eUp;
            var f = e._Lface;

            f._inside = reg._inside;
            f._anEdge = e;
            DeleteRegion(reg);
        }
Example #38
0
 private void DeleteRegion(ActiveRegion reg)
 {
     if (reg._fixUpperEdge)
     {
         // It was created with zero winding number, so it better be
         // deleted with zero winding number (ie. it better not get merged
         // with a real edge).
         Debug.Assert(reg._eUp._winding == 0);
     }
     reg._eUp._activeRegion = null;
     _dict.Remove(reg._nodeUp);
 }
Example #39
0
        /// <summary>
        /// When the upper or lower edge of any region changes, the region is
        /// marked "dirty".  This routine walks through all the dirty regions
        /// and makes sure that the dictionary invariants are satisfied
        /// (see the comments at the beginning of this file).  Of course
        /// new dirty regions can be created as we make changes to restore
        /// the invariants.
        /// </summary>
        private void WalkDirtyRegions(ActiveRegion regUp)
        {
            var regLo = RegionBelow(regUp);
            MeshUtils.Edge eUp, eLo;

            while (true)
            {
                // Find the lowest dirty region (we walk from the bottom up).
                while (regLo._dirty)
                {
                    regUp = regLo;
                    regLo = RegionBelow(regLo);
                }
                if (!regUp._dirty)
                {
                    regLo = regUp;
                    regUp = RegionAbove( regUp );
                    if(regUp == null || !regUp._dirty)
                    {
                        // We've walked all the dirty regions
                        return;
                    }
                }
                regUp._dirty = false;
                eUp = regUp._eUp;
                eLo = regLo._eUp;

                if (eUp._Dst != eLo._Dst)
                {
                    // Check that the edge ordering is obeyed at the Dst vertices.
                    if (CheckForLeftSplice(regUp))
                    {

                        // If the upper or lower edge was marked fixUpperEdge, then
                        // we no longer need it (since these edges are needed only for
                        // vertices which otherwise have no right-going edges).
                        if (regLo._fixUpperEdge)
                        {
                            DeleteRegion(regLo);
                            _mesh.Delete(eLo);
                            regLo = RegionBelow(regUp);
                            eLo = regLo._eUp;
                        }
                        else if( regUp._fixUpperEdge )
                        {
                            DeleteRegion(regUp);
                            _mesh.Delete(eUp);
                            regUp = RegionAbove(regLo);
                            eUp = regUp._eUp;
                        }
                    }
                }
                if (eUp._Org != eLo._Org)
                {
                    if(    eUp._Dst != eLo._Dst
                        && ! regUp._fixUpperEdge && ! regLo._fixUpperEdge
                        && (eUp._Dst == _event || eLo._Dst == _event) )
                    {
                        // When all else fails in CheckForIntersect(), it uses tess._event
                        // as the intersection location. To make this possible, it requires
                        // that tess._event lie between the upper and lower edges, and also
                        // that neither of these is marked fixUpperEdge (since in the worst
                        // case it might splice one of these edges into tess.event, and
                        // violate the invariant that fixable edges are the only right-going
                        // edge from their associated vertex).
                        if (CheckForIntersect(regUp))
                        {
                            // WalkDirtyRegions() was called recursively; we're done
                            return;
                        }
                    }
                    else
                    {
                        // Even though we can't use CheckForIntersect(), the Org vertices
                        // may violate the dictionary edge ordering. Check and correct this.
                        CheckForRightSplice(regUp);
                    }
                }
                if (eUp._Org == eLo._Org && eUp._Dst == eLo._Dst)
                {
                    // A degenerate loop consisting of only two edges -- delete it.
                    Geom.AddWinding(eLo, eUp);
                    DeleteRegion(regUp);
                    _mesh.Delete(eUp);
                    regUp = RegionAbove(regLo);
                }
            }
        }
Example #40
0
        private ActiveRegion TopRightRegion(ActiveRegion reg)
        {
            var 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;
        }
Example #41
0
        private ActiveRegion TopLeftRegion(ActiveRegion reg)
        {
            var org = reg._eUp._Org;

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

            // If the edge above was a temporary edge introduced by ConnectRightVertex,
            // now is the time to fix it.
            if (reg._fixUpperEdge)
            {
                var e = _mesh.Connect(RegionBelow(reg)._eUp._Sym, reg._eUp._Lnext);
                FixUpperEdge(reg, e);
                reg = RegionAbove(reg);
            }

            return reg;
        }
Example #42
0
        /// <summary>
        /// Add a new active region to the sweep line, *somewhere* below "regAbove"
        /// (according to where the new edge belongs in the sweep-line dictionary).
        /// The upper edge of the new region will be "eNewUp".
        /// Winding number and "inside" flag are not updated.
        /// </summary>
        private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp)
        {
            var regNew = new ActiveRegion();

            regNew._eUp = eNewUp;
            regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew);
            regNew._fixUpperEdge = false;
            regNew._sentinel = false;
            regNew._dirty = false;

            eNewUp._activeRegion = regNew;

            return regNew;
        }
Example #43
0
        /// <summary>
        /// Add a new active region to the sweep line, *somewhere* below "regAbove"
        /// (according to where the new edge belongs in the sweep-line dictionary).
        /// The upper edge of the new region will be "eNewUp".
        /// Winding number and "inside" flag are not updated.
        /// </summary>
        private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp)
        {
            var regNew = new ActiveRegion();

            regNew._eUp = eNewUp;
            regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew);

            eNewUp._activeRegion = regNew;

            return regNew;
        }
Example #44
0
        /// <summary>
        /// Make the sentinel coordinates big enough that they will never be
        /// merged with real input features.
        /// 
        /// We add two sentinel edges above and below all other edges,
        /// to avoid special cases at the top and bottom.
        /// </summary>
        private void AddSentinel(float smin, float smax, float t)
        {
            var e = _mesh.MakeEdge();
            e._Org._s = smax;
            e._Org._t = t;
            e._Dst._s = smin;
            e._Dst._t = t;
            _event = e._Dst; // initialize it

            var reg = new ActiveRegion();
            reg._eUp = e;
            reg._sentinel = true;
            reg._nodeUp = _dict.Insert(reg);
        }
Example #45
0
        /// <summary>
        /// Purpose: connect a "left" vertex (one where both edges go right)
        /// to the processed portion of the mesh.  Let R be the active region
        /// containing vEvent, and let U and L be the upper and lower edge
        /// chains of R.  There are two possibilities:
        /// 
        /// - the normal case: split R into two regions, by connecting vEvent to
        ///   the rightmost vertex of U or L lying to the left of the sweep line
        /// 
        /// - the degenerate case: if vEvent is close enough to U or L, we
        ///   merge vEvent into that edge chain.  The subcases are:
        ///     - merging with the rightmost vertex of U or L
        ///     - merging with the active edge of U or L
        ///     - merging with an already-processed portion of U or L
        /// </summary>
        private void ConnectLeftVertex(MeshUtils.Vertex vEvent)
        {
            var tmp = new ActiveRegion();

            // Get a pointer to the active region containing vEvent
            tmp._eUp = vEvent._anEdge._Sym;
            var regUp = _dict.Find(tmp).Key;
            var regLo = RegionBelow(regUp);
            if (regLo == null)
            {
                // This may happen if the input polygon is coplanar.
                return;
            }
            var eUp = regUp._eUp;
            var eLo = regLo._eUp;

            // Try merging with U or L first
            if (Geom.EdgeSign(eUp._Dst, vEvent, eUp._Org) == 0.0f)
            {
                ConnectLeftDegenerate(regUp, vEvent);
                return;
            }

            // Connect vEvent to rightmost processed vertex of either chain.
            // e._Dst is the vertex that we will connect to vEvent.
            var reg = Geom.VertLeq(eLo._Dst, eUp._Dst) ? regUp : regLo;

            if (regUp._inside || reg._fixUpperEdge)
            {
                MeshUtils.Edge eNew;
                if (reg == regUp)
                {
                    eNew = _mesh.Connect(vEvent._anEdge._Sym, eUp._Lnext);
                }
                else
                {
                    eNew = _mesh.Connect(eLo._Dnext, vEvent._anEdge)._Sym;
                }
                if (reg._fixUpperEdge)
                {
                    FixUpperEdge(reg, eNew);
                }
                else
                {
                    ComputeWinding(AddRegionBelow(regUp, eNew));
                }
                SweepEvent(vEvent);
            }
            else
            {
                // The new vertex is in a region which does not belong to the polygon.
                // We don't need to connect this vertex to the rest of the mesh.
                AddRightEdges(regUp, vEvent._anEdge, vEvent._anEdge, null, true);
            }
        }
Example #46
0
        /// <summary>
        /// Purpose: connect a "right" vertex vEvent (one where all edges go left)
        /// to the unprocessed portion of the mesh.  Since there are no right-going
        /// edges, two regions (one above vEvent and one below) are being merged
        /// into one.  "regUp" is the upper of these two regions.
        /// 
        /// There are two reasons for doing this (adding a right-going edge):
        ///  - if the two regions being merged are "inside", we must add an edge
        ///    to keep them separated (the combined region would not be monotone).
        ///  - in any case, we must leave some record of vEvent in the dictionary,
        ///    so that we can merge vEvent with features that we have not seen yet.
        ///    For example, maybe there is a vertical edge which passes just to
        ///    the right of vEvent; we would like to splice vEvent into this edge.
        /// 
        /// However, we don't want to connect vEvent to just any vertex.  We don''t
        /// want the new edge to cross any other edges; otherwise we will create
        /// intersection vertices even when the input data had no self-intersections.
        /// (This is a bad thing; if the user's input data has no intersections,
        /// we don't want to generate any false intersections ourselves.)
        /// 
        /// Our eventual goal is to connect vEvent to the leftmost unprocessed
        /// vertex of the combined region (the union of regUp and regLo).
        /// But because of unseen vertices with all right-going edges, and also
        /// new vertices which may be created by edge intersections, we don''t
        /// know where that leftmost unprocessed vertex is.  In the meantime, we
        /// connect vEvent to the closest vertex of either chain, and mark the region
        /// as "fixUpperEdge".  This flag says to delete and reconnect this edge
        /// to the next processed vertex on the boundary of the combined region.
        /// Quite possibly the vertex we connected to will turn out to be the
        /// closest one, in which case we won''t need to make any changes.
        /// </summary>
        private void ConnectRightVertex(ActiveRegion regUp, MeshUtils.Edge eBottomLeft)
        {
            var eTopLeft = eBottomLeft._Onext;
            var regLo = RegionBelow(regUp);
            var eUp = regUp._eUp;
            var eLo = regLo._eUp;
            bool degenerate = false;

            if (eUp._Dst != eLo._Dst)
            {
                CheckForIntersect(regUp);
            }

            // Possible new degeneracies: upper or lower edge of regUp may pass
            // through vEvent, or may coincide with new intersection vertex
            if (Geom.VertEq(eUp._Org, _event))
            {
                _mesh.Splice(eTopLeft._Oprev, eUp);
                regUp = TopLeftRegion(regUp);
                eTopLeft = RegionBelow(regUp)._eUp;
                FinishLeftRegions(RegionBelow(regUp), regLo);
                degenerate = true;
            }
            if (Geom.VertEq(eLo._Org, _event))
            {
                _mesh.Splice(eBottomLeft, eLo._Oprev);
                eBottomLeft = FinishLeftRegions(regLo, null);
                degenerate = true;
            }
            if (degenerate)
            {
                AddRightEdges(regUp, eBottomLeft._Onext, eTopLeft, eTopLeft, true);
                return;
            }

            // Non-degenerate situation -- need to add a temporary, fixable edge.
            // Connect to the closer of eLo.Org, eUp.Org.
            MeshUtils.Edge eNew;
            if (Geom.VertLeq(eLo._Org, eUp._Org))
            {
                eNew = eLo._Oprev;
            }
            else
            {
                eNew = eUp;
            }
            eNew = _mesh.Connect(eBottomLeft._Lprev, eNew);

            // Prevent cleanup, otherwise eNew might disappear before we've even
            // had a chance to mark it as a temporary edge.
            AddRightEdges(regUp, eNew, eNew._Onext, eNew._Onext, false);
            eNew._Sym._activeRegion._fixUpperEdge = true;
            WalkDirtyRegions(regUp);
        }
Example #47
0
        /// <summary>
        /// Purpose: insert right-going edges into the edge dictionary, and update
        /// winding numbers and mesh connectivity appropriately.  All right-going
        /// edges share a common origin vOrg.  Edges are inserted CCW starting at
        /// eFirst; the last edge inserted is eLast.Oprev.  If vOrg has any
        /// left-going edges already processed, then eTopLeft must be the edge
        /// such that an imaginary upward vertical segment from vOrg would be
        /// contained between eTopLeft.Oprev and eTopLeft; otherwise eTopLeft
        /// should be null.
        /// </summary>
        private void AddRightEdges(ActiveRegion regUp, MeshUtils.Edge eFirst, MeshUtils.Edge eLast, MeshUtils.Edge eTopLeft, bool cleanUp)
        {
            bool firstTime = true;

            var e = eFirst; do
            {
                Debug.Assert(Geom.VertLeq(e._Org, e._Dst));
                AddRegionBelow(regUp, e._Sym);
                e = e._Onext;
            } while (e != eLast);

            // Walk *all* right-going edges from e.Org, in the dictionary order,
            // updating the winding numbers of each region, and re-linking the mesh
            // edges to match the dictionary ordering (if necessary).
            if (eTopLeft == null)
            {
                eTopLeft = RegionBelow(regUp)._eUp._Rprev;
            }

            ActiveRegion regPrev = regUp, reg;
            var ePrev = eTopLeft;
            while (true)
            {
                reg = RegionBelow(regPrev);
                e = reg._eUp._Sym;
                if (e._Org != ePrev._Org) break;

                if (e._Onext != ePrev)
                {
                    // Unlink e from its current position, and relink below ePrev
                    _mesh.Splice(e._Oprev, e);
                    _mesh.Splice(ePrev._Oprev, e);
                }
                // Compute the winding number and "inside" flag for the new regions
                reg._windingNumber = regPrev._windingNumber - e._winding;
                reg._inside = Geom.IsWindingInside(_windingRule, reg._windingNumber);

                // Check for two outgoing edges with same slope -- process these
                // before any intersection tests (see example in tessComputeInterior).
                regPrev._dirty = true;
                if (!firstTime && CheckForRightSplice(regPrev))
                {
                    Geom.AddWinding(e, ePrev);
                    DeleteRegion(regPrev);
                    _mesh.Delete(ePrev);
                }
                firstTime = false;
                regPrev = reg;
                ePrev = e;
            }
            regPrev._dirty = true;
            Debug.Assert(regPrev._windingNumber - e._winding == reg._windingNumber);

            if (cleanUp)
            {
                // Check for intersections between newly adjacent edges.
                WalkDirtyRegions(regPrev);
            }
        }
Example #48
0
        /// <summary>
        /// We are given a vertex with one or more left-going edges.  All affected
        /// edges should be in the edge dictionary.  Starting at regFirst->eUp,
        /// we walk down deleting all regions where both edges have the same
        /// origin vOrg.  At the same time we copy the "inside" flag from the
        /// active region to the face, since at this point each face will belong
        /// to at most one region (this was not necessarily true until this point
        /// in the sweep).  The walk stops at the region above regLast; if regLast
        /// is null we walk as far as possible.  At the same time we relink the
        /// mesh if necessary, so that the ordering of edges around vOrg is the
        /// same as in the dictionary.
        /// </summary>
        private MeshUtils.Edge FinishLeftRegions(ActiveRegion regFirst, ActiveRegion regLast)
        {
            var regPrev = regFirst;
            var ePrev = regFirst._eUp;

            while (regPrev != regLast)
            {
                regPrev._fixUpperEdge = false;	// placement was OK
                var reg = RegionBelow(regPrev);
                var e = reg._eUp;
                if (e._Org != ePrev._Org)
                {
                    if (!reg._fixUpperEdge)
                    {
                        // Remove the last left-going edge.  Even though there are no further
                        // edges in the dictionary with this origin, there may be further
                        // such edges in the mesh (if we are adding left edges to a vertex
                        // that has already been processed).  Thus it is important to call
                        // FinishRegion rather than just DeleteRegion.
                        FinishRegion(regPrev);
                        break;
                    }
                    // If the edge below was a temporary edge introduced by
                    // ConnectRightVertex, now is the time to fix it.
                    e = _mesh.Connect(ePrev._Lprev, e._Sym);
                    FixUpperEdge(reg, e);
                }

                // Relink edges so that ePrev.Onext == e
                if (ePrev._Onext != e)
                {
                    _mesh.Splice(e._Oprev, e);
                    _mesh.Splice(ePrev, e);
                }
                FinishRegion(regPrev); // may change reg.eUp
                ePrev = reg._eUp;
                regPrev = reg;
            }

            return ePrev;
        }
Example #49
0
        /// <summary>
        /// Make the sentinel coordinates big enough that they will never be
        /// merged with real input features.
        /// 
        /// We add two sentinel edges above and below all other edges,
        /// to avoid special cases at the top and bottom.
        /// </summary>
        private void AddSentinel(float smin, float smax, float t)
        {
            var e = _mesh.MakeEdge();
            e._Org._s = smax;
            e._Org._t = t;
            e._Dst._s = smin;
            e._Dst._t = t;
            _event = e._Dst; // initialize it

            var reg = new ActiveRegion();
            reg._eUp = e;
            reg._windingNumber = 0;
            reg._inside = false;
            reg._fixUpperEdge = false;
            reg._sentinel = true;
            reg._dirty = false;
            reg._nodeUp = _dict.Insert(reg);
        }
Example #50
0
 /// <summary>
 /// Replace an upper edge which needs fixing (see ConnectRightVertex).
 /// </summary>
 private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge)
 {
     Debug.Assert(reg._fixUpperEdge);
     _mesh.Delete(reg._eUp);
     reg._fixUpperEdge = false;
     reg._eUp = newEdge;
     newEdge._activeRegion = reg;
 }
Example #51
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;
        }
Example #52
0
 private ActiveRegion RegionBelow(ActiveRegion reg)
 {
     return reg._nodeUp._prev._key;
 }
Example #53
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)
        {
            var regLo = RegionBelow(regUp);
            var eUp = regUp._eUp;
            var 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;
                var e = _mesh.SplitEdge(eUp);
                _mesh.Splice(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;
                var e = _mesh.SplitEdge(eLo);
                _mesh.Splice(eUp._Lnext, eLo._Sym);
                e._Rface._inside = regUp._inside;
            }
            return true;
        }