lazarus/components/aggpas/gpc/gpc.pas
mattias 36a2b1ea07 added aggpas
git-svn-id: trunk@21942 -
2009-10-01 12:24:32 +00:00

1966 lines
63 KiB
ObjectPascal

(*
===========================================================================
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.