/// <summary> /// Determines if the Point is to the right of the HalfEdge. /// </summary> /// <param name="el">edge</param> /// <param name="p">point</param> /// <returns>1 if p is to the right of HalfEdge</returns> private static bool IsRightOf(HalfEdge el, Point2D p) { bool above; var e = el.Edge; var topsite = e.Reg[1]; var rightOfSite = p.X > topsite.X; if (rightOfSite && (el.Midpoint == 0)) { return(true); } if (!rightOfSite && (el.Midpoint == 1)) { return(false); } if (DoubleComparison.IsEqual(e.A, 1.0)) { var dyp = p.Y - topsite.Y; var dxp = p.X - topsite.X; var fast = false; if ((!rightOfSite && (e.B < 0.0)) || (rightOfSite && (e.B >= 0.0))) { above = dyp >= e.B * dxp; fast = above; } else { above = p.X + p.Y * e.B > e.C; if (e.B < 0.0) { above = !above; } if (!above) { fast = true; } } if (fast) { return(el.Midpoint == 0 ? above : !above); } var dxs = topsite.X - e.Reg[0].X; above = e.B * (dxp * dxp - dyp * dyp) < dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.B * e.B); if (e.B < 0.0) { above = !above; } } else { var yl = e.C - e.A * p.X; var t1 = p.Y - yl; var t2 = p.X - topsite.X; var t3 = yl - topsite.Y; above = t1 * t1 > t2 * t2 + t3 * t3; } return(el.Midpoint == 0 ? above : !above); }
// push the HalfEdge into the ordered linked list of vertices internal void PQinsert(HalfEdge he, Point2D v, double offset) { HalfEdge next; he.Vertex = v; he.YStar = v.Y + offset; var last = _hash[0]; _min = 0; while (((next = last.NextSite) != null) && ((he.YStar > next.YStar) || (DoubleComparison.IsEqual(he.YStar, next.YStar) && (v.X > next.Vertex.X)))) { last = next; } he.NextSite = last.NextSite; last.NextSite = he; _count++; }
/// <summary> /// Creates a new site where the HalfEdges intersect. /// </summary> /// <param name="el1">HalfEdge to intersect</param> /// <param name="el2">HalfEdge to intersect</param> /// <returns>new site/point at intersection</returns> private static Point2D Intersect(HalfEdge el1, HalfEdge el2) { Edge e; HalfEdge el; var e1 = el1.Edge; var e2 = el2.Edge; if ((e1 == null) || (e2 == null) || (e1.Reg[1] == e2.Reg[1])) { return(null); } var d = e1.A * e2.B - e1.B * e2.A; //This checks for the value being basically zero if ((-0.0000000001 < d) && (d < 0.0000000001)) { return(null); } var xint = (e1.C * e2.B - e2.C * e1.B) / d; var yint = (e2.C * e1.A - e1.C * e2.A) / d; if ((e1.Reg[1].Y < e2.Reg[1].Y) || (DoubleComparison.IsEqual(e1.Reg[1].Y, e2.Reg[1].Y) && (e1.Reg[1].X < e2.Reg[1].X))) { el = el1; e = e1; } else { el = el2; e = e2; } return(((xint >= e.Reg[1].X) && (el.Midpoint == 0)) || ((xint < e.Reg[1].X) && (el.Midpoint == 1)) ? null : new Point2D(xint, yint)); }
/// <summary> /// Starts fortune's algorithm. /// </summary> private void Voronoi() { Point2D newintstar = null; HalfEdge leftBound; var queue = new PriorityQueue(NumSites); _bottomsite = NextSite(); var list = new EdgeList(NumSites); var newsite = NextSite(); while (true) { Point2D bot; Point2D p; HalfEdge rbnd; HalfEdge bisector; Edge e; if (!queue.IsEmpty()) { newintstar = queue.Min(); } //if the lowest site has a smaller y value than the lowest vector intersection, process the site //otherwise process the vector intersection if ((newsite != null) && (queue.IsEmpty() || (newintstar == null) || (newsite.Y < newintstar.Y) || (DoubleComparison.IsEqual(newsite.Y, newintstar.Y) && (newsite.X < newintstar.X)))) { /* new site is smallest - this is a site event*/ leftBound = list.LeftBound(newsite); //get the first HalfEdge to the LEFT of the new site rbnd = leftBound.Right; //get the first HalfEdge to the RIGHT of the new site bot = Rightreg(leftBound); //if this HalfEdge has no edge, , bot = bottom site (whatever that is) e = Bisect(bot, newsite); //create a new edge that bisects bisector = new HalfEdge(e, 0); //create a new HalfEdge, setting its elPm field to 0 EdgeList.ElInsert(leftBound, bisector); //insert this new bisector edge between the left and right vectors in a linked list if ((p = Intersect(leftBound, bisector)) != null) //if the new bisector intersects with the left edge, remove the left edge's vertex, and put in the new one { queue.PQinsert(queue.Delete(leftBound), p, p.Distance(newsite)); } leftBound = bisector; bisector = new HalfEdge(e, 1); //create a new HalfEdge, setting its elPm field to 1 EdgeList.ElInsert(leftBound, bisector); //insert the new HE to the right of the original bisector earlier in the IF stmt if ((p = Intersect(bisector, rbnd)) != null) //if this new bisector intersects with the { queue.PQinsert(bisector, p, p.Distance(newsite)); //push the HE into the ordered linked list of vertices } newsite = NextSite(); } else if (!queue.IsEmpty()) { /* intersection is smallest - this is a vector event */ leftBound = queue.ExtractMin(); //pop the HalfEdge with the lowest vector off the ordered list of vectors var llbnd = leftBound.Left; rbnd = leftBound.Right; //get the HalfEdge to the right of the above HE var rrbnd = rbnd.Right; bot = Leftreg(leftBound); //get the Site to the left of the left HE which it bisects var top = Rightreg(rbnd); var leftBoundVertex = leftBound.Vertex; Endpoint(leftBound.Edge, leftBound.Midpoint, leftBoundVertex); //set the endpoint of the left HalfEdge to be this vector Endpoint(rbnd.Edge, rbnd.Midpoint, leftBoundVertex); //set the endpoint of the right HalfEdge to be this vector list.Delete(leftBound); //mark the lowest HE for deletion - can't delete yet because there might be pointers to it in Hash Map queue.Delete(rbnd); //remove all vertex events to do with the right HE list.Delete(rbnd); //mark the right HE for deletion - can't delete yet because there might be pointers to it in Hash Map var pm = 0; if (bot.Y > top.Y) { //if the site to the left of the event is higher than the Site to the right of it, then swap them and set the 'pm' variable to 1 var temp = bot; bot = top; top = temp; pm = 1; } e = Bisect(bot, top); //Create Edge between the two Sites bisector = new HalfEdge(e, pm); //Create a HE from the Edge 'e', and make it point to that edge with its elEdge field EdgeList.ElInsert(llbnd, bisector); //Insert the new bisector to the right of the left HE Endpoint(e, 1 - pm, leftBoundVertex); //Set one endpoint to the new edge to be the vector point 'v' //If left HE and the new bisector don't intersect, then delete the left HE, and reinsert if ((p = Intersect(llbnd, bisector)) != null) { queue.PQinsert(queue.Delete(llbnd), p, p.Distance(bot)); } //If right HE and the new bisector don't intersect, then reinsert it if ((p = Intersect(bisector, rrbnd)) != null) { queue.PQinsert(bisector, p, p.Distance(bot)); } } else { break; } } for (leftBound = list.LeftEnd.Right; leftBound != list.RightEnd; leftBound = leftBound.Right) { ClipLine(leftBound.Edge); } }
private void ClipLine(Edge e) { double x1; double x2; double y1; double y2; //if the distance between the two points this line was created from is less than //the square root of 2, then ignore it if (e.Reg[0].Distance(e.Reg[1]) < 1.41421356) { return; } var s1 = DoubleComparison.IsEqual(e.A, 1.0) && (e.B >= 0.0) ? e.EndPoints[1] : e.EndPoints[0]; var s2 = DoubleComparison.IsEqual(e.A, 1.0) && (e.B >= 0.0) ? e.EndPoints[0] : e.EndPoints[1]; if (DoubleComparison.IsEqual(e.A, 1.0)) { y1 = (s1 != null) && (s1.Y > 1) ? s1.Y : 1; y1 = y1 > ImageHeight ? ImageHeight : y1; x1 = e.C - e.B * y1; y2 = (s2 != null) && (s2.Y < ImageHeight) ? s2.Y : ImageHeight; y2 = y2 < 1 ? 1 : y2; x2 = e.C - e.B * y2; if (((x1 > ImageWidth) && (x2 > ImageWidth)) || ((x1 < 1) && (x2 < 1))) { return; } x1 = x1 > ImageWidth ? ImageWidth : x1; x1 = x1 < 1 ? 1 : x1; y1 = (x1 > ImageWidth) || (x1 < 1) ? (e.C - x1) / e.B : y1; x2 = x2 > ImageWidth ? ImageWidth : x2; x2 = x2 < 1 ? 1 : x2; y2 = (x2 > ImageWidth) || (x2 < 1) ? (e.C - x2) / e.B : y2; } else { x1 = (s1 != null) && (s1.X > 1) ? s1.X : 1; x1 = x1 > ImageWidth ? ImageWidth : x1; y1 = e.C - e.A * x1; x2 = (s2 != null) && (s2.X < ImageWidth) ? s2.X : ImageWidth; x2 = x2 < 1 ? 1 : x2; y2 = e.C - e.A * x2; if (((y1 > ImageHeight) & (y2 > ImageHeight)) | ((y1 < 1) & (y2 < 1))) { return; } y1 = y1 > ImageHeight ? ImageHeight : y1; y1 = y1 < 1 ? 1 : y1; x1 = (y1 > ImageHeight) || (y1 < 1) ? (e.C - y1) / e.A : x1; y2 = y2 > ImageHeight ? ImageHeight : y2; y2 = y2 < 1 ? 1 : y2; x2 = (y2 > ImageHeight) || (y2 < 1) ? (e.C - y2) / e.A : x2; } PushGraphEdge(x1, y1, x2, y2); }