(* =========================================================================== Project: Generic Polygon Clipper A new algorithm for calculating the difference, intersection, exclusive-or or union of arbitrary polygon sets. File: gpc.pas Author: Alan Murta (gpc@cs.man.ac.uk) Version: 2.30 Date: 17th October 1998 Pascal port by: Stefan Schedel (Stefan.Schedel@loewe.de) Copyright: (C) 1997, Advanced Interfaces Group, University of Manchester. All rights reserved. This software may be freely copied, modified, and redistributed provided that this copyright notice is preserved on all copies. The intellectual property rights of the algorithms used reside with the University of Manchester Advanced Interfaces Group. You may not distribute this software, in whole or in part, as part of any commercial product without the express consent of the author. There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is". =========================================================================== *) unit GPC; interface uses Windows; //=========================================================================== // Constants //=========================================================================== const Version = 'GPC_VERSION "2.30"'; GPC_EPSILON : double = 2.2204460492503131E-16; { from float.h } //=========================================================================== // Public Data Types //=========================================================================== type Tgpc_op = { Set operation type } ( GPC_DIFF, { Difference } GPC_INT, { Intersection } GPC_XOR, { Exclusive or } GPC_UNION { Union } ); Tgpc_vertex = record { Polygon vertex structure } x : double; { Vertex x component } y : double; { vertex y component } end; Pgpc_vertex_array = ^Tgpc_vertex_array; { Helper Type for indexing } Tgpc_vertex_array = array[0.. MaxInt div sizeof(Tgpc_vertex) - 1] of Tgpc_vertex; Pgpc_vertex_list = ^Tgpc_vertex_list; { Vertex list structure } Tgpc_vertex_list = record num_vertices : integer; { Number of vertices in list } vertex : Pgpc_vertex_array; { Vertex array pointer } end; PIntegerArray = ^TIntegerArray; TIntegerArray = array[0..MaxInt div sizeof(Integer) - 1] of Integer; Pgpc_vertex_list_array = ^Tgpc_vertex_list_array; { Helper Type for indexing } Tgpc_vertex_list_array = array[0.. MaxInt div sizeof(Tgpc_vertex) - 1] of Tgpc_vertex_list; Pgpc_polygon = ^Tgpc_polygon; Tgpc_polygon = record { Polygon set structure } num_contours : integer; { Number of contours in polygon } hole : PIntegerArray; { Hole / external contour flags } contour : Pgpc_vertex_list_array; { Contour array pointer } end; Pgpc_tristrip = ^Tgpc_tristrip; { Tristrip set structure } Tgpc_tristrip = record num_strips : integer; { Number of tristrips } strip : Pgpc_vertex_list_array; { Tristrip array pointer } end; //=========================================================================== // Public Function Prototypes //=========================================================================== procedure gpc_read_polygon (var f : text; p : Pgpc_polygon); procedure gpc_write_polygon (var f : text; p : Pgpc_polygon); procedure gpc_add_contour (polygon : Pgpc_polygon; contour : Pgpc_vertex_list; hole : integer); procedure gpc_polygon_clip (set_operation : Tgpc_op; subject_polygon : Pgpc_polygon; clip_polygon : Pgpc_polygon; result_polygon : Pgpc_polygon); procedure gpc_free_polygon (polygon : Pgpc_polygon); procedure gpc_free_tristrip (tristrip : Pgpc_tristrip); implementation uses SysUtils, Math; //=========================================================================== // Constants //=========================================================================== const DBL_MAX : double = MaxDouble; DBL_DIG = 15; FFALSE = 0; FTRUE = 1; LEFT = 0; RIGHT = 1; ABOVE = 0; BELOW = 1; CLIP = 0; SUBJ = 1; INVERT_TRISTRIPS = FALSE; //=========================================================================== // Private Data Types //=========================================================================== type Tvertex_type = ( { Edge intersection classes } NUL, { Empty non-intersection } EMX, { External maximum } ELI, { External left intermediate } TED, { Top edge } ERI, { External right intermediate } RED, { Right edge } IMM, { Internal maximum and minimum } IMN, { Internal minimum } EMN, { External minimum } EMM, { External maximum and minimum } LED, { Left edge } ILI, { Internal left intermediate } BED, { Bottom edge } IRI, { Internal right intermediate } IMX, { Internal maximum } FUL { Full non-intersection } ); Th_state = { Horizontal edge states } ( NH, { No horizontal edge } BH, { Bottom horizontal edge } TH { Top horizontal edge } ); Tbundle_state = ( UNBUNDLED, BUNDLE_HEAD, BUNDLE_TAIL ); PPvertex_node = ^Pvertex_node; Pvertex_node = ^Tvertex_node; { Internal vertex list datatype } Tvertex_node = record x : double; { X coordinate component } y : double; { Y coordinate component } next : Pvertex_node; { Pointer to next vertex in list } end; Pvertex_node_array = ^Tvertex_node_array; { Helper type for indexing } Tvertex_node_array = array[0..1] of Pvertex_node; PPpolygon_node = ^Ppolygon_node; Ppolygon_node = ^Tpolygon_node; Tpolygon_node = record active : integer; hole : integer; v : array[0..1] of Pvertex_node; Next : Ppolygon_node; proxy : Ppolygon_node; end; PPedge_node = ^Pedge_node; Pedge_node = ^Tedge_node; Tedge_node = record vertex : Tgpc_vertex; { Piggy-backed contour vertex data } bot : Tgpc_vertex; { Edge lower (x, y) coordinate } top : Tgpc_vertex; { Edge upper (x, y) coordinate } xb : double; { Scanbeam bottom x coordinate } xt : double; { Scanbeam top x coordinate } dx : double; { Change in x for a unit y increase } typ : integer; { Clip / subject edge flag } bundle : array[0..1, 0..1] of integer;{ Bundle edge flags } bside : array[0..1] of integer; { Bundle left / right indicators } bstate : array[0..1] of Tbundle_state;{ Edge bundle state } outp : array[0..1] of Ppolygon_node;{ Output polygon / tristrip pointer } prev : Pedge_node; { Previous edge in the AET } next : Pedge_node; { Next edge in the AET } pred : Pedge_node; { Edge connected at the lower end } succ : Pedge_node; { Edge connected at the upper end } next_bound : Pedge_node; { Pointer to next bound in LMT } end; PPedge_node_array = ^Pedge_node_array; Pedge_node_array = ^Tedge_node_array; Tedge_node_array = array[0..MaxInt div sizeof(Tedge_node) - 1] of Tedge_node; PPlmt_node = ^Plmt_node; Plmt_node = ^Tlmt_node; Tlmt_node = record { Local minima table } y : double; { Y coordinate at local minimum } first_bound: Pedge_node; { Pointer to bound list } next : Plmt_node; { Pointer to next local minimum } end; PPsb_tree = ^Psb_tree; Psb_tree = ^Tsb_tree; Tsb_tree = record { Scanbeam tree } y : double; { Scanbeam node y value } less : Psb_tree; { Pointer to nodes with lower y } more : Psb_tree; { Pointer to nodes with higher y } end; PPit_node = ^Pit_node; Pit_node = ^Tit_node; { Intersection table } Tit_node = record ie : array[0..1] of Pedge_node;{ Intersecting edge (bundle) pair } point : Tgpc_vertex; { Point of intersection } next : Pit_node; { The next intersection table node } end; PPst_node = ^Pst_node; Pst_node = ^Tst_node; { Sorted edge table } Tst_node = record edge : Pedge_node; { Pointer to AET edge } xb : double; { Scanbeam bottom x coordinate } xt : double; { Scanbeam top x coordinate } dx : double; { Change in x for a unit y increase } prev : Pst_node; { Previous edge in sorted list } end; Pbbox = ^Tbbox; Tbbox = record { Contour axis-aligned bounding box } xmin : double; { Minimum x coordinate } ymin : double; { Minimum y coordinate } xmax : double; { Maximum x coordinate } ymax : double; { Maximum y coordinate } end; PbboxArray = ^TbboxArray; TbboxArray = array[0..MaxInt div sizeof(Tbbox) - 1] of Tbbox; PDoubleArray = ^TDoubleArray; TDoubleArray = array[0..MaxInt div sizeof(double) - 1] of double; //=========================================================================== // C Macros, defined as function for PASCAL //=========================================================================== function EQ(a, b : double) : boolean; begin EQ := abs(a - b) <= gpc_epsilon end; function NE(a, b : double) : boolean; begin NE := abs(a - b) > gpc_epsilon end; function GT(a, b : double) : boolean; begin GT := (a - b) > gpc_epsilon end; function LT(a, b : double) : boolean; begin LT := (b - a) > gpc_epsilon end; function GE(a, b : double) : boolean; begin GE := not LT(a, b) end; function LE(a, b : double) : boolean; begin LE := not GT(a, b) end; function PREV_INDEX(i, n : integer) : integer; begin PREV_INDEX := ((i - 1 + n) mod n); end; function NEXT_INDEX(i, n : integer) : integer; begin NEXT_INDEX := ((i + 1) mod n); end; function OPTIMAL(v : Pgpc_vertex_array; i, n : integer) : boolean; begin OPTIMAL := NE(v[PREV_INDEX(i, n)].y, v[i].y) or NE(v[NEXT_INDEX(i, n)].y, v[i].y); end; function FWD_MIN(v : Pedge_node_array; i, n : integer) : boolean; begin FWD_MIN := GE(v[PREV_INDEX(i, n)].vertex.y, v[i].vertex.y) and GT(v[NEXT_INDEX(i, n)].vertex.y, v[i].vertex.y); end; function NOT_FMAX(v : Pedge_node_array; i, n : integer) : boolean; begin NOT_FMAX := GT(v[NEXT_INDEX(i, n)].vertex.y, v[i].vertex.y); end; function REV_MIN(v : Pedge_node_array; i, n : integer) : boolean; begin REV_MIN := GT(v[PREV_INDEX(i, n)].vertex.y, v[i].vertex.y) and GE(v[NEXT_INDEX(i, n)].vertex.y, v[i].vertex.y); end; function NOT_RMAX(v : Pedge_node_array; i, n : integer) : boolean; begin NOT_RMAX := GT(v[PREV_INDEX(i, n)].vertex.y, v[i].vertex.y); end; procedure MALLOC(var p : pointer; b : integer; s : string); begin GetMem(p, b); if (p = nil) and (b <> 0) then raise Exception.Create(s); end; procedure add_vertex(var p : Pvertex_node; x, y : double); begin if p = nil then begin MALLOC(pointer(p), sizeof(Tvertex_node), 'tristrip vertex creation'); p.x := x; p.y := y; p.next := nil; end else { Head further down the list } add_vertex(p.next, x, y); end; procedure VERTEX(var e : Pedge_node; p, s : integer; var x, y : double); begin add_vertex(e.outp[p].v[s], x, y); Inc(e.outp[p].active); end; procedure P_EDGE(var d, e : Pedge_node; p : integer; var i, j : double); begin d := e; repeat d := d.prev until d.outp[p] = nil; i := d.bot.x + d.dx * (j - d.bot.y); end; procedure N_EDGE(var d, e : Pedge_node; p : integer; var i, j : double); begin d := e; repeat d := d.next; until d.outp[p] = nil; i := d.bot.x + d.dx * (j - d.bot.y); end; procedure Free(var p : pointer); begin FreeMem(p); p := nil; end; procedure CFree(var p : pointer); begin if p <> nil then Free(p); end; //=========================================================================== // Global Data //=========================================================================== { Horizontal edge state transitions within scanbeam boundary } const next_h_state : array[0..2, 0..5] of Th_state = { ABOVE BELOW CROSS } { L R L R L R } { NH } ((BH, TH, TH, BH, NH, NH), { BH } (NH, NH, NH, NH, TH, TH), { TH } (NH, NH, NH, NH, BH, BH)); //=========================================================================== // Private Functions //=========================================================================== procedure reset_it(var it : Pit_node); var itn : Pit_node; begin while (it <> nil) do begin itn := it.next; Free(pointer(it)); it := itn; end; end; procedure reset_lmt(var lmt : Plmt_node); var lmtn : Plmt_node; begin while lmt <> nil do begin lmtn := lmt^.next; Free(pointer(lmt)); lmt := lmtn; end; end; procedure insert_bound(b : PPedge_node_array; e : Pedge_node_array); var existing_bound : pointer; begin if b^ = nil then begin { Link node e to the tail of the list } b^ := e; end else begin { Do primary sort on the x field } if (LT(e[0].bot.x, b^[0].bot.x)) then begin { Insert a new node mid-list } existing_bound := b^; b^ := e; b^[0].next_bound := existing_bound; end else begin if (EQ(e[0].bot.x, b^[0].bot.x)) then begin { Do secondary sort on the dx field } if (LT(e[0].dx, b^[0].dx)) then begin { Insert a new node mid-list } existing_bound := b^; b^ := e; b^[0].next_bound := existing_bound; end else begin { Head further down the list } insert_bound(@(b^[0].next_bound), e); end; end else begin { Head further down the list } insert_bound(@(b^[0].next_bound), e); end; end; end; end; function bound_list(var lmt : Plmt_node; y : double) : PPedge_node_array; var existing_node : Plmt_node; begin if lmt = nil then begin { Add node onto the tail end of the LMT } MALLOC(pointer(lmt), sizeof(Tlmt_node), 'LMT insertion'); lmt.y := y; lmt.first_bound := nil; lmt.next := nil; result := @lmt.first_bound; end else if LT(y, lmt.y) then begin { Insert a new LMT node before the current node } existing_node := lmt; MALLOC(pointer(lmt), sizeof(Tlmt_node), 'LMT insertion'); lmt.y := y; lmt.first_bound := nil; lmt.next := existing_node; result := @lmt.first_bound; end else if EQ(y, lmt.y) then { Use this existing LMT node } Result := @lmt.first_bound else { Head further up the LMT } Result := bound_list(lmt.next, y); end; procedure add_to_sbtree(var entries : integer; var sbtree : Psb_tree; var y : double); begin if sbtree = nil then begin { Add a new tree node here } MALLOC(pointer(sbtree), sizeof(Tsb_tree), 'scanbeam tree insertion'); sbtree.y := y; sbtree.less := nil; sbtree.more := nil; Inc(entries); end else begin if GT(sbtree.y, y) then begin { Head into the 'less' sub-tree } add_to_sbtree(entries, sbtree.less, y); end else begin if LT(sbtree.y, y) then begin { Head into the 'more' sub-tree } add_to_sbtree(entries, sbtree.more, y); end; end; end; end; procedure build_sbt(var entries : integer; var sbt : TDoubleArray; sbtree : Psb_tree); begin if sbtree.less <> nil then build_sbt(entries, sbt, sbtree.less); sbt[entries] := sbtree.y; Inc(entries); if sbtree.more <> nil then build_sbt(entries, sbt, sbtree.more); end; procedure free_sbtree(var sbtree : Psb_tree); begin if sbtree <> nil then begin free_sbtree(sbtree.less); free_sbtree(sbtree.more); Free(pointer(sbtree)); end; end; function count_optimal_vertices(c : Tgpc_vertex_list) : integer; var i : integer; begin Result := 0; { Ignore non-contributing contours } if c.num_vertices > 0 then begin for i := 0 to c.num_vertices - 1 do { Ignore superfluous vertices embedded in horizontal edges } if OPTIMAL(c.vertex, i, c.num_vertices) then Inc(Result); end; end; function build_lmt(var lmt : Plmt_node; var sbtree : Psb_tree; var sbt_entries : integer; p : Pgpc_polygon; typ : integer; op : Tgpc_op) : Pedge_node_array; var c, i, min, max, num_edges, v, num_vertices : integer; total_vertices, e_index : integer; e, edge_table : Pedge_node_array; begin total_vertices := 0; e_index := 0; for c := 0 to p.num_contours - 1 do Inc(total_vertices, count_optimal_vertices(p.contour[c])); { Create the entire input polygon edge table in one go } MALLOC(pointer(edge_table), total_vertices * sizeof(Tedge_node), 'edge table creation'); for c := 0 to p.num_contours - 1 do begin if p.contour[c].num_vertices < 0 then begin { Ignore the non-contributing contour and repair the vertex count } p.contour[c].num_vertices := -p.contour[c].num_vertices; end else begin { Perform contour optimisation } num_vertices := 0; for i := 0 to p.contour[c].num_vertices - 1 do if (OPTIMAL(p.contour[c].vertex, i, p.contour[c].num_vertices)) then begin edge_table[num_vertices].vertex.x := p.contour[c].vertex[i].x; edge_table[num_vertices].vertex.y := p.contour[c].vertex[i].y; { Record vertex in the scanbeam table } add_to_sbtree(sbt_entries, sbtree, edge_table[num_vertices].vertex.y); Inc(num_vertices); end; { Do the contour forward pass } for min := 0 to num_vertices - 1 do begin { If a forward local minimum... } if FWD_MIN(edge_table, min, num_vertices) then begin { Search for the next local maximum... } num_edges := 1; max := NEXT_INDEX(min, num_vertices); while (NOT_FMAX(edge_table, max, num_vertices)) do begin Inc(num_edges); max := NEXT_INDEX(max, num_vertices); end; { Build the next edge list } e := @edge_table[e_index]; Inc(e_index, num_edges); v := min; e[0].bstate[BELOW] := UNBUNDLED; e[0].bundle[BELOW][CLIP] := FFALSE; e[0].bundle[BELOW][SUBJ] := FFALSE; for i := 0 to num_edges - 1 do begin e[i].xb := edge_table[v].vertex.x; e[i].bot.x := edge_table[v].vertex.x; e[i].bot.y := edge_table[v].vertex.y; v := NEXT_INDEX(v, num_vertices); e[i].top.x := edge_table[v].vertex.x; e[i].top.y := edge_table[v].vertex.y; e[i].dx := (edge_table[v].vertex.x - e[i].bot.x) / (e[i].top.y - e[i].bot.y); e[i].typ := typ; e[i].outp[ABOVE] := nil; e[i].outp[BELOW] := nil; e[i].next := nil; e[i].prev := nil; if (num_edges > 1) and (i < (num_edges - 1)) then e[i].succ := @e[i + 1] else e[i].succ := nil; if (num_edges > 1) and (i > 0) then e[i].pred := @e[i - 1] else e[i].pred := nil; e[i].next_bound := nil; if op = GPC_DIFF then e[i].bside[CLIP] := RIGHT else e[i].bside[CLIP] := LEFT; e[i].bside[SUBJ] := LEFT; end; insert_bound(bound_list(lmt, edge_table[min].vertex.y), e); end; end; { Do the contour reverse pass } for min := 0 to num_vertices - 1 do begin { If a reverse local minimum... } if REV_MIN(edge_table, min, num_vertices) then begin { Search for the previous local maximum... } num_edges := 1; max := PREV_INDEX(min, num_vertices); while NOT_RMAX(edge_table, max, num_vertices) do begin Inc(num_edges); max := PREV_INDEX(max, num_vertices); end; { Build the previous edge list } e := @edge_table[e_index]; Inc(e_index, num_edges); v := min; e[0].bstate[BELOW] := UNBUNDLED; e[0].bundle[BELOW][CLIP] := FFALSE; e[0].bundle[BELOW][SUBJ] := FFALSE; for i := 0 to num_edges - 1 do begin e[i].xb := edge_table[v].vertex.x; e[i].bot.x := edge_table[v].vertex.x; e[i].bot.y := edge_table[v].vertex.y; v := PREV_INDEX(v, num_vertices); e[i].top.x := edge_table[v].vertex.x; e[i].top.y := edge_table[v].vertex.y; e[i].dx := (edge_table[v].vertex.x - e[i].bot.x) / (e[i].top.y - e[i].bot.y); e[i].typ := typ; e[i].outp[ABOVE] := nil; e[i].outp[BELOW] := nil; e[i].next := nil; e[i].prev := nil; if (num_edges > 1) and (i < (num_edges - 1)) then e[i].succ := @e[i + 1] else e[i].succ := nil; if (num_edges > 1) and (i > 0) then e[i].pred := @e[i - 1] else e[i].pred := nil; e[i].next_bound := nil; if op = GPC_DIFF then e[i].bside[CLIP] := RIGHT else e[i].bside[CLIP] := LEFT; e[i].bside[SUBJ] := LEFT; end; insert_bound(bound_list(lmt, edge_table[min].vertex.y), e); end; end; end; end; Result := edge_table; end; procedure add_edge_to_aet(var aet : Pedge_node; edge : Pedge_node; prev : Pedge_node); begin if aet = nil then begin { Append edge onto the tail end of the AET } aet := edge; edge.prev := prev; edge.next := nil; end else begin { Do primary sort on the xb field } if LT(edge.xb, aet.xb) then begin { Insert edge here (before the AET edge) } edge.prev := prev; edge.next := aet; aet.prev := edge; aet := edge; end else begin if EQ(edge.xb, aet.xb) then begin { Do secondary sort on the dx field } if LT(edge.dx, aet.dx) then begin { Insert edge here (before the AET edge) } edge.prev := prev; edge.next := aet; aet.prev := edge; aet := edge; end else begin { Head further into the AET } add_edge_to_aet(aet.next, edge, aet); end; end else begin { Head further into the AET } add_edge_to_aet(aet.next, edge, aet); end; end; end; end; procedure add_intersection(var it : Pit_node; edge0, edge1 : Pedge_node; x, y : double); var existing_node : Pit_node; begin if it = nil then begin { Append a new node to the tail of the list } MALLOC(pointer(it), sizeof(Tit_node), 'IT insertion'); it.ie[0] := edge0; it.ie[1] := edge1; it.point.x := x; it.point.y := y; it.next := nil; end else begin if GT(it.point.y, y) then begin { Insert a new node mid-list } existing_node := it; MALLOC(pointer(it), sizeof(Tit_node), 'IT insertion'); it.ie[0] := edge0; it.ie[1] := edge1; it.point.x := x; it.point.y := y; it.next := existing_node; end else { Head further down the list } add_intersection(it.next, edge0, edge1, x, y); end; end; procedure add_st_edge(var st : Pst_node; var it : Pit_node; edge : Pedge_node; dy : double); var existing_node : Pst_node; den, x, y, r : double; begin if st = nil then begin { Append edge onto the tail end of the ST } MALLOC(pointer(st), sizeof(Tst_node), 'ST insertion'); st.edge := edge; st.xb := edge.xb; st.xt := edge.xt; st.dx := edge.dx; st.prev := nil; end else begin den := (st.xt - st.xb) - (edge.xt - edge.xb); { If new edge and ST edge don't cross } if (GE(edge.xt, st.xt) or EQ(edge.dx, st.dx) or EQ(den, 0.0)) then begin { No intersection - insert edge here (before the ST edge) } existing_node := st; MALLOC(pointer(st), sizeof(Tst_node), 'ST insertion'); st.edge := edge; st.xb := edge.xb; st.xt := edge.xt; st.dx := edge.dx; st.prev := existing_node; end else begin { Compute intersection between new edge and ST edge } r := (edge.xb - st.xb) / den; x := st.xb + r * (st.xt - st.xb); y := r * dy; { Insert the edge pointers and the intersection point in the IT } add_intersection(it, st.edge, edge, x, y); { Head further into the ST } add_st_edge(st.prev, it, edge, dy); end; end; end; procedure build_intersection_table(var it : Pit_node; aet : Pedge_node; dy : double); var st, stp : Pst_node; edge : Pedge_node; begin { Build intersection table for the current scanbeam } reset_it(it); st := nil; { Process each AET edge } edge := aet; while edge <> nil do begin if (edge.bstate[ABOVE] = BUNDLE_HEAD) or (edge.bundle[ABOVE][CLIP] <> 0) or (edge.bundle[ABOVE][SUBJ] <> 0) then add_st_edge(st, it, edge, dy); edge := edge.next; end; { Free the sorted edge table } while st <> nil do begin stp := st.prev; Free(pointer(st)); st := stp; end; end; function count_contours(polygon : Ppolygon_node) : integer; var nv : integer; v, nextv : Pvertex_node; begin Result := 0; while polygon <> nil do begin if polygon.active <> 0 then begin { Count the vertices in the current contour } nv := 0; v := polygon.proxy.v[LEFT]; while v <> nil do begin Inc(nv); v := v.next; end; { Record valid vertex counts in the active field } if (nv > 2) then begin polygon.active := nv; Inc(Result); end else begin { Invalid contour: just free the heap } v := polygon.proxy.v[LEFT]; while v <> nil do begin nextv := v.next; FREE(pointer(v)); v := nextv; end; polygon.active := 0; end; end; polygon := polygon.next; end; end; procedure add_left(p : Ppolygon_node; x, y : double); var nv : Pvertex_node; begin { Create a new vertex node and set its fields } MALLOC(pointer(nv), sizeof(Tvertex_node), 'vertex node creation'); nv.x := x; nv.y := y; { Add vertex nv to the left end of the polygon's vertex list } nv.next := P.proxy.v[LEFT]; { Update proxy[LEFT] to point to nv } P.proxy.v[LEFT] := nv; end; procedure merge_left(P : Ppolygon_node; Q :Ppolygon_node; list : Ppolygon_node); var target : Ppolygon_node; begin { Label contour as a hole } q.proxy.hole := FTRUE; if P.proxy <> Q.proxy then begin { Assign P's vertex list to the left end of Q's list } P.proxy.v[RIGHT].next := Q.proxy.v[LEFT]; Q.proxy.v[LEFT] := P.proxy.v[LEFT]; { Redirect any P->proxy references to Q->proxy } target := P.proxy; while list <> nil do begin if list.proxy = target then begin list.active := FFALSE; list.proxy := Q.proxy; end; list := list.next; end; end; end; procedure add_right(P : Ppolygon_node; x, y : double); var nv : Pvertex_node; begin { Create a new vertex node and set its fields } MALLOC(pointer(nv), sizeof(Tvertex_node), 'vertex node creation'); nv.x := x; nv.y := y; nv.next := nil; { Add vertex nv to the right end of the polygon's vertex list } P.proxy.v[RIGHT].next := nv; { Update proxy.v[RIGHT] to point to nv } P.proxy.v[RIGHT] := nv; end; procedure merge_right(P : Ppolygon_node; Q : Ppolygon_node; list : Ppolygon_node); var target : PPolygon_node; begin { Label contour as external } Q.proxy.hole := FFALSE; if P.proxy <> Q.proxy then begin { Assign P's vertex list to the right end of Q's list } Q.proxy.v[RIGHT].next := P.proxy.v[LEFT]; Q.proxy.v[RIGHT] := P.proxy.v[RIGHT]; { Redirect any P->proxy references to Q->proxy } target := P.proxy; while list <> nil do begin if list.proxy = target then begin list.active := FFALSE; list.proxy := Q.proxy; end; list := list.next; end; end; end; procedure add_local_min(P : PPpolygon_node; edge : Pedge_node; x, y : double); var nv : Pvertex_node; existing_min : Ppolygon_node; begin existing_min := p^; MALLOC(pointer(P^), sizeof(Tpolygon_node), 'polygon node creation'); { Create a new vertex node and set its fields } MALLOC(pointer(nv), sizeof(Tvertex_node), 'vertex node creation'); nv.x := x; nv.y := y; nv.next := nil; { Initialise proxy to point to p itself } p^.proxy := P^; p^.active := FTRUE; p^.next := existing_min; { Make v[LEFT] and v[RIGHT] point to new vertex nv } p^.v[LEFT] := nv; p^.v[RIGHT] := nv; { Assign polygon p to the edge } edge.outp[ABOVE] := p^; end; function count_tristrips(tn : Ppolygon_node) : integer; begin Result := 0; while tn <> nil do begin if tn.active > 2 then Inc(Result); tn := tn.next; end; end; (* procedure add_vertex(t : PPvertex_node; x, y : double) begin if t^ <> nil then begin MALLOC(t^, sizeof(Tvertex_node), ttristrip vertex creationt'); t^.x := x; t^.y := y; t^.next := nil; end else { Head further down the list } add_vertex(@t^.next, x, y); end; *) procedure new_tristrip(var tn : Ppolygon_node; edge : Pedge_node; x, y : double); begin if tn = nil then begin MALLOC(pointer(tn), sizeof(Tpolygon_node), 'tristrip node creation'); tn.next := nil; tn.v[LEFT] := nil; tn.v[RIGHT] := nil; tn.active := 1; add_vertex(tn.v[LEFT], x, y); edge.outp[ABOVE] := tn; end else { Head further down the list } new_tristrip(tn.next, edge, x, y); end; function create_contour_bboxes(p : Pgpc_polygon) : PbboxArray; var c, v : integer; begin MALLOC(pointer(Result), p.num_contours * sizeof(Tbbox), 'Bounding box creation'); { Construct contour bounding boxes } for c := 0 to p.num_contours - 1 do begin { Initialise bounding box extent } Result[c].xmin := DBL_MAX; Result[c].ymin := DBL_MAX; Result[c].xmax := -DBL_MAX; Result[c].ymax := -DBL_MAX; for v := 0 to p.contour[c].num_vertices - 1 do begin { Adjust bounding Result } if (p.contour[c].vertex[v].x < Result[c].xmin) then Result[c].xmin := p.contour[c].vertex[v].x; if (p.contour[c].vertex[v].y < Result[c].ymin) then Result[c].ymin := p.contour[c].vertex[v].y; if (p.contour[c].vertex[v].x > Result[c].xmax) then Result[c].xmax := p.contour[c].vertex[v].x; if (p.contour[c].vertex[v].y > Result[c].ymax) then Result[c].ymax := p.contour[c].vertex[v].y; end; end; end; procedure minimax_test(subj : Pgpc_polygon; clip : Pgpc_polygon; op : Tgpc_op); var s_bbox, c_bbox : PbboxArray; s, c : integer; o_table : PIntegerArray; overlap : integer; begin s_bbox := create_contour_bboxes(subj); c_bbox := create_contour_bboxes(clip); MALLOC(pointer(o_table), subj.num_contours * clip.num_contours * sizeof(Integer), 'overlap table creation'); { Check all subject contour bounding boxes against clip boxes } for s := 0 to subj.num_contours - 1 do for c := 0 to clip.num_contours - 1 do o_table[c * subj.num_contours + s] := integer( (not(LT(s_bbox[s].xmax, c_bbox[c].xmin) or GT(s_bbox[s].xmin, c_bbox[c].xmax))) and (not(LT(s_bbox[s].ymax, c_bbox[c].ymin) or GT(s_bbox[s].ymin, c_bbox[c].ymax)))); { For each clip contour, search for any subject contour overlaps } for c := 0 to clip.num_contours - 1 do begin overlap := 0; s := 0; while (overlap = 0) and (s < subj.num_contours) do begin overlap := o_table[c * subj.num_contours + s]; Inc(s); end; if overlap = 0 then { Flag non contributing status by negating vertex count } clip.contour[c].num_vertices := -clip.contour[c].num_vertices; end; if (op = GPC_INT) then begin { For each subject contour, search for any clip contour overlaps } for s := 0 to subj.num_contours - 1 do begin overlap := 0; c := 0; while (overlap = 0) and (c < clip.num_contours) do begin overlap := o_table[c * subj.num_contours + s]; Inc(c); end; if overlap = 0 then { Flag non contributing status by negating vertex count } subj.contour[s].num_vertices := -subj.contour[s].num_vertices; end; end; FREE(pointer(s_bbox)); FREE(pointer(c_bbox)); FREE(pointer(o_table)); end; //=========================================================================== // Public Functions //=========================================================================== procedure gpc_free_polygon(Polygon : Pgpc_polygon); var c : integer; begin for c := 0 to Polygon.num_contours - 1 do CFree(pointer(Polygon.contour[c].vertex)); CFree(pointer(Polygon.hole)); CFree(pointer(Polygon.contour)); Polygon.num_contours := 0; end; procedure gpc_read_polygon(var f : text; p : Pgpc_polygon); var c, v : integer; begin readln(f, p.num_contours); MALLOC(pointer(p.contour), p.num_contours * sizeof(Tgpc_vertex_list), 'contour creation'); for c := 0 to p.num_contours - 1 do begin readln(f, p.contour[c].num_vertices); MALLOC(pointer(p.contour[c].vertex), p.contour[c].num_vertices * sizeof(Tgpc_vertex), 'vertex creation'); for v := 0 to p.contour[c].num_vertices - 1 do begin read(f, p.contour[c].vertex[v].x); readln(f, p.contour[c].vertex[v].y); end; end; end; procedure gpc_write_polygon(var f : text; p : Pgpc_polygon); var c, v : integer; begin writeln(f, p.num_contours); for c := 0 to p.num_contours - 1 do begin writeln(f, p.contour[c].num_vertices); for v := 0 to p.contour[c].num_vertices - 1 do writeln(f, p.contour[c].vertex[v].x:20:DBL_DIG , ' ' , p.contour[c].vertex[v].y:20:DBL_DIG); end; end; procedure gpc_add_contour(polygon : Pgpc_polygon; contour : Pgpc_vertex_list; hole : integer); var c, v : integer; extended_hole : PIntegerArray; extended_contour : Pgpc_vertex_list_array; begin { Create an extended hole array } MALLOC(pointer(extended_hole), (polygon.num_contours + 1) * sizeof(integer), 'contour hole addition'); { Create an extended contour array } MALLOC(pointer(extended_contour), (polygon.num_contours + 1) * sizeof(Tgpc_vertex_list), 'contour addition'); { Copy the old contour into the extended contour array } for c := 0 to polygon.num_contours - 1 do begin extended_hole[c] := polygon.hole[c]; extended_contour[c] := polygon.contour[c]; end; { Copy the new contour onto the end of the extended contour array } c := polygon.num_contours; extended_hole[c] := hole; extended_contour[c].num_vertices := contour.num_vertices; MALLOC(pointer(extended_contour[c].vertex), contour.num_vertices * sizeof(Tgpc_vertex), 'contour addition'); for v := 0 to contour.num_vertices - 1 do extended_contour[c].vertex[v] := contour.vertex[v]; { Dispose of the old contour } CFREE(pointer(polygon.contour)); CFREE(pointer(polygon.hole)); { Update the polygon information } Inc(polygon.num_contours); polygon.hole := extended_hole; polygon.contour := extended_contour; end; procedure gpc_polygon_clip(set_operation : Tgpc_op; subject_polygon : Pgpc_polygon; clip_polygon : Pgpc_polygon; result_polygon : Pgpc_polygon); var sbtree : Psb_tree; it, intersect : Pit_node; edge, prev_edge, next_edge, succ_edge : Pedge_node; e0, e1 : Pedge_node; aet : Pedge_node; c_heap, s_heap : Pedge_node_array; lmt, local_min : Plmt_node; out_poly, P, Q, poly, npoly, cf : Ppolygon_node; vtx, nv : Pvertex_node; horiz : array[0..1] of Th_state; inn, exists, parity : array[0..1] of integer; c, v, contributing, search, scanbeam : integer; sbt_entries, _class, bl, br, tl, tr : integer; sbt : PDoubleArray; xb, px, yb, yt, dy, ix, iy : double; begin edge := nil; sbtree := nil; it := nil; aet := nil; lmt := nil; out_poly := nil; cf := nil; inn[0] := LEFT; inn[1] := LEFT; exists[0] := LEFT; exists[1] := LEFT; parity[0] := LEFT; parity[1] := LEFT; scanbeam := 0; sbt_entries := 0; sbt := nil; { Test for trivial NULL result cases } if ((subject_polygon.num_contours = 0) and (clip_polygon.num_contours = 0)) or ((subject_polygon.num_contours = 0) and ((set_operation = GPC_INT) or (set_operation = GPC_DIFF))) or ((clip_polygon.num_contours = 0) and (set_operation = GPC_INT)) then begin result_polygon.num_contours := 0; result_polygon.hole := nil; result_polygon.contour := nil; exit; end; { Identify potentialy contributing contours } if (((set_operation = GPC_INT) or (set_operation = GPC_DIFF)) and (subject_polygon.num_contours > 0) and (clip_polygon.num_contours > 0)) then minimax_test(subject_polygon, clip_polygon, set_operation); { Build LMT } if subject_polygon.num_contours > 0 then s_heap := build_lmt(lmt, sbtree, sbt_entries, subject_polygon, SUBJ, set_operation); if clip_polygon.num_contours > 0 then c_heap := build_lmt(lmt, sbtree, sbt_entries, clip_polygon, CLIP, set_operation); { Return a NULL result if no contours contribute } if lmt = nil then begin result_polygon.num_contours := 0; result_polygon.hole := nil; result_polygon.contour := nil; reset_lmt(lmt); FREE(pointer(s_heap)); FREE(pointer(c_heap)); exit; end; { Build scanbeam table from scanbeam tree } MALLOC(pointer(sbt), sbt_entries * sizeof(double), 'sbt creation'); build_sbt(scanbeam, sbt^, sbtree); scanbeam := 0; free_sbtree(sbtree); { Allow pointer re-use without causing memory leak } if subject_polygon = result_polygon then gpc_free_polygon(subject_polygon); if clip_polygon = result_polygon then gpc_free_polygon(clip_polygon); { Invert clip polygon for difference operation } if set_operation = GPC_DIFF then parity[CLIP] := RIGHT; local_min := lmt; { Process each scanbeam } while (scanbeam < sbt_entries) do begin { Set yb and yt to the bottom and top of the scanbeam } yb := sbt[scanbeam]; Inc(scanbeam); if scanbeam < sbt_entries then begin yt := sbt[scanbeam]; dy := yt - yb; end; { === SCANBEAM BOUNDARY PROCESSING ================================ } { If LMT node corresponding to yb exists } if local_min <> nil then begin if EQ(local_min.y, yb) then begin { Add edges starting at this local minimum to the AET } edge := local_min.first_bound; while edge <> nil do begin add_edge_to_aet(aet, edge, nil); edge := edge.next_bound; end; local_min := local_min.next; end; end; { Set dummy previous x value } px := -DBL_MAX; { Create bundles within AET } e0 := aet; e1 := aet; { Set up bundle fields of first edge } aet.bundle[ABOVE][integer(aet.typ <> 0)] := integer(NE(aet.top.y, yb)); aet.bundle[ABOVE][integer(aet.typ = 0)] := FFALSE; aet.bstate[ABOVE] := UNBUNDLED; next_edge := aet.next; while next_edge <> nil do begin { Set up bundle fields of next edge } next_edge.bundle[ABOVE][next_edge.typ] := integer(NE(next_edge.top.y, yb)); next_edge.bundle[ABOVE][integer(next_edge.typ = 0)] := FFALSE; next_edge.bstate[ABOVE] := UNBUNDLED; { Bundle edges above the scanbeam boundary if they coincide } if next_edge.bundle[ABOVE][next_edge.typ] <> 0 then begin if (EQ(e0.xb, next_edge.xb) and EQ(e0.dx, next_edge.dx) and NE(e0.top.y, yb)) then begin next_edge.bundle[ABOVE][next_edge.typ] := next_edge.bundle[ABOVE][next_edge.typ] xor e0.bundle[ABOVE][ next_edge.typ]; next_edge.bundle[ABOVE][integer(next_edge.typ = 0)] := e0.bundle[ABOVE][integer(next_edge.typ = 0)]; next_edge.bstate[ABOVE] := BUNDLE_HEAD; e0.bundle[ABOVE][CLIP] := FFALSE; e0.bundle[ABOVE][SUBJ] := FFALSE; e0.bstate[ABOVE] := BUNDLE_TAIL; end; e0 := next_edge; end; next_edge := next_edge.next; end; horiz[CLIP] := NH; horiz[SUBJ] := NH; { Process each edge at this scanbeam boundary } edge := aet; while edge <> nil do begin exists[CLIP] := edge.bundle[ABOVE][CLIP] + (edge.bundle[BELOW][CLIP] shl 1); exists[SUBJ] := edge.bundle[ABOVE][SUBJ] + (edge.bundle[BELOW][SUBJ] shl 1); if (exists[CLIP] <> 0) or (exists[SUBJ] <> 0) then begin { Set bundle side } edge.bside[CLIP] := parity[CLIP]; edge.bside[SUBJ] := parity[SUBJ]; { Determine contributing status and quadrant occupancies } case set_operation of GPC_DIFF, GPC_INT: begin contributing := integer( ((exists[CLIP] <> 0) and ((parity[SUBJ] <> 0) or (horiz[SUBJ] <> NH))) or ((exists[SUBJ] <> 0) and ((parity[CLIP] <> 0) or (horiz[CLIP] <> NH))) or ((exists[CLIP] <> 0) and (exists[SUBJ] <> 0) and (parity[CLIP] = parity[SUBJ]))); br := integer((parity[CLIP] <> 0) and (parity[SUBJ] <> 0)); bl := integer( ((parity[CLIP] xor edge.bundle[ABOVE][CLIP]) <> 0) and ((parity[SUBJ] xor edge.bundle[ABOVE][SUBJ]) <> 0)); tr := integer( ((parity[CLIP] xor integer(horiz[CLIP] <> NH)) <> 0) and ((parity[SUBJ] xor integer(horiz[SUBJ] <> NH)) <> 0)); tl := integer( ((parity[CLIP] xor integer(horiz[CLIP] <> NH) xor edge.bundle[BELOW][CLIP]) <> 0) and ((parity[SUBJ] xor integer(horiz[SUBJ] <> NH) xor edge.bundle[BELOW][SUBJ]) <> 0)); end; GPC_XOR: begin contributing := integer((exists[CLIP] <> 0) or (exists[SUBJ] <> 0)); br := integer(parity[CLIP] xor parity[SUBJ]); bl := integer( ((parity[CLIP] xor edge.bundle[ABOVE][CLIP]) <> 0) xor ((parity[SUBJ] xor edge.bundle[ABOVE][SUBJ]) <> 0)); tr := integer( ((parity[CLIP] xor integer(horiz[CLIP] <> NH)) <> 0) xor ((parity[SUBJ] xor integer(horiz[SUBJ] <> NH)) <> 0)); tl := integer( ((parity[CLIP] xor integer(horiz[CLIP] <> NH) xor edge.bundle[BELOW][CLIP]) <> 0) xor ((parity[SUBJ] xor integer(horiz[SUBJ] <> NH) xor edge.bundle[BELOW][SUBJ]) <> 0)); end; GPC_UNION: begin contributing := integer( ((exists[CLIP] <> 0) and ((parity[SUBJ] = 0) or (horiz[SUBJ] <> NH))) or ((exists[SUBJ] <> 0) and ((parity[CLIP] = 0) or (horiz[CLIP] <> NH))) or ((exists[CLIP] <> 0) and (exists[SUBJ] <> 0) and (parity[CLIP] = parity[SUBJ]))); br := integer((parity[CLIP] <> 0) or (parity[SUBJ] <> 0)); bl := integer( ((parity[CLIP] xor edge.bundle[ABOVE][CLIP]) <> 0) or ((parity[SUBJ] xor edge.bundle[ABOVE][SUBJ]) <> 0)); tr := integer( ((parity[CLIP] xor integer(horiz[CLIP] <> NH)) <> 0) or ((parity[SUBJ] xor integer(horiz[SUBJ] <> NH)) <> 0)); tl := integer( ((parity[CLIP] xor integer(horiz[CLIP] <> NH) xor edge.bundle[BELOW][CLIP]) <> 0) or ((parity[SUBJ] xor integer(horiz[SUBJ] <> NH) xor edge.bundle[BELOW][SUBJ]) <> 0)); end; end; { case } { Update parity } (* parity[CLIP] := integer((parity[CLIP] <> 0) xor (edge.bundle[ABOVE][CLIP] <> 0)); parity[SUBJ] := integer((parity[SUBJ] <> 0) xor (edge.bundle[ABOVE][SUBJ] <> 0)); *) parity[CLIP] := parity[CLIP] xor edge.bundle[ABOVE][CLIP]; parity[SUBJ] := parity[SUBJ] xor edge.bundle[ABOVE][SUBJ]; { Update horizontal state } if exists[CLIP] <> 0 then horiz[CLIP] := next_h_state[integer(horiz[CLIP])] [((exists[CLIP] - 1) shl 1) + parity[CLIP]]; if exists[SUBJ] <> 0 then horiz[SUBJ] := next_h_state[integer(horiz[SUBJ])] [((exists[SUBJ] - 1) shl 1) + parity[SUBJ]]; _class := tr + (tl shl 1) + (br shl 2) + (bl shl 3); if contributing <> 0 then begin xb := edge.xb; case Tvertex_type(_class) of EMN, IMN: begin add_local_min(@out_poly, edge, xb, yb); px := xb; cf := edge.outp[ABOVE]; end; ERI: begin if NE(xb, px) then begin add_right(cf, xb, yb); px := xb; end; edge.outp[ABOVE] := cf; cf := nil; end; ELI: begin add_left(edge.outp[BELOW], xb, yb); px := xb; cf := edge.outp[BELOW]; end; EMX: begin if NE(xb, px) then begin add_left(cf, xb, yb); px := xb; end; merge_right(cf, edge.outp[BELOW], out_poly); cf := nil; end; ILI: begin if NE(xb, px) then begin add_left(cf, xb, yb); px := xb; end; edge.outp[ABOVE] := cf; cf := nil; end; IRI: begin add_right(edge.outp[BELOW], xb, yb); px := xb; cf := edge.outp[BELOW]; edge.outp[BELOW] := nil; end; IMX: begin if NE(xb, px) then begin add_right(cf, xb, yb); px := xb; end; merge_left(cf, edge.outp[BELOW], out_poly); cf := nil; edge.outp[BELOW] := nil; end; IMM: begin if NE(xb, px) then begin add_right(cf, xb, yb); px := xb; end; merge_left(cf, edge.outp[BELOW], out_poly); edge.outp[BELOW] := nil; add_local_min(@out_poly, edge, xb, yb); cf := edge.outp[ABOVE]; end; EMM: begin if NE(xb, px) then begin add_left(cf, xb, yb); px := xb; end; merge_right(cf, edge.outp[BELOW], out_poly); edge.outp[BELOW] := nil; add_local_min(@out_poly, edge, xb, yb); cf := edge.outp[ABOVE]; end; LED: begin if EQ(edge.bot.y, yb) then add_left(edge.outp[BELOW], xb, yb); edge.outp[ABOVE] := edge.outp[BELOW]; px := xb; end; RED: begin if EQ(edge.bot.y, yb) then add_right(edge.outp[BELOW], xb, yb); edge.outp[ABOVE] := edge.outp[BELOW]; px := xb; end; else end; { End of case } end; { End of contributing conditional } end; { End of edge exists conditional } edge := edge.next; end; { End of AET loop } { Delete terminating edges from the AET, otherwise compute xt } edge := aet; while edge <> nil do begin if EQ(edge.top.y, yb) then begin prev_edge := edge.prev; next_edge := edge.next; if prev_edge <> nil then prev_edge.next := next_edge else aet := next_edge; if next_edge <> nil then next_edge.prev := prev_edge; { Copy bundle head state to the adjacent tail edge if required } if (edge.bstate[BELOW] = BUNDLE_HEAD) and (prev_edge <> nil) then begin if prev_edge.bstate[BELOW] = BUNDLE_TAIL then begin prev_edge.outp[BELOW] := edge.outp[BELOW]; prev_edge.bstate[BELOW] := UNBUNDLED; if prev_edge.prev <> nil then if prev_edge.prev.bstate[BELOW] = BUNDLE_TAIL then prev_edge.bstate[BELOW] := BUNDLE_HEAD; end; end; end else begin if EQ(edge.top.y, yt) then edge.xt := edge.top.x else edge.xt := edge.bot.x + edge.dx * (yt - edge.bot.y); end; edge := edge.next; end; if scanbeam < sbt_entries then begin { === SCANBEAM INTERIOR PROCESSING ============================== } build_intersection_table(it, aet, dy); { Process each node in the intersection table } intersect := it; while intersect <> nil do begin e0 := intersect.ie[0]; e1 := intersect.ie[1]; { Only generate output for contributing intersections } if ((e0.bundle[ABOVE][CLIP] <> 0) or (e0.bundle[ABOVE][SUBJ] <> 0)) and ((e1.bundle[ABOVE][CLIP] <> 0) or (e1.bundle[ABOVE][SUBJ] <> 0)) then begin P := e0.outp[ABOVE]; Q := e1.outp[ABOVE]; ix := intersect.point.x; iy := intersect.point.y + yb; inn[CLIP] := integer( ((e0.bundle[ABOVE][CLIP] <> 0) and (e0.bside[CLIP] = 0)) or ((e1.bundle[ABOVE][CLIP] <> 0) and (e1.bside[CLIP] <> 0)) or ((e0.bundle[ABOVE][CLIP] = 0) and (e1.bundle[ABOVE][CLIP] = 0) and (e0.bside[CLIP] <> 0) and (e1.bside[CLIP] <> 0))); inn[SUBJ] := integer( ((e0.bundle[ABOVE][SUBJ] <> 0) and (e0.bside[SUBJ] = 0)) or ((e1.bundle[ABOVE][SUBJ] <> 0) and (e1.bside[SUBJ] <> 0)) or ((e0.bundle[ABOVE][SUBJ] = 0) and (e1.bundle[ABOVE][SUBJ] = 0) and (e0.bside[SUBJ] <> 0) and (e1.bside[SUBJ] <> 0))); { Determine quadrant occupancies } case set_operation of GPC_DIFF, GPC_INT: begin tr := integer((inn[CLIP] <> 0) and (inn[SUBJ] <> 0)); tl := integer( ((inn[CLIP] xor e1.bundle[ABOVE][CLIP]) <> 0) and ((inn[SUBJ] xor e1.bundle[ABOVE][SUBJ]) <> 0)); br := integer( ((inn[CLIP] xor e0.bundle[ABOVE][CLIP]) <> 0) and ((inn[SUBJ] xor e0.bundle[ABOVE][SUBJ]) <> 0)); bl := integer( ((inn[CLIP] xor e1.bundle[ABOVE][CLIP] xor e0.bundle[ABOVE][CLIP]) <> 0) and ((inn[SUBJ] xor e1.bundle[ABOVE][SUBJ] xor e0.bundle[ABOVE][SUBJ]) <> 0)); end; GPC_XOR: begin tr := integer((inn[CLIP] <> 0) xor (inn[SUBJ] <> 0)); tl := integer( (inn[CLIP] xor e1.bundle[ABOVE][CLIP]) xor (inn[SUBJ] xor e1.bundle[ABOVE][SUBJ])); br := integer( (inn[CLIP] xor e0.bundle[ABOVE][CLIP]) xor (inn[SUBJ] xor e0.bundle[ABOVE][SUBJ])); bl := integer( (inn[CLIP] xor e1.bundle[ABOVE][CLIP] xor e0.bundle[ABOVE][CLIP]) xor (inn[SUBJ] xor e1.bundle[ABOVE][SUBJ] xor e0.bundle[ABOVE][SUBJ])); end; GPC_UNION: begin tr := integer((inn[CLIP] <> 0) or (inn[SUBJ] <> 0)); tl := integer( ((inn[CLIP] xor e1.bundle[ABOVE][CLIP]) <> 0) or ((inn[SUBJ] xor e1.bundle[ABOVE][SUBJ]) <> 0)); br := integer( ((inn[CLIP] xor e0.bundle[ABOVE][CLIP]) <> 0) or ((inn[SUBJ] xor e0.bundle[ABOVE][SUBJ]) <> 0)); bl := integer( ((inn[CLIP] xor e1.bundle[ABOVE][CLIP] xor e0.bundle[ABOVE][CLIP]) <> 0) or ((inn[SUBJ] xor e1.bundle[ABOVE][SUBJ] xor e0.bundle[ABOVE][SUBJ]) <> 0)); end; end; { case } _class := tr + (tl shl 1) + (br shl 2) + (bl shl 3); case Tvertex_type(_class) of EMN: begin add_local_min(@out_poly, e0, ix, iy); e1.outp[ABOVE] := e0.outp[ABOVE]; end; ERI: begin if P <> nil then begin add_right(P, ix, iy); e1.outp[ABOVE] := P; e0.outp[ABOVE] := nil; end; end; ELI: begin if Q <> nil then begin add_left(Q, ix, iy); e0.outp[ABOVE] := Q; e1.outp[ABOVE] := nil; end; end; EMX: begin if (P <> nil) and (Q <> nil) then begin add_left(P, ix, iy); merge_right(P, Q, out_poly); e0.outp[ABOVE] := nil; e1.outp[ABOVE] := nil; end; end; IMN: begin add_local_min(@out_poly, e0, ix, iy); e1.outp[ABOVE] := e0.outp[ABOVE]; end; ILI: begin if P <> nil then begin add_left(P, ix, iy); e1.outp[ABOVE] := P; e0.outp[ABOVE] := nil; end; end; IRI: begin if Q <> nil then begin add_right(Q, ix, iy); e0.outp[ABOVE] := Q; e1.outp[ABOVE] := nil; end; end; IMX: begin if (P <> nil) and (Q <> nil) then begin add_right(P, ix, iy); merge_left(P, Q, out_poly); e0.outp[ABOVE] := nil; e1.outp[ABOVE] := nil; end; end; IMM: begin if (P <> nil) and (Q <> nil) then begin add_right(P, ix, iy); merge_left(P, Q, out_poly); add_local_min(@out_poly, e0, ix, iy); e1.outp[ABOVE] := e0.outp[ABOVE]; end; end; EMM: begin if (P <> nil) and (Q <> nil) then begin add_left(P, ix, iy); merge_right(P, Q, out_poly); add_local_min(@out_poly, e0, ix, iy); e1.outp[ABOVE] := e0.outp[ABOVE]; end; end; else end; { End of case } end; { End of contributing intersection conditional } { Swap bundle sides in response to edge crossing } if (e0.bundle[ABOVE][CLIP] <> 0) then e1.bside[CLIP] := integer(e1.bside[CLIP] = 0); if (e1.bundle[ABOVE][CLIP] <> 0) then e0.bside[CLIP] := integer(e0.bside[CLIP] = 0); if (e0.bundle[ABOVE][SUBJ] <> 0) then e1.bside[SUBJ] := integer(e1.bside[SUBJ] = 0); if (e1.bundle[ABOVE][SUBJ] <> 0) then e0.bside[SUBJ] := integer(e0.bside[SUBJ] = 0); { Swap e0 and e1 bundles in the AET } prev_edge := e0.prev; next_edge := e1.next; if next_edge <> nil then next_edge.prev := e0; if e0.bstate[ABOVE] = BUNDLE_HEAD then begin search := FTRUE; while search <> 0 do begin prev_edge := prev_edge.prev; if prev_edge <> nil then begin if prev_edge.bstate[ABOVE] <> BUNDLE_TAIL then search := FFALSE; end else search := FFALSE; end; end; if prev_edge = nil then begin aet.prev := e1; e1.next := aet; aet := e0.next; end else begin prev_edge.next.prev := e1; e1.next := prev_edge.next; prev_edge.next := e0.next; end; e0.next.prev := prev_edge; e1.next.prev := e1; e0.next := next_edge; intersect := intersect.next; end; { End of IT loop} { Prepare for next scanbeam } edge := aet; while edge <> nil do begin next_edge := edge.next; succ_edge := edge.succ; if EQ(edge.top.y, yt) and (succ_edge <> nil) then begin { 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]; prev_edge := edge.prev; if prev_edge <> nil then prev_edge.next := succ_edge else aet := succ_edge; if next_edge <> nil then next_edge.prev := succ_edge; succ_edge.prev := prev_edge; succ_edge.next := next_edge; end else begin { 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; end; edge.outp[ABOVE] := nil; edge := next_edge; end; end; end; { === END OF SCANBEAM PROCESSING ================================== } { Generate result polygon from out_poly } result_polygon.contour := nil; result_polygon.num_contours := count_contours(out_poly); if result_polygon.num_contours > 0 then begin MALLOC(pointer(result_polygon.hole), result_polygon.num_contours * sizeof(Integer), 'hole flag table creation'); MALLOC(pointer(result_polygon.contour), result_polygon.num_contours * sizeof(Tgpc_vertex_list), 'contour creation'); poly := out_poly; c := 0; while poly <> nil do begin npoly := poly.next; if poly.active <> 0 then begin result_polygon.hole[c] := poly.proxy.hole; result_polygon.contour[c].num_vertices := poly.active; MALLOC(pointer(result_polygon.contour[c].vertex), result_polygon.contour[c].num_vertices * sizeof(Tgpc_vertex), 'vertex creation'); v := result_polygon.contour[c].num_vertices - 1; vtx := poly.proxy.v[LEFT]; while vtx <> nil do begin nv := vtx.next; result_polygon.contour[c].vertex[v].x := vtx.x; result_polygon.contour[c].vertex[v].y := vtx.y; FREE(pointer(vtx)); Dec(v); vtx := nv; end; Inc(c); end; FREE(pointer(poly)); poly := npoly; end; end; { Tidy up } reset_it(it); reset_lmt(lmt); FREE(pointer(c_heap)); FREE(pointer(s_heap)); FREE(pointer(sbt)); end; procedure gpc_free_tristrip(tristrip : Pgpc_tristrip); var s : integer; begin for s := 0 to tristrip.num_strips - 1 do CFREE(pointer(tristrip.strip[s].vertex)); CFREE(pointer(tristrip.strip)); tristrip.num_strips := 0; end; //=========================================================================== // End of file: gpc.pas //=========================================================================== end.