예제 #1
0
        /// <summary>
        /// Is the point to the left of the edge?
        /// </summary>
        bool ToTheLeft(int pi, int li0, int li1)
        {
            if (li0 == -2)
            {
                return(Higher(li1, pi));
            }
             else if (li0 == -1)
            {
                return(Higher(pi, li1));
            }
             else if (li1 == -2)
            {
                return(Higher(pi, li0));
            }
            else if (li1 == -1)
            {
                return(Higher(li0, pi));
            }
            else
            {
                Debug.Assert(li0 >= 0);
                Debug.Assert(li1 >= 0);

                return(Geom.ToTheLeft(verts[pi], verts[li0], verts[li1]));
            }
        }
예제 #2
0
        /// <summary>
        /// Is the edge legal, or does it need to be flipped?
        /// </summary>
        bool LegalEdge(int k, int l, int i, int j)
        {
            Debug.Assert(k != highest && k >= 0);

            var lMagic = l < 0;
            var iMagic = i < 0;
            var jMagic = j < 0;

            Debug.Assert(!(iMagic && jMagic));

            if (lMagic)
            {
                return(true);
            }
            else if (iMagic)
            {
                Debug.Assert(!jMagic);

                var p  = verts[l];
                var l0 = verts[k];
                var l1 = verts[j];

                return(Geom.ToTheLeft(p, l0, l1));
            }
            else if (jMagic)
            {
                Debug.Assert(!iMagic);

                var p  = verts[l];
                var l0 = verts[k];
                var l1 = verts[i];

                return(!Geom.ToTheLeft(p, l0, l1));
            }
             else {
                Debug.Assert(k >= 0 && l >= 0 && i >= 0 && j >= 0);

                var p  = verts[l];
                var c0 = verts[k];
                var c1 = verts[i];
                var c2 = verts[j];

                Debug.Assert(Geom.ToTheLeft(c2, c0, c1));
                Debug.Assert(Geom.ToTheLeft(c2, c1, p));

                return(!Geom.InsideCircumcircle(p, c0, c1, c2));
            }
        }
예제 #3
0
        /// <summary>
        /// Non-allocating version of CalculateDiagram.
        ///
        /// I guess it's not strictly true that it generates NO garbage, because
        /// it might if it has to resize internal buffers, but all buffers are
        /// reused from invocation to invocation.
        /// </summary>
        public void CalculateDiagram(IList <Vector2> inputVertices, ref VoronoiDiagram result)
        {
            // TODO: special case for 1 points
            // TODO: special case for 2 points
            // TODO: special case for 3 points
            // TODO: special case for collinear points
            if (inputVertices.Count < 3)
            {
                throw new NotImplementedException("Not implemented for < 3 vertices");
            }

            if (result == null)
            {
                result = new VoronoiDiagram();
            }

            var trig = result.Triangulation;

            result.Clear();

            Profiler.BeginSample("Delaunay triangulation");
            delCalc.CalculateTriangulation(inputVertices, ref trig);
            Profiler.EndSample();

            pts.Clear();

            var verts   = trig.Vertices;
            var tris    = trig.Triangles;
            var centers = result.Vertices;
            var edges   = result.Edges;


            if (tris.Count > pts.Capacity)
            {
                   {
                    pts.Capacity = tris.Count;
                }
            }
            if (tris.Count > edges.Capacity)
            {
                edges.Capacity = tris.Count;
            }


            for (int ti = 0; ti < tris.Count; ti += 3)
            {
                var p0 = verts[tris[ti]];
                var p1 = verts[tris[ti + 1]];
                var p2 = verts[tris[ti + 2]];

                // Triangle is in CCW order
                Debug.Assert(Geom.ToTheLeft(p2, p0, p1));

                centers.Add(Geom.CircumcircleCenter(p0, p1, p2));
            }


            for (int ti = 0; ti < tris.Count; ti += 3)
            {
                pts.Add(new PointTriangle(tris[ti], ti));
                pts.Add(new PointTriangle(tris[ti + 1], ti));
                pts.Add(new PointTriangle(tris[ti + 2], ti));
            }

            cmp.tris  = tris;
            cmp.verts = verts;

            Profiler.BeginSample("Sorting");
            pts.Sort(cmp);
            Profiler.EndSample();

            // The comparer lives on between runs of the algorithm, so clear the
            // reference to the arrays so that the reference is lost. It may be
            // the case that the calculator lives on much longer than the
            // results, and not clearing these would keep the results alive,
            // leaking memory.
            cmp.tris  = null;
            cmp.verts = null;

            for (int i = 0; i < pts.Count; i++)
            {
                result.FirstEdgeBySite.Add(edges.Count);

                var start = i;
                var end   = -1;

                for (int j = i + 1; j < pts.Count; j++)
                {
                    if (pts[i].Point != pts[j].Point)
                    {
                        end = j - 1;
                        break;
                    }
                }

                if (end == -1)
                {
                    end = pts.Count - 1;
                }

                i = end;

                var count = end - start;

                Debug.Assert(count >= 0);

                for (int ptiCurr = start; ptiCurr <= end; ptiCurr++)
                {
                    bool isEdge;

                    var ptiNext = ptiCurr + 1;

                    if (ptiNext > end)
                    {
                        ptiNext = start;
                    }

                    var ptCurr = pts[ptiCurr];
                    var ptNext = pts[ptiNext];

                    var tiCurr = ptCurr.Triangle;
                    var tiNext = ptNext.Triangle;

                    var p0 = verts[ptCurr.Point];

                    var v2nan = new Vector2(float.NaN, float.NaN);

                    if (count == 0)
                    {
                        isEdge = true;
                    }
                    else if (count == 1)
                    {
                        var cCurr = Geom.TriangleCentroid(verts[tris[tiCurr]], verts[tris[tiCurr + 1]], verts[tris[tiCurr + 2]]);
                        var cNext = Geom.TriangleCentroid(verts[tris[tiNext]], verts[tris[tiNext + 1]], verts[tris[tiNext + 2]]);

                        isEdge = Geom.ToTheLeft(cCurr, p0, cNext);
                    }
                    else
                    {
                        isEdge = !SharesEdge(tris, tiCurr, tiNext);
                    }

                    if (isEdge)
                    {
                        Vector2 v0, v1;

                        if (ptCurr.Point == tris[tiCurr])
                        {
                            v0 = verts[tris[tiCurr + 2]] - verts[tris[tiCurr + 0]];
                        }
                        else if (ptCurr.Point == tris[tiCurr + 1])
                        {
                            v0 = verts[tris[tiCurr + 0]] - verts[tris[tiCurr + 1]];
                        }
                        else
                        {
                            Debug.Assert(ptCurr.Point == tris[tiCurr + 2]);
                            v0 = verts[tris[tiCurr + 1]] - verts[tris[tiCurr + 2]];
                        }

                        if (ptNext.Point == tris[tiNext])
                        {
                            v1 = verts[tris[tiNext + 0]] - verts[tris[tiNext + 1]];
                        }
                        else if (ptNext.Point == tris[tiNext + 1])
                        {
                            v1 = verts[tris[tiNext + 1]] - verts[tris[tiNext + 2]];
                        }
                        else
                        {
                            Debug.Assert(ptNext.Point == tris[tiNext + 2]);
                            v1 = verts[tris[tiNext + 2]] - verts[tris[tiNext + 0]];
                        }

                        edges.Add(new VoronoiDiagram.Edge(
                                      VoronoiDiagram.EdgeType.RayCCW,
                                      ptCurr.Point,
                                      tiCurr / 3,
                                      -1,
                                      Geom.RotateRightAngle(v0)
                                      ));

                        edges.Add(new VoronoiDiagram.Edge(
                                      VoronoiDiagram.EdgeType.RayCW,
                                      ptCurr.Point,
                                      tiNext / 3,
                                      -1,
                                      Geom.RotateRightAngle(v1)
                                      ));
                    }
                    else
                    {
                        if (!Geom.AreCoincident(centers[tiCurr / 3], centers[tiNext / 3]))
                        {
                            edges.Add(new VoronoiDiagram.Edge(
                                          VoronoiDiagram.EdgeType.Segment,
                                          ptCurr.Point,
                                          tiCurr / 3,
                                          tiNext / 3,
                                          v2nan
                                          ));
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Clip site of voronoi diagram using polygon (must be convex),
        /// returning the clipped vertices in clipped list. Modifies neither
        /// polygon nor diagram, so can be run in parallel for several sites at
        /// once.
        /// </summary>
        public void ClipSite(VoronoiDiagram diag, IList <Vector2> polygon, int site, ref List <Vector2> clipped)
        {
            pointsIn.Clear();

            pointsIn.AddRange(polygon);

            int firstEdge, lastEdge;

            if (site == diag.Sites.Count - 1)
            {
                firstEdge = diag.FirstEdgeBySite[site];
                lastEdge  = diag.Edges.Count - 1;
            }
            else
            {
                firstEdge = diag.FirstEdgeBySite[site];
                lastEdge  = diag.FirstEdgeBySite[site + 1] - 1;
            }

            for (int ei = firstEdge; ei <= lastEdge; ei++)
            {
                pointsOut.Clear();

                var edge = diag.Edges[ei];

                Vector2 lp, ld;

                if (edge.Type == VoronoiDiagram.EdgeType.RayCCW || edge.Type == VoronoiDiagram.EdgeType.RayCW)
                {
                    lp = diag.Vertices[edge.Vert0];
                    ld = edge.Direction;

                    if (edge.Type == VoronoiDiagram.EdgeType.RayCW)
                    {
                        ld *= -1;
                    }
                }
                else if (edge.Type == VoronoiDiagram.EdgeType.Segment)
                {
                    var lp0 = diag.Vertices[edge.Vert0];
                    var lp1 = diag.Vertices[edge.Vert1];

                    lp = lp0;
                    ld = lp1 - lp0;
                }
                else if (edge.Type == VoronoiDiagram.EdgeType.Line)
                {
                    throw new NotSupportedException("Haven't implemented voronoi halfplanes yet");
                }
                else
                {
                    Debug.Assert(false);
                    return;
                }

                for (int pi0 = 0; pi0 < pointsIn.Count; pi0++)
                {
                    var pi1 = pi0 == pointsIn.Count - 1 ? 0 : pi0 + 1;

                    var p0 = pointsIn[pi0];
                    var p1 = pointsIn[pi1];

                    var p0Inside = Geom.ToTheLeft(p0, lp, lp + ld);
                    var p1Inside = Geom.ToTheLeft(p1, lp, lp + ld);

                    if (p0Inside && p1Inside)
                    {
                        pointsOut.Add(p1);
                    }
                    else if (!p0Inside && !p1Inside)
                    {
                        // Do nothing, both are outside
                    }
                    else
                    {
                        var intersection = Geom.LineLineIntersection(lp, ld.normalized, p0, (p1 - p0).normalized);

                        if (p0Inside)
                        {
                            pointsOut.Add(intersection);
                        }
                        else if (p1Inside)
                        {
                            pointsOut.Add(intersection);
                            pointsOut.Add(p1);
                        }
                        else
                        {
                            Debug.Assert(false);
                        }
                    }
                }

                var tmp = pointsIn;
                pointsIn  = pointsOut;
                pointsOut = tmp;
            }

            if (clipped == null)
            {
                clipped = new List <Vector2>();
            }
            else
            {
                clipped.Clear();
            }

            clipped.AddRange(pointsIn);

            /*
             * pointsIn.Clear();
             *
             * pointsIn.AddRange(polygon);
             *
             * int firstEdge, lastEdge;
             *
             * if (site == diag.Sites.Count - 1)
             * {
             *  firstEdge = diag.FirstEdgeBySite[site];
             *  lastEdge = diag.Edges.Count - 1;
             * }
             * else
             * {
             *  firstEdge = diag.FirstEdgeBySite[site];
             *  lastEdge = diag.FirstEdgeBySite[site + 1] - 1;
             * }
             *
             * for (int ei = firstEdge; ei <= lastEdge; ei++)
             * {
             *  pointsOut.Clear();
             *
             *  var edge = diag.Edges[ei];
             *
             *  Vector2 lp, ld;
             *
             *  if (edge.Type == VoronoiDiagram.EdgeType.RayCCW || edge.Type == VoronoiDiagram.EdgeType.RayCW)
             *  {
             *      lp = diag.Vertices[edge.Vert0];
             *      ld = edge.Direction;
             *
             *      if (edge.Type == VoronoiDiagram.EdgeType.RayCW)
             *      {
             *          ld *= -1;
             *      }
             *  }
             *  else if (edge.Type == VoronoiDiagram.EdgeType.Segment)
             *  {
             *      var lp0 = diag.Vertices[edge.Vert0];
             *      var lp1 = diag.Vertices[edge.Vert1];
             *
             *      lp = lp0;
             *      ld = lp1 - lp0;
             *  }
             *  else if (edge.Type == VoronoiDiagram.EdgeType.Line)
             *  {
             *      throw new NotSupportedException("Haven't implemented voronoi halfplanes yet");
             *  }
             *  else
             *  {
             *      Debug.Assert(false);
             *      return;
             *  }
             *
             *  for (int pi0 = 0; pi0 < pointsIn.Count; pi0++)
             *  {
             *      var pi1 = pi0 == pointsIn.Count - 1 ? 0 : pi0 + 1;
             *
             *      var p0 = pointsIn[pi0];
             *      var p1 = pointsIn[pi1];
             *
             *      var p0Inside = Geom.ToTheLeft(p0, lp, lp + ld);
             *      var p1Inside = Geom.ToTheLeft(p1, lp, lp + ld);
             *
             *      if (p0Inside && p1Inside)
             *      {
             *          pointsOut.Add(p1);
             *      }
             *      else if (!p0Inside && !p1Inside)
             *      {
             *          // Do nothing, both are outside
             *      }
             *      else
             *      {
             *          var intersection = Geom.LineLineIntersection(lp, ld.normalized, p0, (p1 - p0).normalized);
             *
             *          if (p0Inside)
             *          {
             *              pointsOut.Add(intersection);
             *          }
             *          else if (p1Inside)
             *          {
             *              pointsOut.Add(intersection);
             *              pointsOut.Add(p1);
             *          }
             *          else
             *          {
             *              Debug.Assert(false);
             *          }
             *      }
             *  }
             *
             *  if(pointsOut.Count == 0)
             *  {
             *      //pointsOut = new List<Vector2>(pointsIn);
             *  }
             *
             *  var tmp = pointsIn;
             *  pointsIn = pointsOut;
             *  pointsOut = tmp;
             * }
             *
             * if (clipped == null)
             * {
             *  clipped = new List<Vector2>();
             * }
             * else
             * {
             *  clipped.Clear();
             * }
             *
             * clipped.AddRange(pointsIn);
             *
             */
        }