public Voronoi(List<Point> points, List<int> colors, Rectangle plotBounds) { _sitesIndexedByLocation = new Dictionary<Point, Site>(); _triangles = new List<Triangle>(); Edges = new List<Edge>(); PlotBounds = plotBounds; AddSites(points, colors); FortunesAlgorithm(); }
public void RegionPrepare(Rectangle clippingBounds) { if (EdgeReordered) { return; } ReorderEdges(); _region = ClipToBounds(clippingBounds); if (new Polygon(_region).Winding == WindingDirection.Clockwise) { _region.Reverse(); } }
private void MainForm_Load(object sender, EventArgs e) { var rand = new Random(0); var points = new List<Point>(); var r = ClientRectangle; for (int i = 0; i < 100; i++) { points.Add(new Point(rand.Next(r.Width), rand.Next(r.Height))); } var rect = new Rectangle(r.X, r.Y, r.Width, r.Height); _voronoi = new Voronoi(points, null, rect); Invalidate(); }
public static Sides Check(Point p, Rectangle bounds) { var value = Sides.None; if (Math.Abs(p.X - bounds.Left) < float.Epsilon) { value |= Sides.Left; } if (Math.Abs(p.X - bounds.Right) < float.Epsilon) { value|=Sides.Right; } if (Math.Abs(p.Y - bounds.Top) < float.Epsilon) { value |= Sides.Top; } if (Math.Abs(p.Y - bounds.Bottom) < float.Epsilon) { value |= Sides.Bottom; } return value; }
public void ClipVertices(Rectangle bounds) { var xmin = bounds.X; var ymin = bounds.Y; var xmax = bounds.Right; var ymax = bounds.Bottom; Vertex vertex0, vertex1; float x0, x1, y0, y1; if (Math.Abs(A - 1.0) < Double.Epsilon && B >= 0.0) { vertex0 = RightVertex; vertex1 = LeftVertex; } else { vertex0 = LeftVertex; vertex1 = RightVertex; } if (Math.Abs(A - 1.0) < Double.Epsilon) { y0 = ymin; if (vertex0 != null && vertex0.Y > ymin) y0 = vertex0.Y; if (y0 > ymax) return; x0 = C - B * y0; y1 = ymax; if (vertex1 != null && vertex1.Y < ymax) y1 = vertex1.Y; if (y1 < ymin) return; x1 = C - B * y1; if ((x0 > xmax && x1 > xmax) || (x0 < xmin && x1 < xmin)) return; if (x0 > xmax) { x0 = xmax; y0 = (C - x0) / B; } else if (x0 < xmin) { x0 = xmin; y0 = (C - x0) / B; } if (x1 > xmax) { x1 = xmax; y1 = (C - x1) / B; } else if (x1 < xmin) { x1 = xmin; y1 = (C - x1) / B; } } else { x0 = xmin; if (vertex0 != null && vertex0.X > xmin) x0 = vertex0.X; if (x0 > xmax) return; y0 = C - A * x0; x1 = xmax; if (vertex1 != null && vertex1.X < xmax) x1 = vertex1.X; if (x1 < xmin) return; y1 = C - A * x1; if ((y0 > ymax && y1 > ymax) || (y0 < ymin && y1 < ymin)) return; if (y0 > ymax) { y0 = ymax; x0 = (C - y0) / A; } else if (y0 < ymin) { y0 = ymin; x0 = (C - y0) / A; } if (y1 > ymax) { y1 = ymax; x1 = (C - y1) / A; } else if (y1 < ymin) { y1 = ymin; x1 = (C - y1) / A; } } if (vertex0 == LeftVertex) { ClippedEnds[LR.Side.Left] = new Point(x0, y0); ClippedEnds[LR.Side.Right] = new Point(x1, y1); } else { ClippedEnds[LR.Side.Right] = new Point(x0, y0); ClippedEnds[LR.Side.Left] = new Point(x1, y1); } Console.WriteLine("cl {0} {1} {2} {3}", x0, y0, x1, y1); }
public List<Point> Region(Rectangle clippingBounds) { if (Edges.Count == 0) { return new List<Point>(); } RegionPrepare(clippingBounds); return _region; }
private void Connect(List<Point> points, int j, Rectangle bounds, bool closingUp = false) { var rightPoint = points.Last(); var newEdge = Edges[j]; var newOrientation = EdgeOrientations[j]; // the point that must be connected to rightPoint: var newPoint = newEdge.ClippedEnds[newOrientation]; if (!CloseEnough(rightPoint, newPoint)) { // The points do not coincide, so they must have been clipped at the bounds; // see if they are on the same border of the bounds: if (rightPoint.X != newPoint.X && rightPoint.Y != newPoint.Y) { // They are on different borders of the bounds; // insert one or two corners of bounds as needed to hook them up: // (NOTE this will not be correct if the region should take up more than // half of the bounds rect, for then we will have gone the wrong way // around the bounds and included the smaller part rather than the larger) var rightCheck = BoundsCheck.Check(rightPoint, bounds); var newCheck = BoundsCheck.Check(newPoint, bounds); float px, py; // TODO: refactor origin lib copypasta if (rightCheck.HasFlag(BoundsCheck.Sides.Right)) { px = bounds.Right; if (newCheck.HasFlag(BoundsCheck.Sides.Bottom)) { py = bounds.Bottom; points.Add(new Point(px,py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Top)) { py = bounds.Top; points.Add(new Point(px,py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Left)) { if (rightPoint.Y - bounds.Y + newPoint.Y - bounds.Y < bounds.Height) { py = bounds.Top; } else { py = bounds.Bottom; } points.Add(new Point(px,py)); points.Add(new Point(bounds.Left, py)); } } else if (rightCheck.HasFlag(BoundsCheck.Sides.Left)) { px = bounds.Left; if (newCheck.HasFlag(BoundsCheck.Sides.Bottom)) { py = bounds.Bottom; points.Add(new Point(px, py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Top)) { py = bounds.Top; points.Add(new Point(px, py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Right)) { if (rightPoint.Y - bounds.Y + newPoint.Y - bounds.Y < bounds.Height) { py = bounds.Top; } else { py = bounds.Bottom; } points.Add(new Point(px, py)); points.Add(new Point(bounds.Right, py)); } } else if (rightCheck.HasFlag(BoundsCheck.Sides.Top)) { py = bounds.Top; if (newCheck.HasFlag(BoundsCheck.Sides.Right)) { px = bounds.Right; points.Add(new Point(px, py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Left)) { px = bounds.Left; points.Add(new Point(px, py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Bottom)) { if (rightPoint.X - bounds.X + newPoint.X - bounds.Y < bounds.Width) { px = bounds.Left; } else { px = bounds.Right; } points.Add(new Point(px, py)); points.Add(new Point(bounds.Left, bounds.Bottom)); } } else if (rightCheck.HasFlag(BoundsCheck.Sides.Bottom)) { py = bounds.Bottom; if (newCheck.HasFlag(BoundsCheck.Sides.Right)) { px = bounds.Right; points.Add(new Point(px, py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Left)) { px = bounds.Left; points.Add(new Point(px, py)); } else if (newCheck.HasFlag(BoundsCheck.Sides.Bottom)) { if (rightPoint.X - bounds.X + newPoint.X - bounds.Y < bounds.Width) { px = bounds.Left; } else { px = bounds.Right; } points.Add(new Point(px, py)); points.Add(new Point(bounds.Left, bounds.Top)); } } } if (closingUp) { // newEdge's ends have already been added return; } points.Add(newPoint); } var newRightPoint = newEdge.ClippedEnds[LR.Other(newOrientation)]; if (!CloseEnough(points.First(), newRightPoint)) { points.Add(newRightPoint); } }
private List<Point> ClipToBounds(Rectangle bounds) { var points = new List<Point>(); int i = -1; for (int j = 0; j < Edges.Count; j++) { var edge = Edges[j]; if (edge == null || !edge.Visible) { continue; } if (i >= 0) { Connect(points, j, bounds); } else { i = j; var orientation = EdgeOrientations[j]; points.Add(edge.ClippedEnds[orientation]); points.Add(edge.ClippedEnds[LR.Other(orientation)]); } } if (i >= 0) { Connect(points, i, bounds, true); } return points; }
public void RegionsPrepare(Rectangle bounds) { foreach (var site in _sites) { site.RegionPrepare(bounds); } }
public List<List<Point>> Regions(Rectangle bounds) { return _sites.Select(site => site.Region(bounds)).ToList(); }