/// <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 ; }