private void FinishLine(JCVEdge e) { if (!clipper.ClipEdge(e)) { return; } // Make sure the graph edges are CCW int flip = TriIsCCW(e.Sites[0].center, e.Points[0], e.Points[1]) ? 0 : 1; for (int i = 0; i < 2; i++) { PointF[] tempPts = new PointF[2]; JCVSite home = e.Sites[i]; JCVSite neighbor = e.Sites[1 - i]; tempPts[flip] = e.Points[i]; tempPts[1 - flip] = e.Points[1 - i]; JCVGraphEdge ge = new JCVGraphEdge(e, home, neighbor, tempPts); home.edges.Add(ge); } // // check that we didn't accidentally add a duplicate (rare), then remove it // if(ge->next && ge->angle == ge->next->angle ) // { // if(jcv_point_eq( &ge->pos[0], &ge->next->pos[0] ) && jcv_point_eq( &ge->pos[1], &ge->next->pos[1] ) ) // { // ge->next = ge->next->next; // Throw it away, they're so few anyways // } // } // } }
private void SiteEvent(JCVSite site) { JCVHalfEdge left = GetEdgeAboveX(site.center); JCVHalfEdge right = left.right; JCVSite bottom = (left.Edge is null) ? sites[0] : left.rSite; JCVEdge edge = new JCVEdge(bottom, site); edges.Add(edge); JCVHalfEdge he1 = new JCVHalfEdge(edge, false, left); JCVHalfEdge he2 = new JCVHalfEdge(edge, true, he1); lastInserted = right; PointF p; if (CheckCircleEvent(left, he1, out p)) { priorityQueue.Remove(left); left.vertex = p; left.Y = p.Y + PointDistance(site.center, p); priorityQueue.Add(left); } if (CheckCircleEvent(he2, right, out p)) { he2.vertex = p; he2.Y = p.Y + PointDistance(site.center, p); priorityQueue.Add(he2); } }
public float A, B, C; // line equation: ax + by + c = 0 public JCVEdge(JCVSite site, PointF[] points) { Points = points; Sites = new JCVSite[2]; Next = null; Sites[0] = site; Sites[1] = null; }
public JCVGraphEdge(JCVEdge e, JCVSite home, JCVSite neighbor, PointF[] points) { Edge = e; Home = home; Neighbor = neighbor; Points = points; Angle = calcAngle(home); }
//note to self: this is currently a black box. Figure out how it works at some point private bool RightOf(JCVHalfEdge he, PointF p) { JCVEdge edge = he.Edge; JCVSite topsite = edge.Sites[1]; bool rightOfSite = (p.X > topsite.X); if (rightOfSite ^ he.directionIsRight) //direction and rightOfSite don't match { return(rightOfSite); } float dxp, dyp, dxs, t1, t2, t3, yl; bool above; if (edge.A == 1) { dyp = p.Y - topsite.Y; dxp = p.X - topsite.X; bool fast = false; if ((!rightOfSite & (edge.B < 0)) || (rightOfSite & (edge.B >= 0))) { above = dyp >= edge.B * dxp; fast = above; } else { above = (p.X + p.Y * edge.B) > edge.C; if (edge.B < 0) { above = !above; } if (!above) { fast = true; } } if (!fast) { dxs = topsite.X - edge.Sites[0].X; above = edge.B * (dxp * dxp - dyp * dyp) < dxs * dyp * (1 + 2 * dxp / dxs + edge.B * edge.B); if (edge.B < 0) { above = !above; } } } else // edge.b == 1 { yl = edge.C - edge.A * p.X; t1 = p.Y - yl; t2 = p.X - topsite.X; t3 = yl - topsite.Y; above = t1 * t1 > (t2 * t2 + t3 * t3); } return(he.directionIsRight ^ above); }
private void CircleEvent() { JCVHalfEdge left = priorityQueue.First(); priorityQueue.Remove(left); JCVHalfEdge leftleft = left.left; JCVHalfEdge right = left.right; JCVHalfEdge rightright = right.right; JCVSite bottom = left.lSite; JCVSite top = right.rSite; PointF vertex = left.vertex; EndPoints(left.Edge, vertex, left.directionIsRight); EndPoints(right.Edge, vertex, right.directionIsRight); lastInserted = rightright; priorityQueue.Remove(right); left.Unlink(); right.Unlink(); bool dirIsRight = false; if (bottom.Y > top.Y) { JCVSite temp = bottom; bottom = top; top = temp; dirIsRight = true; } JCVEdge edge = new JCVEdge(bottom, top); edges.Add(edge); JCVHalfEdge he = new JCVHalfEdge(edge, dirIsRight, leftleft); EndPoints(edge, vertex, !dirIsRight); PointF p; if (CheckCircleEvent(leftleft, he, out p)) { priorityQueue.Remove(leftleft); leftleft.vertex = p; leftleft.Y = p.Y + PointDistance(bottom.center, p); priorityQueue.Add(leftleft); } if (CheckCircleEvent(he, rightright, out p)) { he.vertex = p; he.Y = p.Y + PointDistance(bottom.center, p); priorityQueue.Add(he); } }
private float calcAngle(JCVSite site) { // We take the average of the two points, since we can better distinguish between very small edges float x = (Points[0].X + Points[1].X) / 2; float y = (Points[0].Y + Points[1].Y) / 2; float diffy = y - site.Y; float angle = (float)Math.Atan2(diffy, x - site.X); if (diffy < 0) { angle = (float)(angle + 2 * Math.PI); } return(angle); }
public JCVEdge(JCVSite site1, JCVSite site2) { Points = new PointF[2]; Sites = new JCVSite[2]; Next = null; Sites[0] = site1; Sites[1] = site2; Points[0].X = JCV_INVALID_VALUE; Points[0].Y = JCV_INVALID_VALUE; Points[1].X = JCV_INVALID_VALUE; Points[1].Y = JCV_INVALID_VALUE; // Create line equation between S1 and S2: // float a = -1 * (site2.Y - site1.Y); // float b = site2.X - site1.X; // //float c = -1 * (site2.X - site1.X) * site1.Y + (site2.Y - site1.Y) * site1.X; // // // create perpendicular line // float pa = b; // float pb = -a; // //float pc = pa * site1.X + pb * site1.Y; // // // Move to the mid point // float mx = site1.X + dx * float(0.5); // float my = site1.Y + dy * float(0.5); // float pc = ( pa * mx + pb * my ); float dx = site2.X - site1.X; float dy = site2.Y - site1.Y; bool dx_is_larger = (dx * dx) > (dy * dy); // instead of fabs // Simplify it, using dx and dy C = dx * (site1.X + dx * 0.5f) + dy * (site1.Y + dy * 0.5f); if (dx_is_larger) { A = (float)1; B = dy / dx; C /= dx; } else { A = dx / dy; B = (float)1; C /= dy; } }
// They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap public override bool FillGaps(JCVSite site, JCVDiagram diagram) { if (site.edges.Count == 0) //no edges, must be single cell graph { if (diagram.sites.Count != 0) { throw new Exception("Invalid graph: edgeless site in graph"); } PointF[] points = new PointF[2]; points[0] = new PointF(boundingBox.Left, boundingBox.Top); points[1] = new PointF(boundingBox.Right, boundingBox.Top); JCVEdge edge = new JCVEdge(site, points); diagram.edges.Add(edge); JCVGraphEdge gap = new JCVGraphEdge(edge, site, null, points); site.edges.Add(gap); } if (site.edges.Count == 1) // one edge, assume corner gap { JCVGraphEdge current = site.edges[0]; PointF[] points = new PointF[2]; points[0] = current.Points[1]; if (current.Points[1].X < boundingBox.Right && current.Points[1].Y == boundingBox.Top) { points[1] = new PointF(boundingBox.Right, boundingBox.Top); } else if (current.Points[1].X > boundingBox.Left && current.Points[1].Y == boundingBox.Bottom) { points[1] = new PointF(boundingBox.Left, boundingBox.Bottom); } else if (current.Points[1].Y > boundingBox.Top && current.Points[1].X == boundingBox.Left) { points[1] = new PointF(boundingBox.Left, boundingBox.Top); } else if (current.Points[1].Y < boundingBox.Bottom && current.Points[1].X == boundingBox.Right) { points[1] = new PointF(boundingBox.Right, boundingBox.Bottom); } JCVEdge edge = new JCVEdge(site, points); diagram.edges.Add(edge); JCVGraphEdge gap = new JCVGraphEdge(edge, site, null, points); site.edges.Add(gap); } int eIndex = 0; while (eIndex < site.edges.Count) { JCVGraphEdge current = site.edges[eIndex]; JCVGraphEdge next = site.edges[(eIndex + 1) % site.edges.Count]; if (PointIsOnEdge(current.Points[1]) && current.Points[1] != next.Points[0]) { //border gap if (current.Points[1].X == next.Points[0].X || current.Points[1].Y == next.Points[0].Y) { PointF[] points = { current.Points[1], next.Points[0] }; JCVEdge edge = new JCVEdge(site, points); diagram.edges.Add(edge); JCVGraphEdge gap = new JCVGraphEdge(edge, site, null, points); // note: performance of repeated insertions may justify switching to linked list or SortedSet structure // for processing phase; List currently used due to ease of use, and fact that it will be as fast or // faster than other structures when the diagram is later used (post generation) site.edges.Insert(eIndex + 1, gap); } else if (PointIsOnEdge(current.Points[1]) && PointIsOnEdge(next.Points[0])) { PointF[] points = new PointF[2]; points[0] = current.Points[1]; if (current.Points[1].X < boundingBox.Right && current.Points[1].Y == boundingBox.Top) { points[1] = new PointF(boundingBox.Right, boundingBox.Top); } else if (current.Points[1].X > boundingBox.Left && current.Points[1].Y == boundingBox.Bottom) { points[1] = new PointF(boundingBox.Left, boundingBox.Bottom); } else if (current.Points[1].Y > boundingBox.Top && current.Points[1].X == boundingBox.Left) { points[1] = new PointF(boundingBox.Left, boundingBox.Top); } else if (current.Points[1].Y < boundingBox.Bottom && current.Points[1].X == boundingBox.Right) { points[1] = new PointF(boundingBox.Right, boundingBox.Bottom); } JCVEdge edge = new JCVEdge(site, points); diagram.edges.Add(edge); JCVGraphEdge gap = new JCVGraphEdge(edge, site, null, points); site.edges.Insert(eIndex + 1, gap); } else { //something went wrong; abort instead of looping indefinitely throw new Exception("Invalid Gap state, site location " + site.center.ToString()); } } eIndex++; } // current = current->next; // if (current) // { // next = current->next; // if (!next) // next = site->edges; // } //} ////*********** moved down from top **************** //jcv_graphedge* current = site->edges; //if (!current) //{ // // No edges, then it should be a single cell // assert(allocator->numsites == 1); // jcv_graphedge* gap = jcv_alloc_graphedge(allocator); // gap->neighbor = 0; // gap->pos[0] = clipper->min; // gap->pos[1].x = clipper->max.x; // gap->pos[1].y = clipper->min.y; // gap->angle = jcv_calc_sort_metric(site, gap); // gap->next = 0; // gap->edge = jcv_create_gap_edge(allocator, site, gap); // current = gap; // site->edges = gap; //} //jcv_graphedge* next = current->next; //if (!next) //{ // // Only one edge, then we assume it's a corner gap // jcv_graphedge* gap = jcv_alloc_graphedge(allocator); // jcv_create_corner_edge(allocator, site, current, gap); // gap->edge = jcv_create_gap_edge(allocator, site, gap); // gap->next = current->next; // current->next = gap; // current = gap; // next = site->edges; //} return(false); }
public abstract bool FillGaps(JCVSite site, JCVDiagram diagram);