//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Clip this voronoi cell with a passed in bounding box. /// </summary> /// <remarks> /// <para> /// I'm trying to handle all the exceptional cases. There is one incredibly exceptional /// case which I'm ignoring. That is the case where there are two vertices at infinity which are /// at such nearly opposite directions without being completely collinear that we can't push /// their points at infinity out far enough to encompass the rest of the box within the range of /// a double. If this is important to you, then see the comments below, but it's hard to imagine /// it ever arising. /// </para> /// <para> /// Editorial comment - The annoying thing about all of this is that, like in so much of /// computational geometry, the rarer and less significant the exceptional cases are, the more /// difficult they are to handle. It's both a blessing and a curse - it means that the normal /// cases are generally faster, but it also makes it difficult to get excited about slogging /// through the tedious details of situations that will probably never arise in practice. Still, /// in order to keep our noses clean, we press on regardless. /// </para> /// Darrellp, 2/26/2011. /// </remarks> /// <param name="ptUL"> The upper left point of the box. </param> /// <param name="ptLR"> The lower right point of the box. </param> /// <returns> An enumerable of real points representing the voronoi cell clipped to the box. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// public IEnumerable <Vector> BoxVertices(Vector ptUL, Vector ptLR) { // If no edges, then it's just the entire box if (!Edges.Any()) { foreach (var pt in BoxPoints(ptUL, ptLR)) { yield return(pt); } yield break; } var ptsBox = BoxPoints(ptUL, ptLR); var fFound = FCheckEasy(out var ptsToBeClipped); if (!fFound) { fFound = FCheckParallelLines(ptsBox, out ptsToBeClipped); } if (!fFound) { fFound = FCheckDoublyInfinite(ptsBox, out ptsToBeClipped); } if (!fFound) { ptsToBeClipped = RealVertices(CalcRayLength(ptsBox)); } foreach (var pt in ConvexPolyIntersection.FindIntersection(ptsToBeClipped, ptsBox)) { yield return(pt); } }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Lloyd relaxation. </summary> /// <remarks> /// This routine performs a single Lloyd relaxation on a fortune winged edge structure. That /// means the points for the voronoi diagram are moved to the centroid of their cell and the /// voronoi algorithm is run again. In order to do this we have to be able to clip the infinite /// polygons or else their centroid is undefined so a clipping polygon is passed in to clip to. /// Also, as usual, we need a ray length to know how far to extend any infinite rays. This /// should be a large enough value that all rays extend to the side of the clipping polygon. /// Currently, my clipping algorithm only works for convex polygons so the clipping polygon /// passed in must be convex and the points must be in counterclockwise order. This works fine /// for clipping to rectangles or voronoi cells which are the two cases I'm currently interested /// in. /// Darrellp, 2/24/2011. /// </remarks> /// <param name="we"> WingedEdge structure we'll relax. </param> /// <param name="rayLength"> Length of the rays we extend. </param> /// <param name="polyClip"> The polygon to clip to. </param> /// <param name="strength"> /// Strength of relaxation. The default is 1 for "standard" /// relaxation. 0 has no effect. Negative values can make it more /// "spikey". /// </param> /// <returns> The relaxed Winged Edge structure. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// public static WE LloydRelax(WE we, double rayLength, IEnumerable <Vector> polyClip, double strength = 1) { // Locals var ptsRelaxed = new List <Vector>(); // For each polygon in the Winged edge foreach (var poly in we.LstPolygons) { // Skip the polygon at infinity if (poly.FAtInfinity) { continue; } // Get the polygon vertices in CCW order var ptsPoly = poly.RealVertices(rayLength); // Clip them to our clipping polygon // ReSharper disable PossibleMultipleEnumeration var ptsCentroid = ConvexPolyIntersection.FindIntersection(polyClip, ptsPoly); // If this polygon is outside our bounding box, just skip it if (!ptsCentroid.Any()) { continue; } // Locals for the centroid calculations var cpts = 0; var ptCentroid = new Vector(); // For each point in the clipped polygon foreach (var pt in ptsCentroid) { // Add it in to the centroid accumulator ptCentroid += pt; cpts++; } // ReSharper restore PossibleMultipleEnumeration // Determine the centroid var ctrd = new Vector(ptCentroid.X / cpts, ptCentroid.Y / cpts); // If strength is the default -1, just take our centroid // ReSharper disable once CompareOfFloatsByEqualityOperator if (strength == 1) { // Calculate the new centroid and add it to our list of points ptsRelaxed.Add(new Vector(ptCentroid.X / cpts, ptCentroid.Y / cpts)); } else { // Move the original voronoi point nearer or further from the centroid ptsRelaxed.Add(poly.VoronoiPoint + (poly.VoronoiPoint - ctrd) * -strength); } } // Return the voronoi diagram on all the centroids return(ComputeVoronoi(ptsRelaxed)); }