unit OvUnit;
{
 calculate areas A, B and AB overlap
}
interface
uses dialogs,sysutils,graphics;

type TrealPoint = record
                   x,y : double;
                  end;

     Tedge = record
              x1,y1,x2,y2 : double;        //start and finish of edge
             end;
     TPoly12 = record
                angles : byte;
                coord  :array[1..12] of TrealPoint;
               end;
             
     TEdge12 = record
                edges : byte;
                coord : array[1..12] of TEdge;
               end;

     TIntersection = record
                      fa,fb : double;   //mult factor of vector to intersection
                      valid : boolean;  //false if parallel
                     end;

           TISlist = record            //intersection list of A,B
                      Acount   : byte;
                      Bcount   : byte;
                      isdata   : array[1..12,1..12] of TIntersection;
                     end;

      TInnerPoint = record
                     x,y   : double;       //intersection coordinates
                     ia,ib : byte;         //edges with (x,y)
                    end;

          TIPlist = record
                     top : byte;
                     ipdata : array[1..50] of TInnerpoint;
                    end;

           TTrack = record
                     x,y : double;         //ref to point in TInnerpoint list
                     Ematch : LongInt;     //bit set = point on this edge
                     visited : boolean;    //used in innerpath
                    end;                   //B points index + 12

       TTrackList = record
                     top : byte;
                     trackdata : array[1..50] of TTrack;
                    end;

        TPolyList = record
                     top   : byte;
                     coord : array[1..50] of TRealpoint;
                    end;



function overlap(var pA, pB : TPoly12) : double;
function PolyArea(var poly : TPoly12) : double;
function TestConvex(var poly : TPoly12) : boolean;

var Apoints : TPoly12;            //angle coordinates of A
    Bpoints : TPoly12;            //angle coordinates of B
    APosOK    : boolean = false;  //valid shape of A
    BPosOK    : boolean = false;  //...............B
    polyList  : TPolyList;        //final angle coordinates of inner polygon

implementation

uses unit1,paramunit;

{--------forward declarations------------}

procedure makeEdgeList(var Elist:Tedge12 ; crd: TPoly12);forward;
procedure makeIntersections;forward;
procedure makeIPlist;forward;
procedure makeTrackList;forward;
procedure makePolyList;forward;
function calcArea : double;forward;
function distance(i,j : byte) : double;forward;
procedure paintPolylist;forward;
procedure addtrackpoint(n : byte);forward;
function Polydistance(var po : TPoly12; i,j : byte) : double;forward;

{------------ tables used during calculation of areas -------}

var Aedges    : TEdge12;    //list of edges of polygon A
    Bedges    : TEdge12;    //.........................B
    ISList    : TISlist;    //table with intersection data
    IPlist    : TIPlist;    //list of innerpoints, point may appear > once
    TrackList : TTracklist; //bit encoded edges of inner polygon

{------------ main calls -------------}

function PolyArea(var poly : TPoly12) : double;
//calculate area of polygon
//this is done by breaking down the polygon into triangles
//with edges a,b,c and s = (a+b+c)/2
//apply herons lemma: area = sqrt(s*(s-a)*(s-b)*(s-c))
var a,b,c,s : double;
    i : byte;
begin
 result := 0;
 with poly do
 for i := 1 to angles-2 do
  begin
   a := Polydistance(poly,1,i+1);
   b := Polydistance(poly,i+1,i+2);
   c := Polydistance(poly,1,i+2);
   s := (a+b+c)2;
   result := result + sqrt(s*(s-a)*(s-b)*(s-c));
  end;
end;

function Testconvex(var poly : TPoly12) : boolean;
//return true is polygon is convex
var i,j,iscount : byte;
    b1,b2 : boolean;
begin
 makeEdgeList(Aedges,poly);
 makeEdgeList(Bedges,poly);
 makeIntersections;
 for j := 1 to ISList.Acount do
  begin
   iscount := 0;
   for i := 1 to ISList.Acount do
    with ISList.isdata[i,j] do
     if valid then
      begin
       b1 := (fa >= 0) and (fa <= 1);
       b2 := (fb >= 0) and (fb <= 1);
       if b1 or b2 then inc(iscount);
      end;
   if iscount > 2 then                //may intersect with only 2 other edges
    begin
     result := false;
     exit;
    end;
  end;//for
 result := true;
end;

function Overlap(var pA, pB : TPoly12) : double;
//calculate A,B areas and overlap
//A,B,shape must be OK
begin
 result := 0;
 makeEdgeList(Aedges , pA);  //list of projected edges
 makeEdgeList(Bedges , pB);  //list of reference rectangle edges
 makeIntersections;
 makeIPlist;
 makeTrackList;                   //list of inner points
 makePolyList;                    //final result, global list
 if PolyList.Top >= 3 then
  begin
   paintpolyList;
   result := calcArea;           //area of polylist
  end;
end;

{----------- sub procs of proc. "overlap" ----------}

procedure makeEdgeList(var Elist:Tedge12 ; crd: TPoly12);
//use Tpoly12 array to build EdgeList
var i,n : byte;
begin
 n := crd.angles;
 Elist.edges := n;
 for i := 1 to n do
  begin
   Elist.coord[i].x1 := crd.coord[i].x;
   Elist.coord[i].y1 := crd.coord[i].y;
   if i < n then
    begin
     Elist.coord[i].x2 := crd.coord[i+1].x;
     Elist.coord[i].y2 := crd.coord[i+1].y;
    end
   else begin
         Elist.coord[i].x2 := crd.coord[1].x;
         Elist.coord[i].y2 := crd.coord[1].y;
        end;
  end;//for
end;

procedure makeIntersections;
//use edge lists of poly12
//to build the intersections list
var i,j : byte;
    a,b,c,d,e,f : double;
    x,y,discr : double;
begin
 with ISlist do
  begin
   Acount := Aedges.edges;
   Bcount := Bedges.edges;
  end;
 for i := 1 to ISlist.Acount do   //find intersections of edges
  for j := 1 to ISlist.Bcount do
   begin
    a :=  Aedges.coord[i].x2 - Aedges.coord[i].x1;
    b :=  Bedges.coord[j].x1 - Bedges.coord[j].x2;   {ax + by = e}
    c :=  Aedges.coord[i].y2 - Aedges.coord[i].y1;   {cx + dy = f}
    d :=  Bedges.coord[j].y1 - Bedges.coord[j].y2;   {fa=x,fb=y}
    e :=  Bedges.coord[j].x1 - Aedges.coord[i].x1;
    f :=  Bedges.coord[j].y1 - Aedges.coord[i].y1;
    discr := a*d-b*c;
    if discr <> 0 then
     begin
      ISList.isdata[i,j].fa := (e*d-b*f)discr;  //vector mult factor
      ISList.isdata[i,j].fb := (a*f-c*e)discr;  //....
      ISList.isdata[i,j].valid := true;
     end
     else
      begin
       ISList.isdata[i,j].valid := false; //edges parallel or coincident
      end;
   end;//for i,j
end;

procedure makeIPlist;
//use intersections to calculate IPlist  (inner points list)
var i,j,n : byte;
    Poscode : word;//1xx:minus out; x1x:inner; xx1: positive out
    IPhit : boolean;
begin
 for i := 1 to 50 do     //clear list
  with IPlist.ipdata[i] do
   begin
    ia := 0;
    ib := 0;
   end;
//
 n := 0;
 for i := 1 to ISList.Acount do                   //find intersections
  for j := 1 to ISList.Bcount do
   with ISList.isdata[i,j] do
    if valid and (fb >= 0) and (fb <= 1) and (fa >= 0) and (fa <=1) then
     begin
      inc(n);
      with Aedges.coord[i] do
       with IPList.ipdata[n] do
        begin
         x := x1 + fa*(x2 - x1);
         y := y1 + fa*(y2 - y1);
         ia := i;
         ib := j;
        end;
      end;
//
 for i := 1 to ISList.Acount do     //find inner A points
  begin
   poscode := 0;
   IPhit := false;
   for j := 1 to ISList.Bcount do
    with ISList.isdata[i,j] do
     if valid and (fb >=0) and (fb <=1) and (IPhit = false) then
      begin
       if (fa > 1) then poscode := poscode or $0001
        else if (fa < 0) then poscode := poscode or $0100
         else poscode := poscode or $0010;

       if (poscode=$0110) or (poscode=$0101) then    //first point
        begin
         inc(n);
         IPhit := true;
         with IPlist.ipdata[n] do
          begin
           x := Aedges.coord[i].x1;
           y := Aedges.coord[i].y1;
           ia := i;
          end;
        end;//if
       if (poscode=$0011) or (poscode=$0101) then
        begin                                        //second point
         inc(n);
         IPhit := true;
         with IPlist.ipdata[n] do
          begin
           x := Aedges.coord[i].x2;
           y := Aedges.coord[i].y2;
           ia := i;
          end;
        end;//if
      end;//if valid
  end;//for j
//
 for j := 1 to ISList.Bcount do                     //find inner B points
  begin
   poscode := 0;
   IPhit := false;
   for i := 1 to ISList.Acount do
    with ISList.isdata[i,j] do
     if valid and (fa >= 0) and (fa <= 1) and (IPhit = false) then
      begin
       if fb > 1 then poscode := poscode or $0001
        else if fb < 0 then poscode := poscode or $0100
         else poscode := poscode or $0010;

       if (poscode=$0110) or (poscode=$0101) then   //first point
        begin
         inc(n);
         IPhit := true;
         with IPlist.ipdata[n] do
          begin
           x := Bedges.coord[j].x1;
           y := Bedges.coord[j].y1;
           ib := j;
          end;
        end;//if
       if (poscode=$0011) or (poscode=$0101) then   //second point
        begin
         inc(n);
         IPhit := true;
         with IPlist.ipdata[n] do
          begin
           x := Bedges.coord[j].x2;
           y := Bedges.coord[j].y2;
           ib := j;
          end;
        end;//if
      end;//if valid
  end;//for i
 IPlist.top := n;
end;

procedure makeTrackList;
//use IP list to built the trackList
var i : byte;
begin
 TrackList.top := 0;
//
 for i := 1 to 50 do       //clear the TrackList
  with TrackList.trackdata[i] do
   begin
    x := 0; y := 0;
    Ematch := 0;
    visited := false;
   end;
//
 for i := 1 to IPlist.top do addTrackpoint(i);
end;

procedure makePolyList;
var oldEdges : longInt;            //bit set = on edge, PEdges bias + 4
    trackNr,i : byte;
    hit : boolean;
begin
 polylist.top := 0;
 for i := 1 to 50 do               //clear polylist
  with polylist.coord[i] do
   begin
    x := 0;
    y := 0;
   end;
 if Tracklist.Top < 3 then exit;
//
 Polylist.Top := 1;                 //set 1st point
 with PolyList.coord[1] do
  begin
   x := TrackList.trackdata[1].x;
   y := Tracklist.trackdata[1].y;
  end;
 trackList.trackdata[1].visited := true;
 oldEdges := TrackList.trackdata[1].Ematch;
//    ---  find connection in TrackList---
 repeat
  trackNr := 0;        //entry of trackList
  hit := false;
  repeat
   inc(trackNr);
   with TrackList.trackdata[trackNr] do
    if (visited = false) and (OldEdges and EMatch <> 0) then hit := true;
  until hit or (trackNr = Tracklist.Top);
//
  if hit then
   with trackList.trackdata[tracknr] do
    begin
     visited := true;
     oldEdges := Ematch;
     inc(Polylist.Top);
     PolyList.coord[Polylist.top].x := x;
     PolyList.coord[Polylist.Top].y := y;
    end;
 until hit = false;
end;

procedure paintPolylist;
//paint in map2, copy to paintbox
//called in test mode to show IP list
var i : byte;
    xx,yy : integer;
begin
 with map2 do with canvas do
  begin
   pen.color := $000000;
   brush.color := $000000;
   brush.Style := bsSolid;
   for i := 1 to Polylist.Top do
    with PolyList.coord[i] do
     begin
      xx := XToPixel(x);
      yy := YToPixel(y);
      ellipse(xx-4,yy-4,xx+4,yy+4);
     end;
  end;//with
 map2ToBox;
end;

function calcArea : double;
//use polygon list to calculate overlapping area
//this is done by breaking down the polygon into triangles
//with edges a,b,c and s = (a+b+c)/2
//apply herons lemma: area = sqrt(s*(s-a)*(s-b)*(s-c))
var a,b,c,s : double;
    i : byte;
begin
 result := 0;
 if polylist.top < 3 then exit;
//
 for i := 1 to Polylist.Top-2 do
  begin
   a := distance(1,i+1);
   b := distance(i+1,i+2);
   c := distance(1,i+2);
   s := (a+b+c)2;
   result := result + sqrt(s*(s-a)*(s-b)*(s-c));
  end;
end;

//----- help for polylist generation , area calculation -----------

procedure addtrackpoint(n : byte);
//adds entry n of IP list to the tracklist
var i,j,aa,bb : byte;
    x,y : double;
    hit : boolean;
begin
 i := 0;
 x := IPlist.ipdata[n].x;
 y := IPlist.ipdata[n].y;
 hit := false;
 j := 0;
 if tracklist.top > 0 then
  repeat
   inc(j);
   if (Tracklist.trackdata[j].x = x) and
      (TrackList.trackdata[j].y = y) then hit := true;
  until hit or (j = TrackList.Top);
//
 if hit = false then
  begin
   inc(TrackList.Top);             //create new entry
   j := Tracklist.Top;
   Tracklist.trackdata[j].x := x;
   Tracklist.trackdata[j].y := y
  end;
//
 aa := IPlist.ipdata[n].ia;        //point on edge ia
 bb := IPlist.ipdata[n].ib;        //..............ib
 with TrackList.trackdata[j] do
  begin
   if aa <> 0 then Ematch := Ematch or (1 shl aa);
   if bb <> 0 then Ematch := Ematch or (1 shl (bb+12));
  end;
end;

function Polydistance(var po : TPoly12; i,j : byte) : double;
var dx, dy : double;
begin
 with po do
  begin
   dx := (coord[i].x - coord[j].x);
   dy := (coord[i].y - coord[j].y);
  end;
 result := sqrt(dx*dx + dy*dy);
end;

function distance(i,j : byte) : double;
//pythagoras, distance between (x1,y1)...(x2,y2) of polylist
var dx, dy : double;
begin
 dx := (PolyList.coord[i].x - PolyList.coord[j].x);
 dy := (PolyList.coord[i].y - PolyList.coord[j].y);
 result := sqrt(dx*dx + dy*dy);
end;

end.