mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 02:03:39 +02:00
1966 lines
63 KiB
ObjectPascal
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.
|
|
|