Polygon expansion / contraction

This Delphi project shows expansion and contraction of polygons.
Below is a picture of the project at work:

The grey lines are the original polygon, the black lines form the contracted polygon.
The process uses basic vector operations. So, I start with a brief explanation.

Vector geometry in a plane.

A vector (V) in 2D is a line segment defined by dx and dy (horizontal and vertical distances).

Multiplication by a scalar

Equation of a line

Intersection of lines

Parallel shift

Vector direction

Left and Right orientation
Left picture: counterclockwise (CCW). Right: clockwise (CW).

Expansion algorithm

A right shift of vectors is needed if
- the polygon is CW oriented and
- expansion is required
or
- polygon is CW oriented and
- contraction is requested
In other cases a left shift must take place.
The new points are the intersections of the shifted vectors.

Before expansion, the vector list is created from the list of points.
The angles between connecting vectors are summed to know CW or CCW orientation.

Using the vector list, the new points are calculated.

The program

``` const offset = 0.025;           //displacement
pi05 = 0.5*pi;
pi15 = 1.5*pi;
pi2 = 2*pi;
```
Vector direction
```function VDir(deltaX,deltaY : single) : single;
//return direction of vector in radians
//(+,0) = 0; (0,+) = 0.5pi ; (-,0) = pi ; (0,-) = 1.5pi
begin
if deltaX = 0 then
begin
if deltaY > 0 then result := pi05 else result := pi15;
exit;
end;
result := arctan((deltaY)/(deltaX));
if deltaX < 0 then result := result + pi;
if result < 0 then result := result + pi2;
end;
```
Angle between vectors
```function V12angle(dir1,dir2 : single) : single;
//return angle between vectors v1,v2 in radians
begin
result := dir2 - dir1;
if result > pi then result := result-pi2
else if result < -pi then result := result+pi2;
end;
```
Summing the angles
```function SumAngles : single;
var i : byte;
begin
result := 0;
for i := 1 to vcount-1 do
result := result + V12Angle(vectorlist[i].dir,vectorlist[i+1].dir);
result := result + V12Angle(vectorlist[vcount].dir,vectorlist[1].dir);
end;
```
Data formats
```Const maxpoint = 40;
type  Tvector = record
x,y   : single;
dx,dy : single;
dir   : single;       //direction 0..+-pi
modulus : single;
end;
TVectorList = array[1..maxpoint] of TVector;
TFpoint = record
x : single;
y : single;
end;
TFpoints = array[1..maxpoint] of TFpoint;
var Fpoints : TFpoints;   //in interface
pcount : byte;        //number of points
vectorlist : TVectorlist;//in implementation
vcount : byte;           //number of vectors
```
Vector shifting
```procedure shiftvector(var v : Tvector; R : boolean);
//R : true for right shift
var dd,m: single;
begin
if R then m := 1 else m := -1;
with v do
begin
dd := offset/modulus;
x := x+dd*dy*m;
y := y-dd*dx*m;
end;
end;
```

Form and buttons

The polygon is painted on a bitmap which is copied to form1.paintbox1.

Units
Unit1 holds the procedures for drawing and the event handlers.
The poly_unit takes care of the polygon specific procedures and data.
The clock_unit supplies a microseconds clock to measure processing time and
control the expansion speed.

For details please refer to the source code.

Operations

drawing a polygon
[draw] button down.
- mouse DOWN on paintbox and move
- mouse UP to end.
modify a polygon
[modify] button down
- mouse DOWN on point and move
- mouse UP to stop
[undo] : remove last added vector.
[clear] : erase polygon
[save] : polygon is stored and remains drawn in grey.