/// <summary> /// Find the optimal split for a polygon /// </summary> /// <param name="poly"></param> /// <returns></returns> private (Vector3 point, Vector3 normal, List <BSPPoly <T> > left, List <BSPPoly <T> > right) FindOptimalSplit(BSPPoly <T>[] poly) { //init Vector3 point = Vector3.zero, normal = Vector3.zero; List <BSPPoly <T> > left = null, right = null; //optimal diff int diff = int.MaxValue; //get the working polygon IPoly working = poly[0].working; for (int i = 0; i < working.Resolution; i++) { //get the current point and normal Vector3 localPoint = working.GetPoint(i), localNormal = working.GetSurfaceNormal(i); //get output (List <BSPPoly <T> > localInside, List <BSPPoly <T> > localOutput) = GetSplit(poly, localPoint, localNormal); //calc the diff, and compare int localDiff = Math.Abs(localInside.Count - localOutput.Count); if (localDiff < diff) { left = localInside; right = localOutput; point = localPoint; normal = localNormal; diff = localDiff; } } return(point, normal, left, right); }
/// <summary> /// Get the next bsp node for the polygons /// </summary> /// <param name="polygons"></param> /// <param name="optimize"></param> /// <returns></returns> private BSPNode <T> GetNode(BSPPoly <T>[] polygons, bool optimize) { if (polygons.Length == 0) { return(null); } if (polygons.Length == 1) { return(new BSPNode <T>(polygons[0], null, null, Vector3.zero, Vector3.zero)); } if (optimize) { polygons = FindOptimalPoly(polygons); Vector3 point, normal; List <BSPPoly <T> > left, right; (point, normal, left, right) = FindOptimalSplit(polygons); return(new BSPNode <T>(polygons[0], GetNode(left.ToArray(), optimize), GetNode(right.ToArray(), optimize), point, normal)); } else { IPoly working = polygons[0].working; Vector3 point = working.GetPoint(0), normal = working.GetSurfaceNormal(0); (List <BSPPoly <T> > left, List <BSPPoly <T> > right) = GetSplit(polygons, point, normal); return(new BSPNode <T>(polygons[0], GetNode(left.ToArray(), optimize), GetNode(right.ToArray(), optimize), point, normal)); } }
public static bool IsEdgeIntersectingPolygon(IPoly polygon, Vector3 point1, Vector3 point2) { for (int i = 0; i < polygon.Resolution; i++) { Vector3 a = polygon.GetPoint(i); Vector3 b = polygon.GetPointWrapped(i + 1); if ((point1 - a).sqrMagnitude < 0.0001f) { continue; } if ((point1 - b).sqrMagnitude < 0.0001f) { continue; } if ((point2 - a).sqrMagnitude < 0.0001f) { continue; } if ((point2 - b).sqrMagnitude < 0.0001f) { continue; } if (Math3d.AreLineSegmentsCrossing(a, b, point1, point2)) { return(true); } } return(false); }
public static IPoly RemoveDuplicateVerticies(IPoly poly) { List <Vector3> output = new List <Vector3>(); for (int i = 0; i < poly.Resolution; i++) { if (output.Count == 0) { output.Add(poly.GetPoint(i)); } else { Vector3 point = poly.GetPoint(i); Vector3 last = output[output.Count - 1]; if (!Math3d.Compare(point, last)) { output.Add(point); } } } if (Math3d.Compare(output[0], output[output.Count - 1])) { output.RemoveAt(0); } return(poly.Clone(output.ToArray())); }
/// <summary> /// Make a concave polygon into a convex one /// </summary> /// <param name="poly"></param> /// <returns></returns> public static IEnumerable <IPoly> MakeConvex(IPoly poly) { List <Vector3> input = poly.GetPoints().ToList(); List <List <Vector3> > output = BayazitDecomposer.ConvexPartition(input); return(output.Select(x => poly.Clone(x.ToArray()))); }
public static (float[], int[]) GetBisectedThetaValues(IPoly polygon, int index) { Vector3 center = polygon.GetPoint(index); Vector3 left = polygon.GetPointWrapped(index - 1); Vector3 right = polygon.GetPointWrapped(index + 1); Vector3 biSect = Vector3.Lerp((left - center).normalized, (right - center).normalized, 0.5f).normalized; int[] indicies = new int[polygon.Resolution - 1]; float[] tValues = new float[polygon.Resolution - 1]; for (int i = 0; i < polygon.Resolution; i++) { if (i < index) { indicies[i] = i; tValues[i] = Vector3.Dot(biSect, (polygon.GetPoint(i) - center).normalized); } else if (i == index) { continue; } else { indicies[i - 1] = i; tValues[i - 1] = Vector3.Dot(biSect, (polygon.GetPoint(i) - center).normalized); } } Array.Sort(tValues, indicies); return(tValues, indicies); }
public ComplexPoly(IPoly poly, IPoly[] holes) { Debug.Assert(poly.Holes.Length == 0, "Outline can not have holes"); Debug.Assert(holes.All(p => p.Holes.Length == 0), "Holes can not have holes"); Holes = holes; _poly = poly; }
internal static void DefaultWrite(this IPoly poly, EndianWriter writer) { foreach (ushort i in poly.Indices) { writer.WriteUInt16(i); } }
public static IPoly InjectEdge(IPoly poly, IEdge newEdge) { if (poly is EdgePoly) { return(InjectEdge(poly as EdgePoly, newEdge)); } throw new System.NotImplementedException(); }
internal static void DefaultWriteNJA(this IPoly poly, TextWriter writer) { foreach (ushort i in poly.Indices) { writer.Write(i); writer.Write(", "); } }
/// <summary> /// Divide a polygon using the specified plane. /// </summary> /// <param name="poly"> /// The polygon to divide. /// </param> /// <param name="inside"> /// The new polygon inside the plane. /// </param> /// <param name="outside"> /// The new polygon outside the plane. /// </param> /// <param name="p0"> /// Plane Origin. /// </param> /// <param name="pn"> /// Plane Normal. /// </param> /// <param name="threshold"> /// The percision threshold for comparing points to the plane. /// </param> public static void Divide( IPoly poly, out IPoly inside, out IPoly outside, Vector3 p0, Vector3 pn, float threshold = 0.001f) { Divide(poly, out inside, out outside, p0, pn, (x, y) => x.Clone(y), threshold); }
/// <summary> /// Calculates the collision status of two polygons. /// Returns colliding is the polygons are intersecting, not colliding if they are not, AEnclosedInB is A is inside B, and BEnclosedInA for ... /// This method also returns the offending index /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="offendingIndex"></param> /// <param name="threshold"></param> /// <returns></returns> public static CollisionType CalcCollision2D(IPoly a, IPoly b, out int offendingIndex, float threshold = 0.001f) { offendingIndex = -1; //check for a bool enclosed = true; for (int i = 0; i < a.Resolution; i++) { Vector3 point = a.GetPoint(i); Vector3 surfaceNormal = a.GetSurfaceNormal(i); bool inside, outside; CheckPoly(b, point, surfaceNormal, out inside, out outside, threshold); if (outside) { enclosed = false; } if (!inside) { return(CollisionType.NotColliding); } if (inside && outside) { offendingIndex = i; } } if (enclosed) { return(CollisionType.BEnclosedInA); } //check for b enclosed = true; for (int i = 0; i < b.Resolution; i++) { Vector3 point = b.GetPoint(i); Vector3 surfaceNormal = b.GetSurfaceNormal(i); bool inside, outside; CheckPoly(a, point, surfaceNormal, out inside, out outside, threshold); if (outside) { enclosed = false; } if (!inside) { return(CollisionType.NotColliding); } } if (enclosed) { return(CollisionType.AEnclosedInB); } return(CollisionType.Colliding); }
/// <summary> /// Calculates the collision status of two blocks. /// Returns colliding is the polygons are intersecting, not colliding if they are not, AEnclosedInB is A is inside B, and BEnclosedInA for ... /// The offending face is also returned, for the sake of other algorithms. /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="offendingFace"></param> /// <param name="threshold"></param> /// <returns></returns> public static CollisionType CalcCollision3D(IBlock a, IBlock b, out IPoly offendingFace, float threshold = 0.001f) { offendingFace = null; //check for a bool enclosed = true; foreach (var poly in a.GetFaces()) { Vector3 point = poly.GetPoint(0); Vector3 surfaceNormal = poly.GetNormal(); bool inside, outside; CheckBlock(b, point, surfaceNormal, out inside, out outside, threshold); if (outside) { enclosed = false; } if (!inside) { return(CollisionType.NotColliding); } if (inside && outside) { offendingFace = poly; } } if (enclosed) { return(CollisionType.BEnclosedInA); } //check for b enclosed = true; foreach (var poly in b.GetFaces()) { Vector3 point = poly.GetPoint(0); Vector3 surfaceNormal = poly.GetNormal(); bool inside, outside; CheckBlock(a, point, surfaceNormal, out inside, out outside, threshold); if (outside) { enclosed = false; } if (!inside) { return(CollisionType.NotColliding); } } if (enclosed) { return(CollisionType.AEnclosedInB); } return(CollisionType.Colliding); }
public ComplexPoly(Vector2[] points, Vector2[][] holes) { Holes = new Poly[holes.Length]; for (int n = 0; n < holes.Length; n++) { Holes[n] = new Poly(holes[n]); } _poly = new Poly(points); }
public BSPPoly(T original, IPoly working) { this.original = original; if (working is NGon) { this.working = (NGon)working; } else { this.working = new NGon(working); } }
/// <summary> /// Create a RenderInstruction representing a polygon. Uses triangulation via Triangulator.dll. /// </summary> /// <param name="points">The points of the polygon</param> /// <param name="color">The color of the polygon</param> public static RenderInstruction Polygon(IPoly poly, Color color) { Vector2[] pointsToSend = poly.Points; /*foreach (IPoly hole in poly.Holes) { pointsToSend = Triangulator.Triangulator.CutHoleInShape(pointsToSend, hole.Points); }*/ Triangulation triangulation = Triangulator.Triangulate(pointsToSend); VertexPositionColor[] vertices = new VertexPositionColor[triangulation.Vertices.Length]; for (int n = 0; n < triangulation.Vertices.Length; n++) { vertices[n].Position = triangulation.Vertices[n].ToVector3(); vertices[n].Color = color; } return new RenderInstruction(vertices, triangulation.Indices, PrimitiveType.TriangleList); }
private static EdgeTable build_lmt( LmtTable lmt_table, ScanBeamTreeEntries sbte, IPoly p, int type, //poly type SUBJ/CLIP OperationType op) { /* Create the entire input polygon edge table in one go */ EdgeTable edge_table = new EdgeTable(); for ( int c= 0; c < p.InnerPolygonCount; c++) { IPoly ip = p.GetInnerPoly(c); if( !ip.IsContributing(0) ) { /* Ignore the non-contributing contour */ ip.SetContributing(0, true); } else { /* Perform contour optimisation */ int num_vertices= 0; int e_index = 0 ; edge_table = new EdgeTable(); for ( int i= 0; i < ip.PointCount; i++) { if( OPTIMAL(ip, i) ) { double x = ip.GetX(i); double y = ip.GetY(i); edge_table.addNode( x, y ); /* Record vertex in the scanbeam table */ add_to_sbtree( sbte, ip.GetY(i) ); num_vertices++; } } /* Do the contour forward pass */ for ( int min= 0; min < num_vertices; min++) { /* If a forward local minimum... */ if( edge_table.FWD_MIN( min ) ) { /* Search for the next local maximum... */ int num_edges = 1; int max = NEXT_INDEX( min, num_vertices ); while( edge_table.NOT_FMAX( max ) ) { num_edges++; max = NEXT_INDEX( max, num_vertices ); } /* Build the next edge list */ int v = min; EdgeNode e = edge_table.getNode( e_index ); e.bstate[BELOW] = BundleState.UNBUNDLED; e.bundle[BELOW, CLIP] = 0; e.bundle[BELOW, SUBJ] = 0; for ( int i= 0; i < num_edges; i++) { EdgeNode ei = edge_table.getNode( e_index+i ); EdgeNode ev = edge_table.getNode( v ); ei.xb = ev.vertex.X; ei.bot.X = ev.vertex.X; ei.bot.Y = ev.vertex.Y; v = NEXT_INDEX(v, num_vertices); ev = edge_table.getNode( v ); ei.top.X= ev.vertex.X; ei.top.Y= ev.vertex.Y; ei.dx= (ev.vertex.X - ei.bot.X) / (ei.top.Y - ei.bot.Y); ei.type = type; ei.outp[ABOVE] = null ; ei.outp[BELOW] = null; ei.next = null; ei.prev = null; ei.succ = ((num_edges > 1) && (i < (num_edges - 1))) ? edge_table.getNode(e_index+i+1) : null; ei.pred = ((num_edges > 1) && (i > 0)) ? edge_table.getNode(e_index+i-1) : null ; ei.next_bound = null ; ei.bside[CLIP] = (op == OperationType.GPC_DIFF) ? RIGHT : LEFT; ei.bside[SUBJ] = LEFT ; } insert_bound( bound_list(lmt_table, edge_table.getNode(min).vertex.Y), e); #if DEBUG Console.WriteLine("fwd"); lmt_table.print(); #endif e_index += num_edges; } } /* Do the contour reverse pass */ for ( int min= 0; min < num_vertices; min++) { /* If a reverse local minimum... */ if ( edge_table.REV_MIN( min ) ) { /* Search for the previous local maximum... */ int num_edges= 1; int max = PREV_INDEX(min, num_vertices); while( edge_table.NOT_RMAX( max ) ) { num_edges++; max = PREV_INDEX(max, num_vertices); } /* Build the previous edge list */ int v = min; EdgeNode e = edge_table.getNode( e_index ); e.bstate[BELOW] = BundleState.UNBUNDLED; e.bundle[BELOW, CLIP] = 0; e.bundle[BELOW, SUBJ] = 0; for (int i= 0; i < num_edges; i++) { EdgeNode ei = edge_table.getNode( e_index+i ); EdgeNode ev = edge_table.getNode( v ); ei.xb = ev.vertex.X; ei.bot.X = ev.vertex.X; ei.bot.Y = ev.vertex.Y; v= PREV_INDEX(v, num_vertices); ev = edge_table.getNode( v ); ei.top.X = ev.vertex.X; ei.top.Y = ev.vertex.Y; ei.dx = (ev.vertex.X - ei.bot.X) / (ei.top.Y - ei.bot.Y); ei.type = type; ei.outp[ABOVE] = null; ei.outp[BELOW] = null; ei.next = null ; ei.prev = null; ei.succ = ((num_edges > 1) && (i < (num_edges - 1))) ? edge_table.getNode(e_index+i+1) : null; ei.pred = ((num_edges > 1) && (i > 0)) ? edge_table.getNode(e_index+i-1) : null ; ei.next_bound = null ; ei.bside[CLIP] = (op == OperationType.GPC_DIFF) ? RIGHT : LEFT; ei.bside[SUBJ] = LEFT; } insert_bound( bound_list(lmt_table, edge_table.getNode(min).vertex.Y), e); #if DEBUG Console.WriteLine("rev"); lmt_table.print(); #endif e_index+= num_edges; } } } } return edge_table; }
/// <summary> /// <code>clip()</code> is the main method of the clipper This /// is where the conversion from really begins. /// </summary> private static IPoly Clip( OperationType op, IPoly subj, IPoly clip, Type polyType) { // Create an empty type IPoly result = CreateNewPoly(polyType) ; /* Test for trivial NULL result cases */ if( (subj.IsEmpty() && clip.IsEmpty()) || (subj.IsEmpty() && ((op == OperationType.GPC_INT) || (op == OperationType.GPC_DIFF))) || (clip.IsEmpty() && (op == OperationType.GPC_INT)) ) { return result ; } /* Identify potentialy contributing contours */ if( ((op == OperationType.GPC_INT) || (op == OperationType.GPC_DIFF)) && !subj.IsEmpty() && !clip.IsEmpty() ) { minimax_test(subj, clip, op); } /* Build LMT */ LmtTable lmt_table = new LmtTable(); ScanBeamTreeEntries sbte = new ScanBeamTreeEntries(); EdgeTable s_heap = null ; EdgeTable c_heap = null ; if (!subj.IsEmpty()) { s_heap = build_lmt(lmt_table, sbte, subj, SUBJ, op); } #if DEBUG // Show debugging information Console.WriteLine(""); Console.WriteLine(" ------------ After build_lmt for subj ---------"); lmt_table.print(); #endif if (!clip.IsEmpty()) { c_heap = build_lmt(lmt_table, sbte, clip, CLIP, op); } #if DEBUG // Debugging information Console.WriteLine(""); Console.WriteLine(" ------------ After build_lmt for clip ---------"); lmt_table.print(); #endif /* Return a NULL result if no contours contribute */ if (lmt_table.top_node == null) { return result; } /* Build scanbeam table from scanbeam tree */ double[] sbt = sbte.build_sbt(); int[] parity = new int[2] ; parity[0] = LEFT ; parity[1] = LEFT ; /* Invert clip polygon for difference operation */ if (op == OperationType.GPC_DIFF) { parity[CLIP]= RIGHT; } #if DEBUG print_sbt(sbt); #endif LmtNode local_min = lmt_table.top_node ; TopPolygonNode out_poly = new TopPolygonNode(); // used to create resulting Poly AetTree aet = new AetTree(); int scanbeam = 0 ; /* Process each scanbeam */ while( scanbeam < sbt.Length ) { /* Set yb and yt to the bottom and top of the scanbeam */ double yb = sbt[scanbeam++]; double yt = 0.0 ; double dy = 0.0 ; if( scanbeam < sbt.Length ) { yt = sbt[scanbeam]; dy = yt - yb; } /* === SCANBEAM BOUNDARY PROCESSING ================================ */ /* If LMT node corresponding to yb exists */ if (local_min != null ) { if (local_min.y == yb) { /* Add edges starting at this local minimum to the AET */ for( EdgeNode edge = local_min.first_bound; (edge != null) ; edge= edge.next_bound) { add_edge_to_aet( aet, edge ); } local_min = local_min.next; } } #if DEBUG aet.print(); #endif /* Set dummy previous x value */ double px = -Double.MaxValue ; /* Create bundles within AET */ EdgeNode e0 = aet.top_node ; EdgeNode e1 = aet.top_node ; /* Set up bundle fields of first edge */ aet.top_node.bundle[ABOVE, aet.top_node.type ] = (aet.top_node.top.Y != yb) ? 1 : 0; aet.top_node.bundle[ABOVE, ((aet.top_node.type==0) ? 1 : 0) ] = 0; aet.top_node.bstate[ABOVE] = BundleState.UNBUNDLED; for (EdgeNode next_edge= aet.top_node.next ; (next_edge != null); next_edge = next_edge.next) { int ne_type = next_edge.type ; int ne_type_opp = ((next_edge.type==0) ? 1 : 0); //next edge type opposite /* Set up bundle fields of next edge */ next_edge.bundle[ABOVE, ne_type ]= (next_edge.top.Y != yb) ? 1 : 0; next_edge.bundle[ABOVE, ne_type_opp ] = 0 ; next_edge.bstate[ABOVE] = BundleState.UNBUNDLED; /* Bundle edges above the scanbeam boundary if they coincide */ if ( next_edge.bundle[ABOVE, ne_type] == 1 ) { if (EQ(e0.xb, next_edge.xb) && EQ(e0.dx, next_edge.dx) && (e0.top.Y != yb)) { next_edge.bundle[ABOVE, ne_type ] ^= e0.bundle[ABOVE, ne_type ]; next_edge.bundle[ABOVE, ne_type_opp ] = e0.bundle[ABOVE, ne_type_opp ]; next_edge.bstate[ABOVE] = BundleState.BUNDLE_HEAD; e0.bundle[ABOVE, CLIP] = 0; e0.bundle[ABOVE, SUBJ] = 0; e0.bstate[ABOVE] = BundleState.BUNDLE_TAIL; } e0 = next_edge; } } int[] horiz = new int[2] ; horiz[CLIP]= HState.NH; horiz[SUBJ]= HState.NH; int[] exists = new int[2] ; exists[CLIP] = 0 ; exists[SUBJ] = 0 ; PolygonNode cf = null ; /* Process each edge at this scanbeam boundary */ for (EdgeNode edge= aet.top_node ; (edge != null); edge = edge.next ) { exists[CLIP] = edge.bundle[ABOVE, CLIP] + (edge.bundle[BELOW, CLIP] << 1); exists[SUBJ] = edge.bundle[ABOVE, SUBJ] + (edge.bundle[BELOW, SUBJ] << 1); if( (exists[CLIP] != 0) || (exists[SUBJ] != 0) ) { /* Set bundle side */ edge.bside[CLIP] = parity[CLIP]; edge.bside[SUBJ] = parity[SUBJ]; bool contributing = false ; int br=0, bl=0, tr=0, tl=0 ; /* Determine contributing status and quadrant occupancies */ if( (op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT) ) { contributing= ((exists[CLIP]!=0) && ((parity[SUBJ]!=0) || (horiz[SUBJ]!=0))) || ((exists[SUBJ]!=0) && ((parity[CLIP]!=0) || (horiz[CLIP]!=0))) || ((exists[CLIP]!=0) && (exists[SUBJ]!=0) && (parity[CLIP] == parity[SUBJ])); br = ((parity[CLIP]!=0) && (parity[SUBJ]!=0)) ? 1 : 0; bl = ( ((parity[CLIP] ^ edge.bundle[ABOVE, CLIP])!=0) && ((parity[SUBJ] ^ edge.bundle[ABOVE, SUBJ])!=0) ) ? 1 : 0; tr = ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0)) !=0) && ((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0)) !=0) ) ? 1 : 0; tl = (((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW, CLIP])!=0) && ((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW, SUBJ])!=0))?1:0; } else if( op == OperationType.GPC_XOR ) { contributing= (exists[CLIP]!=0) || (exists[SUBJ]!=0); br= (parity[CLIP]) ^ (parity[SUBJ]); bl= (parity[CLIP] ^ edge.bundle[ABOVE, CLIP]) ^ (parity[SUBJ] ^ edge.bundle[ABOVE, SUBJ]); tr= (parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0)) ^ (parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0)); tl= (parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW, CLIP]) ^ (parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW, SUBJ]); } else if( op == OperationType.GPC_UNION ) { contributing= ((exists[CLIP]!=0) && (!(parity[SUBJ]!=0) || (horiz[SUBJ]!=0))) || ((exists[SUBJ]!=0) && (!(parity[CLIP]!=0) || (horiz[CLIP]!=0))) || ((exists[CLIP]!=0) && (exists[SUBJ]!=0) && (parity[CLIP] == parity[SUBJ])); br= ((parity[CLIP]!=0) || (parity[SUBJ]!=0))?1:0; bl= (((parity[CLIP] ^ edge.bundle[ABOVE, CLIP])!=0) || ((parity[SUBJ] ^ edge.bundle[ABOVE, SUBJ])!=0))?1:0; tr= ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0))!=0) || ((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0))!=0) ) ?1:0; tl= ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW, CLIP])!=0) || ((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW, SUBJ])!=0) ) ? 1:0; } else { throw new Exception("Unknown op"); } /* Update parity */ parity[CLIP] ^= edge.bundle[ABOVE, CLIP]; parity[SUBJ] ^= edge.bundle[ABOVE, SUBJ]; /* Update horizontal state */ if (exists[CLIP]!=0) { horiz[CLIP] = HState.next_h_state[horiz[CLIP], ((exists[CLIP] - 1) << 1) + parity[CLIP]]; } if( exists[SUBJ]!=0) { horiz[SUBJ] = HState.next_h_state[horiz[SUBJ], ((exists[SUBJ] - 1) << 1) + parity[SUBJ]]; } if (contributing) { double xb = edge.xb; VertexType vclass = (VertexType) ( tr + (tl << 1) + (br << 2) + (bl << 3)); switch (vclass) { case VertexType.EMN: case VertexType.IMN: edge.outp[ABOVE] = out_poly.add_local_min(xb, yb); px = xb; cf = edge.outp[ABOVE]; break; case VertexType.ERI: if (xb != px) { cf.add_right( xb, yb); px= xb; } edge.outp[ABOVE]= cf; cf= null; break; case VertexType.ELI: edge.outp[BELOW].add_left( xb, yb); px= xb; cf= edge.outp[BELOW]; break; case VertexType.EMX: if (xb != px) { cf.add_left( xb, yb); px= xb; } out_poly.merge_right(cf, edge.outp[BELOW]); cf= null; break; case VertexType.ILI: if (xb != px) { cf.add_left( xb, yb); px= xb; } edge.outp[ABOVE]= cf; cf= null; break; case VertexType.IRI: edge.outp[BELOW].add_right( xb, yb ); px= xb; cf= edge.outp[BELOW]; edge.outp[BELOW]= null; break; case VertexType.IMX: if (xb != px) { cf.add_right( xb, yb ); px= xb; } out_poly.merge_left(cf, edge.outp[BELOW]); cf= null; edge.outp[BELOW]= null; break; case VertexType.IMM: if (xb != px) { cf.add_right( xb, yb); px= xb; } out_poly.merge_left(cf, edge.outp[BELOW]); edge.outp[BELOW]= null; edge.outp[ABOVE] = out_poly.add_local_min(xb, yb); cf= edge.outp[ABOVE]; break; case VertexType.EMM: if (xb != px) { cf.add_left( xb, yb); px= xb; } out_poly.merge_right(cf, edge.outp[BELOW]); edge.outp[BELOW]= null; edge.outp[ABOVE] = out_poly.add_local_min(xb, yb); cf= edge.outp[ABOVE]; break; case VertexType.LED: if (edge.bot.Y == yb) edge.outp[BELOW].add_left( xb, yb); edge.outp[ABOVE]= edge.outp[BELOW]; px= xb; break; case VertexType.RED: if (edge.bot.Y == yb) edge.outp[BELOW].add_right( xb, yb ); edge.outp[ABOVE]= edge.outp[BELOW]; px= xb; break; default: break; } /* End of switch */ } /* End of contributing conditional */ } /* End of edge exists conditional */ #if DEBUG out_poly.print(); #endif } /* End of AET loop */ /* Delete terminating edges from the AET, otherwise compute xt */ for (EdgeNode edge = aet.top_node ; (edge != null); edge = edge.next) { if (edge.top.Y == yb) { EdgeNode prev_edge = edge.prev; EdgeNode next_edge= edge.next; if (prev_edge != null) prev_edge.next = next_edge; else aet.top_node = next_edge; if (next_edge != null ) next_edge.prev = prev_edge; /* Copy bundle head state to the adjacent tail edge if required */ if ((edge.bstate[BELOW] == BundleState.BUNDLE_HEAD) && (prev_edge!=null)) { if (prev_edge.bstate[BELOW] == BundleState.BUNDLE_TAIL) { prev_edge.outp[BELOW]= edge.outp[BELOW]; prev_edge.bstate[BELOW]= BundleState.UNBUNDLED; if ( prev_edge.prev != null) { if (prev_edge.prev.bstate[BELOW] == BundleState.BUNDLE_TAIL) { prev_edge.bstate[BELOW] = BundleState.BUNDLE_HEAD; } } } } } else { if (edge.top.Y == yt) edge.xt= edge.top.X; else edge.xt= edge.bot.X + edge.dx * (yt - edge.bot.Y); } } if (scanbeam < sbte.sbt_entries ) { /* === SCANBEAM INTERIOR PROCESSING ============================== */ /* Build intersection table for the current scanbeam */ ItNodeTable it_table = new ItNodeTable(); it_table.build_intersection_table(aet, dy); /* Process each node in the intersection table */ for (ItNode intersect = it_table.top_node ; (intersect != null); intersect = intersect.next) { e0= intersect.ie[0]; e1= intersect.ie[1]; /* Only generate output for contributing intersections */ if ( ((e0.bundle[ABOVE, CLIP]!=0) || (e0.bundle[ABOVE, SUBJ]!=0)) && ((e1.bundle[ABOVE, CLIP]!=0) || (e1.bundle[ABOVE, SUBJ]!=0))) { PolygonNode p = e0.outp[ABOVE]; PolygonNode q = e1.outp[ABOVE]; double ix = intersect.point.X; double iy = intersect.point.Y + yb; int in_clip = ( ( (e0.bundle[ABOVE, CLIP]!=0) && !(e0.bside[CLIP]!=0)) || ( (e1.bundle[ABOVE, CLIP]!=0) && (e1.bside[CLIP]!=0)) || (!(e0.bundle[ABOVE, CLIP]!=0) && !(e1.bundle[ABOVE, CLIP]!=0) && (e0.bside[CLIP]!=0) && (e1.bside[CLIP]!=0) ) ) ? 1 : 0; int in_subj = ( ( (e0.bundle[ABOVE, SUBJ]!=0) && !(e0.bside[SUBJ]!=0)) || ( (e1.bundle[ABOVE, SUBJ]!=0) && (e1.bside[SUBJ]!=0)) || (!(e0.bundle[ABOVE, SUBJ]!=0) && !(e1.bundle[ABOVE, SUBJ]!=0) && (e0.bside[SUBJ]!=0) && (e1.bside[SUBJ]!=0) ) ) ? 1 : 0; int tr=0, tl=0, br=0, bl=0 ; /* Determine quadrant occupancies */ if( (op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT) ) { tr= ((in_clip!=0) && (in_subj!=0)) ? 1 : 0; tl= (((in_clip ^ e1.bundle[ABOVE, CLIP])!=0) && ((in_subj ^ e1.bundle[ABOVE, SUBJ])!=0))?1:0; br= (((in_clip ^ e0.bundle[ABOVE, CLIP])!=0) && ((in_subj ^ e0.bundle[ABOVE, SUBJ])!=0))?1:0; bl= (((in_clip ^ e1.bundle[ABOVE, CLIP] ^ e0.bundle[ABOVE, CLIP])!=0) && ((in_subj ^ e1.bundle[ABOVE, SUBJ] ^ e0.bundle[ABOVE, SUBJ])!=0) ) ? 1:0; } else if( op == OperationType.GPC_XOR ) { tr= (in_clip)^ (in_subj); tl= (in_clip ^ e1.bundle[ABOVE, CLIP]) ^ (in_subj ^ e1.bundle[ABOVE, SUBJ]); br= (in_clip ^ e0.bundle[ABOVE, CLIP]) ^ (in_subj ^ e0.bundle[ABOVE, SUBJ]); bl= (in_clip ^ e1.bundle[ABOVE, CLIP] ^ e0.bundle[ABOVE, CLIP]) ^ (in_subj ^ e1.bundle[ABOVE, SUBJ] ^ e0.bundle[ABOVE, SUBJ]); } else if( op == OperationType.GPC_UNION ) { tr= ((in_clip!=0) || (in_subj!=0)) ? 1 : 0; tl= (((in_clip ^ e1.bundle[ABOVE, CLIP])!=0) || ((in_subj ^ e1.bundle[ABOVE, SUBJ])!=0)) ? 1 : 0; br= (((in_clip ^ e0.bundle[ABOVE, CLIP])!=0) || ((in_subj ^ e0.bundle[ABOVE, SUBJ])!=0)) ? 1 : 0; bl= (((in_clip ^ e1.bundle[ABOVE, CLIP] ^ e0.bundle[ABOVE, CLIP])!=0) || ((in_subj ^ e1.bundle[ABOVE, SUBJ] ^ e0.bundle[ABOVE, SUBJ])!=0)) ? 1 : 0; } else { throw new Exception("Unknown op type, "+op); } VertexType vclass = (VertexType) ( tr + (tl << 1) + (br << 2) + (bl << 3)); switch (vclass) { case VertexType.EMN: e0.outp[ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[ABOVE] = e0.outp[ABOVE]; break; case VertexType.ERI: if (p != null) { p.add_right(ix, iy); e1.outp[ABOVE]= p; e0.outp[ABOVE]= null; } break; case VertexType.ELI: if (q != null) { q.add_left(ix, iy); e0.outp[ABOVE]= q; e1.outp[ABOVE]= null; } break; case VertexType.EMX: if ((p!=null) && (q!=null)) { p.add_left( ix, iy); out_poly.merge_right(p, q); e0.outp[ABOVE]= null; e1.outp[ABOVE]= null; } break; case VertexType.IMN: e0.outp[ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[ABOVE]= e0.outp[ABOVE]; break; case VertexType.ILI: if (p != null) { p.add_left(ix, iy); e1.outp[ABOVE]= p; e0.outp[ABOVE]= null; } break; case VertexType.IRI: if (q!=null) { q.add_right(ix, iy); e0.outp[ABOVE]= q; e1.outp[ABOVE]= null; } break; case VertexType.IMX: if ((p!=null) && (q!=null)) { p.add_right(ix, iy); out_poly.merge_left(p, q); e0.outp[ABOVE]= null; e1.outp[ABOVE]= null; } break; case VertexType.IMM: if ((p!=null) && (q!=null)) { p.add_right(ix, iy); out_poly.merge_left(p, q); e0.outp[ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[ABOVE]= e0.outp[ABOVE]; } break; case VertexType.EMM: if ((p!=null) && (q!=null)) { p.add_left(ix, iy); out_poly.merge_right(p, q); e0.outp[ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[ABOVE] = e0.outp[ABOVE]; } break; default: break; } /* End of switch */ } /* End of contributing intersection conditional */ /* Swap bundle sides in response to edge crossing */ if (e0.bundle[ABOVE, CLIP]!=0) e1.bside[CLIP] = (e1.bside[CLIP]==0)?1:0; if (e1.bundle[ABOVE, CLIP]!=0) e0.bside[CLIP]= (e0.bside[CLIP]==0)?1:0; if (e0.bundle[ABOVE, SUBJ]!=0) e1.bside[SUBJ]= (e1.bside[SUBJ]==0)?1:0; if (e1.bundle[ABOVE, SUBJ]!=0) e0.bside[SUBJ]= (e0.bside[SUBJ]==0)?1:0; /* Swap e0 and e1 bundles in the AET */ EdgeNode prev_edge = e0.prev; EdgeNode next_edge = e1.next; if (next_edge != null) { next_edge.prev = e0; } if (e0.bstate[ABOVE] == BundleState.BUNDLE_HEAD) { bool search = true; while (search) { prev_edge= prev_edge.prev; if (prev_edge != null) { if (prev_edge.bstate[ABOVE] != BundleState.BUNDLE_TAIL) { search= false; } } else { search= false; } } } if (prev_edge == null) { aet.top_node.prev = e1; e1.next = aet.top_node; aet.top_node = e0.next; } else { prev_edge.next.prev = e1; e1.next = prev_edge.next; prev_edge.next = e0.next; } e0.next.prev = prev_edge; e1.next.prev = e1; e0.next = next_edge; #if DEBUG out_poly.print(); #endif } /* End of IT loop*/ /* Prepare for next scanbeam */ for ( EdgeNode edge = aet.top_node; (edge != null); edge = edge.next) { EdgeNode next_edge = edge.next; EdgeNode succ_edge = edge.succ; if ((edge.top.Y == yt) && (succ_edge!=null)) { /* Replace AET edge by its successor */ succ_edge.outp[BELOW]= edge.outp[ABOVE]; succ_edge.bstate[BELOW]= edge.bstate[ABOVE]; succ_edge.bundle[BELOW, CLIP]= edge.bundle[ABOVE, CLIP]; succ_edge.bundle[BELOW, SUBJ]= edge.bundle[ABOVE, SUBJ]; EdgeNode prev_edge = edge.prev; if ( prev_edge != null ) prev_edge.next = succ_edge; else aet.top_node = succ_edge; if (next_edge != null) next_edge.prev= succ_edge; succ_edge.prev = prev_edge; succ_edge.next = next_edge; } else { /* Update this edge */ edge.outp[BELOW]= edge.outp[ABOVE]; edge.bstate[BELOW]= edge.bstate[ABOVE]; edge.bundle[BELOW, CLIP]= edge.bundle[ABOVE, CLIP]; edge.bundle[BELOW, SUBJ]= edge.bundle[ABOVE, SUBJ]; edge.xb= edge.xt; } edge.outp[ABOVE]= null; } } } /* === END OF SCANBEAM PROCESSING ================================== */ /* Generate result polygon from out_poly */ result = out_poly.getResult(polyType); return result ; }
/** * Return the xor of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyType</code>. See the note in the class description * for more on <ocde>polyType</code>. * * @param p1 One of the polygons to performt he xor with * @param p2 One of the polygons to performt he xor with * @param polyType The type of <code>Poly</code> to return */ public static IPoly Xor( IPoly p1, IPoly p2, Type polyType ) { return Clip( OperationType.GPC_XOR, p1, p2, polyType ); }
/** * Return the xor of <code>p1</code> and <code>p2</code> where the * return type is of <code>PolyDefault</code>. * * @param p1 One of the polygons to performt he xor with * @param p2 One of the polygons to performt he xor with */ public static IPoly Xor( IPoly p1, IPoly p2 ) { return Clip( OperationType.GPC_XOR, p1, p2, typeof(PolyDefault) ); }
/** * Throws exception if called */ public void Add(IPoly p) { throw new Exception("Cannot add poly to a simple poly."); }
/** * Return the union of <code>p1</code> and <code>p2</code> where the * return type is of <code>PolyDefault</code>. * * @param p1 One of the polygons to performt he union with * @param p2 One of the polygons to performt he union with */ public static IPoly Union( IPoly p1, IPoly p2 ) { return Clip( OperationType.GPC_UNION, p1, p2, typeof(PolyDefault) ); }
/// <summary> /// Recursively process the polygon to attempt to find a /// single polygon element that could be added to the physics /// layer without any holes or additional physics. /// </summary> private void CreateJunctionPhysics( int depth, IPoly poly, RectangleF bounds) { // Ignore empty polygons if (poly.InnerPolygonCount == 0) return; // See if we are a solid polygon double areaDifference = bounds.Width * bounds.Height - poly.Area; if (poly.InnerPolygonCount == 1) { if (poly.IsHole()) // Don't add holes return; if (poly.PointCount == 4 && areaDifference <= 0.1f) // We appear to be at least mostly solid, drop it return; } // If we have more than one polygon, split it if (poly.InnerPolygonCount > 1 || bounds.Width > Constants.MaximumJunctionPhysicsBlock || bounds.Height > Constants.MaximumJunctionPhysicsBlock) { // We split the polygon into quads and process each // one to add it recursively. CreateJunctionPhysics(depth + 1, poly, bounds, 0, 0); CreateJunctionPhysics(depth + 1, poly, bounds, 0, 1); CreateJunctionPhysics(depth + 1, poly, bounds, 1, 1); CreateJunctionPhysics(depth + 1, poly, bounds, 1, 0); return; } // We should never get a hole if (poly.IsHole()) { // We shouldn't get this Log.Error("Got a top-level polygon hole"); return; } // Create a polygon shape as vectors LinkedList<Vector2D> vectors = new LinkedList<Vector2D>(); for (int i = 0; i < poly.PointCount; i++) { // Get the coordinates float x = (float) poly.GetX(i); float y = (float) poly.GetY(i); // Create the vector vectors.Add(new Vector2D(x, y)); } // Convert it into a physics2d polygon shape. Making the // PolygonShape second parameter too small makes the game // basically unusable in terms of stage generation but // more accurate for impacts with the side. Vector2D [] array = vectors.ToArray(); IShape ps = new PolygonShape(array, 5f); physicsShapes.Add(ps); }
/** * Return a IPoly that is the union of this polygon with the given polygon. * The returned polygon could be complex. * * @return the returned IPoly will be an instance of PolyDefault. */ public IPoly Union(IPoly p) { return Clipper.Union( p, this, GetType() ); }
/// <summary> /// Returns true if the two polygons intersect. /// </summary> public bool HasIntersection(IPoly p) { // Get the intersection IPoly intersection = Intersection(p); return intersection.PointCount != 0; }
/** * Add an inner polygon to this polygon - assumes that adding polygon does not * have any inner polygons. * * @throws IllegalStateException if the number of inner polygons is greater than * zero and this polygon was designated a hole. This would break the assumption * that only simple polygons can be holes. */ public void Add( IPoly p ) { if( (m_List.Count > 0) && isHole ) { throw new Exception("Cannot add polys to something designated as a hole."); } m_List.Add( p ); }
private void Reset() { Holes = new List<IPoly>(); Outline = null; }
/** * Return the difference of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyType</code>. See the note in the class description * for more on <ocde>polyType</code>. * * @param p1 One of the polygons to performt he intersection with * @param p2 One of the polygons to performt he intersection with * @param polyType The type of <code>Poly</code> to return */ public static IPoly Difference( IPoly p1, IPoly p2, Type polyType ) { return Clip( OperationType.GPC_DIFF, p1, p2, polyType ); }
/** * Return the difference of <code>p1</code> and <code>p2</code> where the * return type is of <code>PolyDefault</code>. * * @param p1 One of the polygons to performt he intersection with * @param p2 One of the polygons to performt he intersection with */ public static IPoly Difference( IPoly p1, IPoly p2 ) { return Clip( OperationType.GPC_DIFF, p1, p2, typeof(PolyDefault) ); }
/// <summary> /// Resets the junction node's status, including purging all /// generated data and resetting to a known seed value. /// </summary> public void Reset() { // Reset the flags builtShape = false; builtConnections = false; // Purge the stored data internalShape = null; shape = null; segments.Clear(); physicsShapes.Clear(); // Reset the random value random = new MersenneRandom(randomSeed); }
private static RectangleF[] create_contour_bboxes( IPoly p ) { RectangleF[] box = new RectangleF[p.InnerPolygonCount] ; /* Construct contour bounding boxes */ for ( int c= 0; c < p.InnerPolygonCount; c++) { IPoly inner_poly = p.GetInnerPoly(c); box[c] = inner_poly.Bounds; } return box; }
/// <summary> /// Splits apart a polygon into a quad and processes it for physics. /// </summary> private void CreateJunctionPhysics( int depth, IPoly poly, RectangleF bounds, int row, int col) { // Figure out the desired size and shape SizeF size = new SizeF(bounds.Width / 2, bounds.Height / 2); PointF point = new PointF( bounds.X + col * bounds.Width / 2, bounds.Y + row * bounds.Height / 2); RectangleF rect = new RectangleF(point, size); // Create the GPC polygon and calculate the intersection IPoly rectangle = Geometry.CreateRectangle(rect); IPoly intersection = rectangle.Intersection(poly); // Process this one CreateJunctionPhysics(depth, intersection, rect); }
/** * Return the intersection of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyType</code>. See the note in the class description * for more on <ocde>polyType</code>. * * @param p1 One of the polygons to performt he intersection with * @param p2 One of the polygons to performt he intersection with * @param polyType The type of <code>Poly</code> to return */ public static IPoly Intersection( IPoly p1, IPoly p2, Type polyType ) { return Clip( OperationType.GPC_INT, p1, p2, polyType ); }
private static IPoly CreatePoly(IPoly poly, List<IPoly> holes) { if (holes.Count > 0) { return new ComplexPoly(poly, holes.ToArray()); } else { return poly; } }
private static void minimax_test( IPoly subj, IPoly clip, OperationType op ) { RectangleF[] s_bbox = create_contour_bboxes(subj); RectangleF[] c_bbox = create_contour_bboxes(clip); int subj_num_poly = subj.InnerPolygonCount; int clip_num_poly = clip.InnerPolygonCount; bool[,] o_table = new bool[subj_num_poly,clip_num_poly] ; /* Check all subject contour bounding boxes against clip boxes */ for( int s = 0; s < subj_num_poly; s++ ) { for( int c= 0; c < clip_num_poly ; c++ ) { o_table[s, c] = (!((s_bbox[s].Right < c_bbox[c].Left) || (s_bbox[s].Left > c_bbox[c].Right))) && (!((s_bbox[s].Bottom < c_bbox[c].Top) || (s_bbox[s].Top > c_bbox[c].Bottom))); } } /* For each clip contour, search for any subject contour overlaps */ for( int c = 0; c < clip_num_poly; c++ ) { bool overlap = false; for( int s = 0; !overlap && (s < subj_num_poly) ; s++) { overlap = o_table[s, c]; } if (!overlap) { clip.SetContributing( c, false ); // Flag non contributing status } } if (op == OperationType.GPC_INT) { /* For each subject contour, search for any clip contour overlaps */ for ( int s= 0; s < subj_num_poly; s++) { bool overlap = false; for ( int c= 0; !overlap && (c < clip_num_poly); c++) { overlap = o_table[s, c]; } if (!overlap) { subj.SetContributing( s, false ); // Flag non contributing status } } } }
/** * Return a IPoly that is the difference of this polygon with the given polygon. * The returned polygon could be complex. * * @return the returned IPoly will be an instance of PolyDefault. */ public IPoly Difference(IPoly p) { return Clipper.Difference( p, this, GetType() ); }
private static bool OPTIMAL( IPoly p, int i ) { return (p.GetY(PREV_INDEX(i, p.PointCount)) != p.GetY(i)) || (p.GetY(NEXT_INDEX(i, p.PointCount)) != p.GetY(i)) ; }
/** * Return a IPoly that is the intersection of this polygon with the given polygon. * The returned polygon could be complex. * * @return the returned IPoly will be an instance of PolyDefault. */ public IPoly Intersection(IPoly p) { return Clipper.Intersection( p, this, GetType() ); }
/** * Return the intersection of <code>p1</code> and <code>p2</code> where the * return type is of <code>PolyDefault</code>. * * @param p1 One of the polygons to performt he intersection with * @param p2 One of the polygons to performt he intersection with */ public static IPoly Intersection( IPoly p1, IPoly p2 ) { return Clip( OperationType.GPC_INT, p1, p2, typeof(PolyDefault) ); }
/** * Return a IPoly that is the exclusive-or of this polygon with the given polygon. * The returned polygon could be complex. * * @return the returned IPoly will be an instance of PolyDefault. */ public IPoly Xor(IPoly p) { return Clipper.Xor( p, this, GetType() ); }
/** * Return the union of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyType</code>. See the note in the class description * for more on <ocde>polyType</code>. * * @param p1 One of the polygons to performt he union with * @param p2 One of the polygons to performt he union with * @param polyType The type of <code>Poly</code> to return */ public static IPoly Union( IPoly p1, IPoly p2, Type polyType ) { return Clip( OperationType.GPC_UNION, p1, p2, polyType ); }
private IPoly merge(IPoly poly1, IPoly poly2) { IPoly merged; Assert.True(poly1.TryMerge(poly2, out merged), "Should succeed with merge"); return merged; }
/// <summary> /// Renders an arbitrary polygon to the context. /// </summary> private void RenderPolygon( Context g, IPoly poly, PointF point, Color color) { // Save the first point PointF firstPoint = PointF.Empty; // Go through the points of the polygon for (int i = 0; i < poly.PointCount; i++) { // Pull out the coordinates float x = (float) poly.GetX(i); float y = (float) poly.GetY(i); PointF p = new PointF(cx + point.X + x, cy + point.Y + y); // Either move or line if (firstPoint == PointF.Empty) { firstPoint = p; g.MoveTo(p.X, p.Y); } else { g.LineTo(p.X, p.Y); } } // Finish up g.LineTo(firstPoint.X, firstPoint.Y); g.Color = color; g.Fill(); }