/// <summary> /// Changes the envelope extent by the specified amount relative /// to it's current extent in that dimension (preserving the aspect ratio). /// So Zoom(10) on a 100 unit wide envelope creates a 110 unit wide envlope, /// while Zoom(-10) on a 100 unit wide envelope creates a 90 unit wide envelope. /// Zoom(-100) on an envelope makes it 100% smaller, or effectively a point. /// Tragically, a point cannot be "zoomed" back in, so a check should be used /// to ensure that the envelope is not currently a point before attempting /// to zoom in. /// </summary> /// <param name="self">The IEnvelope that this zoom method modifies</param> /// <param name="percent"> /// Positive 50 makes the envelope 50% larger /// Negative 50 makes the envelope 50% smaller /// </param> /// <example> /// perCent = -50 compact the envelope a 50% (make it smaller). /// perCent = 200 enlarge envelope by 2. /// </example> public static void Zoom(this IEnvelope self, double percent) { if (self == null) { return; } if (self.IsNull) { return; } double ratio = percent / 100; Coordinate size = new Coordinate(); for (int i = 0; i < self.NumOrdinates; i++) { double oldSize = self.Maximum[i] - self.Minimum[i]; size[i] = oldSize + ratio * oldSize; } SetCenter(self, self.Center(), size); }
/// <summary> /// The original algorithm simply allows edges that have one defined point and /// another "NAN" point. Simply excluding the not a number coordinates fails /// to preserve the known direction of the ray. We only need to extend this /// long enough to encounter the bounding box, not infinity. /// </summary> /// <param name="graph">The VoronoiGraph with the edge list.</param> /// <param name="bounds">The polygon bounding the datapoints.</param> private static void HandleBoundaries(VoronoiGraph graph, IEnvelope bounds) { List <ILineString> boundSegments = new List <ILineString>(); List <VoronoiEdge> unboundEdges = new List <VoronoiEdge>(); // Identify bound edges for intersection testing foreach (VoronoiEdge edge in graph.Edges) { if (edge.VVertexA.ContainsNan() || edge.VVertexB.ContainsNan()) { unboundEdges.Add(edge); continue; } boundSegments.Add( new LineString(new List <Coordinate> { edge.VVertexA.ToCoordinate(), edge.VVertexB.ToCoordinate() })); } // calculate a length to extend a ray to look for intersections IEnvelope env = bounds; double h = env.Height; double w = env.Width; double len = Math.Sqrt((w * w) + (h * h)); // len is now long enough to pass entirely through the dataset no matter where it starts foreach (VoronoiEdge edge in unboundEdges) { // the unbound line passes thorugh start Coordinate start = (edge.VVertexB.ContainsNan()) ? edge.VVertexA.ToCoordinate() : edge.VVertexB.ToCoordinate(); // the unbound line should have a direction normal to the line joining the left and right source points double dx = edge.LeftData.X - edge.RightData.X; double dy = edge.LeftData.Y - edge.RightData.Y; double l = Math.Sqrt((dx * dx) + (dy * dy)); // the slope of the bisector between left and right double sx = -dy / l; double sy = dx / l; Coordinate center = bounds.Center(); if ((start.X > center.X && start.Y > center.Y) || (start.X < center.X && start.Y < center.Y)) { sx = dy / l; sy = -dx / l; } Coordinate end1 = new Coordinate(start.X + (len * sx), start.Y + (len * sy)); Coordinate end2 = new Coordinate(start.X - (sx * len), start.Y - (sy * len)); Coordinate end = (end1.Distance(center) < end2.Distance(center)) ? end2 : end1; if (bounds.Contains(end)) { end = new Coordinate(start.X - (sx * len), start.Y - (sy * len)); } if (edge.VVertexA.ContainsNan()) { edge.VVertexA = new Vector2(end.ToArray()); } else { edge.VVertexB = new Vector2(end.ToArray()); } } }
/// <summary> /// The original algorithm simply allows edges that have one defined point and /// another "NAN" point. Simply excluding the not a number coordinates fails /// to preserve the known direction of the ray. We only need to extend this /// long enough to encounter the bounding box, not infinity. /// </summary> /// <param name="graph">The VoronoiGraph with the edge list</param> /// <param name="bounds">The polygon bounding the datapoints</param> private static void HandleBoundaries(VoronoiGraph graph, IEnvelope bounds) { List<ILineString> boundSegments = new List<ILineString>(); List<VoronoiEdge> unboundEdges = new List<VoronoiEdge>(); // Identify bound edges for intersection testing foreach (VoronoiEdge edge in graph.Edges) { if(edge.VVertexA.ContainsNan() || edge.VVertexB.ContainsNan()) { unboundEdges.Add(edge); continue; } boundSegments.Add(new LineString(new List<Coordinate>{edge.VVertexA.ToCoordinate(), edge.VVertexB.ToCoordinate()})); } // calculate a length to extend a ray to look for intersections IEnvelope env = bounds; double h = env.Height; double w = env.Width; double len = Math.Sqrt(w * w + h * h); // len is now long enough to pass entirely through the dataset no matter where it starts foreach (VoronoiEdge edge in unboundEdges) { // the unbound line passes thorugh start Coordinate start = (edge.VVertexB.ContainsNan()) ? edge.VVertexA.ToCoordinate() : edge.VVertexB.ToCoordinate(); // the unbound line should have a direction normal to the line joining the left and right source points double dx = edge.LeftData.X - edge.RightData.X; double dy = edge.LeftData.Y - edge.RightData.Y; double l = Math.Sqrt(dx*dx + dy*dy); // the slope of the bisector between left and right double sx = -dy/l; double sy = dx/l; Coordinate center = bounds.Center(); if((start.X > center.X && start.Y > center.Y) || (start.X < center.X && start.Y < center.Y)) { sx = dy/l; sy = -dx/l; } Coordinate end1 = new Coordinate(start.X + len*sx, start.Y + len * sy); Coordinate end2 = new Coordinate(start.X - sx * len, start.Y - sy * len); Coordinate end = (end1.Distance(center) < end2.Distance(center)) ? end2 : end1; if(bounds.Contains(end)) { end = new Coordinate(start.X - sx * len, start.Y - sy * len); } if (edge.VVertexA.ContainsNan()) { edge.VVertexA = new Vector2(end.ToArray()); } else { edge.VVertexB = new Vector2(end.ToArray()); } } }