unit Clipper.Minkowski;

(*******************************************************************************
* Author    :  Angus Johnson                                                   *
* Date      :  21 December 2023                                                *
* Copyright :  Angus Johnson 2010-2022                                         *
* Purpose   :  Minkowski Addition and Difference                               *
* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
*******************************************************************************)

{$I Clipper.inc}

interface

uses
  Classes, Math, Clipper.Core;

function MinkowskiSum(const Pattern, Path: TPath64;
  PathIsClosed: Boolean): TPaths64; overload;
function MinkowskiSum(const Pattern, Path: TPathD;
  PathIsClosed: Boolean; decimalPlaces: integer = 2): TPathsD; overload;
function MinkowskiDiff(const path1, path2: TPath64): TPaths64; overload;
function MinkowskiDiff(const Pattern, Path: TPathD;
  PathIsClosed: Boolean; decimalPlaces: integer): TPathsD; overload;

implementation

uses
  Clipper;

function AddPoints(val1, val2: TPoint64): TPoint64;
  {$IFDEF INLINING} inline; {$ENDIF}
begin
  Result.X := val1.X + val2.X;
  Result.Y := val1.Y + val2.Y;
end;
//------------------------------------------------------------------------------

function SubtractPoints(val1, val2: TPoint64): TPoint64;
  {$IFDEF INLINING} inline; {$ENDIF}
begin
  Result.X := val1.X - val2.X;
  Result.Y := val1.Y - val2.Y;
end;
//------------------------------------------------------------------------------

function Minkowski(const Base, Path: TPath64;
  IsSum: Boolean; IsClosed: Boolean): TPaths64;
var
  i,j,k,g,h, delta, baseLen, pathLen: integer;
  tmp: TPaths64;
  quad: TPath64;
begin
  delta := Iif(IsClosed, 0 , 1);
  baseLen := Length(Base);
  pathLen := Length(Path);
  setLength(tmp, pathLen);

  for i := 0 to pathLen -1 do
  begin
    setLength(tmp[i], baseLen);
    if IsSum then
      for j := 0 to baseLen -1 do
        tmp[i][j] := AddPoints(Path[i], Base[j])
    else
      for j := 0 to baseLen -1 do
        tmp[i][j] := SubtractPoints(Path[i], Base[j]);
  end;

  SetLength(quad, 4);
  SetLength(Result, (pathLen - delta) * baseLen);
  g := Iif(IsClosed, pathLen - 1, 0);

  for i := delta to pathLen - 1 do
  begin
    h := baseLen - 1;
    k := (i - delta) * baseLen;
    for j := 0 to baseLen - 1 do
    begin
      quad[0] := tmp[g][h];
      quad[1] := tmp[i][h];
      quad[2] := tmp[i][(j)];
      quad[3] := tmp[g][(j)];
      if not IsPositive(quad) then
        Result[k + j] := ReversePath(quad) else
        Result[k + j] := copy(quad, 0, 4);
      h := j;
    end;
    g := i;
  end;
end;
//------------------------------------------------------------------------------

function MinkowskiSum(const Pattern, Path: TPath64; PathIsClosed: Boolean): TPaths64;
begin
   Result := Union( Minkowski(Pattern, Path, true, PathIsClosed), frNonZero);
end;
//------------------------------------------------------------------------------

function MinkowskiSum(const Pattern, Path: TPathD;
  PathIsClosed: Boolean; decimalPlaces: integer): TPathsD;
var
  tmp: TPaths64;
  scale: double;
begin
  scale := Power(10, decimalPlaces);
  tmp := Union( Minkowski(ScalePath(Pattern, scale),
    ScalePath(Path, scale), true, PathIsClosed), frNonZero);
  Result := ScalePathsD(tmp, 1/scale);
end;
//------------------------------------------------------------------------------

function MinkowskiDiff(const path1, path2: TPath64): TPaths64;
begin
  Result := Union( Minkowski(path1, path2, false, true), frNonZero);
end;
//------------------------------------------------------------------------------

function MinkowskiDiff(const Pattern, Path: TPathD;
  PathIsClosed: Boolean; decimalPlaces: integer): TPathsD;
var
  tmp: TPaths64;
  scale: double;
begin
  scale := Power(10, decimalPlaces);
  tmp := Union( Minkowski(ScalePath(Pattern, scale),
    ScalePath(Path, scale), false, PathIsClosed), frNonZero);
  Result := ScalePathsD(tmp, 1/scale);
end;
//------------------------------------------------------------------------------

end.

