{
     Apophysis Copyright (C) 2001-2004 Mark Townsend
     Apophysis Copyright (C) 2005-2006 Ronald Hordijk, Piotr Borys, Peter Sdobnov
     Apophysis Copyright (C) 2007-2008 Piotr Borys, Peter Sdobnov
     
     Apophysis "3D hack" Copyright (C) 2007-2008 Peter Sdobnov
     Apophysis "7X" Copyright (C) 2009-2010 Georg Kiehne
     Apophysis AV "Phoenix Edition" Copyright (C) 2021-2022 Alice V. Koryagina

     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}

// REMOVE COMMENT TO COMPILE T-500
//{$define T500}
//{$define Light}

unit XForm;

interface

uses
{$ifdef CPUX86}
AsmRandom,
{$endif}
  XFormMan, BaseVariation, Classes;

const
  MAX_WEIGHT = 1000.0;
  {$ifndef Light}
    {$ifndef T500}
    NXFORMS = 100;
    {$else}
    NXFORMS = 250; // 500; // AV: to use 1-byte type
    {$endif}
  {$else}
    NXFORMS = 50;
  {$endif}

type
  TCPpoint = record
    x, y, z, c: double;
  end;
  PCPpoint = ^TCPpoint;

  TXYpoint = record
    x, y: double;
  end;
  PXYpoint = ^TXYpoint;

  {
  T2Cpoint = record
    x, y, c1, c2: double;
  end;
  P2Cpoint = ^T2Cpoint;

  TMatrix = array[0..2, 0..2] of double;
  }

// AV: rewrote all asm-code from Apo7X to make it work properly again
{$ifdef CPUX86}
  {$define _ASM_}
{$endif}

type
  TCoefsArray = array[0..2, 0..1] of double; // AV: moved from ControlPoint
  //pCoefsArray = ^TCoefsArray;

  TXForm = class
  public

    c: TCoefsArray;     // the coefs to the affine part of the function
    p: TCoefsArray;     // post-transform coefs!
    density: double;      // prob is this function is chosen
    color: double;        // color coord for this function. 0 - 1
    color2: double;       // Second color coord for this function. 0 - 1
    vc: double;           // Calculated color to be passed to the plugin
    symmetry: double;
    c00, c01, c10, c11, c20, c21: double; // unnecessary duplicated variables
    p00, p01, p10, p11, p20, p21: double; // :-)
    postXswap: boolean;
    TransformName : string;

    autoZscale: boolean; // for 3d editing
    transOpacity: double;
    pluginColor: double;

    modWeights: array [0..NXFORMS] of double;
    PropTable: array of TXForm;

   // Orientationtype: integer;
    ifs: TStringList; // AV: to keep custom variation order
  private
    vars: array of double; // {normalized} interp coefs between variations
    FNrFunctions: Integer;
    FFunctionList: array of TCalcFunction;
    FCalcFunctionList: array of TCalcFunction;

    FTx, FTy: double; // must remain in this order
    FPx, FPy: double; // some asm code relies on this
    FTz, FPz: double; // 3d hack

    FAngle: double;
    FSinA: double;
    FCosA: double;
    FLength: double;
    colorC1, colorC2: double;

    // precalculated constants for some variations
    polar_vpi, disc_vpi: double;
    polar2_vpi, p2vv2: double;

    gauss_rnd: array [0..3] of double;
    gauss_N: integer;

    rx_sin, rx_cos, ry_sin, ry_cos: double;
    px_sin, px_cos, py_sin, py_cos: double;

    FRegVariations: array of TBaseVariation;

    procedure PrecalcAngle;
    procedure PrecalcSinCos;
    procedure PrecalcAll;
    procedure DoPostTransform;
    procedure DoInvalidOperation;

    procedure Linear3D;            // vars[0]
    procedure PreBlur3D;          // vars[2]
    procedure Spherical;           // vars[3]
    procedure Swirl;               // vars[4]
    procedure Horseshoe;           // vars[5]
    procedure Polar;               // vars[6]

    procedure Disc;                // vars[7]
    procedure Spiral;              // vars[8]
    procedure hyperbolic;          // vars[9]
    procedure Diamond;              // vars[10]

    procedure Eyefish;   // vars[11]
    procedure Bubble;    // vars[12]
    procedure Cylinder;  // vars[13]
    procedure Noise;    // vars[14]
    procedure Blur;    // vars[15]
    procedure Gaussian;  // vars[16]
    procedure ZBlur;    // vars[17]
    procedure Blur3D;    // vars[18]

    procedure PreBlur;   // vars[19]
    procedure PreZScale;  // vars[20]
    procedure PreZTranslate;  // vars[21]
    procedure PreRotateX;    // vars[22]
    procedure PreRotateY;    // vars[23]

    procedure Flatten;   // vars[1]
    procedure ZScale;    // vars[24]
    procedure ZTranslate;  // vars[25]
    procedure ZCone;       // vars[26]

    procedure PostRotateX;   // vars[27]
    procedure PostRotateY;   // vars[28]

    procedure PostMirrorX;   // vars[29]
    procedure PostMirrorY;   // vars[30]
    procedure PostMirrorZ;   // vars[31]

    procedure Hemisphere;  // vars[32]
    procedure Cross;		// vars[33]
    procedure Pyramid;  // vars[34]
    procedure Polar2;  // vars[35]

    //function Mul33(const M1, M2: TMatrix): TMatrix;
    //function Identity: TMatrix;

    procedure BuildFunctionlist;
    procedure AddRegVariations;

  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear(keepXaos: boolean = false);
    procedure Prepare;
    procedure PrepareInvalidXForm;

    procedure Assign(Xform: TXForm);

    procedure NextPoint(var CPpoint: TCPpoint);
    procedure NextPointTo(var CPpoint, ToPoint: TCPpoint);
    procedure NextPointXY(var px, py: double);
    //procedure NextPoint2C(var p: T2CPoint);

    // AV: extended all following methods for post-coefs support
    procedure Rotate(var t: TCoefsArray; const degrees: double);
    procedure Translate(var t: TCoefsArray; const x, y: double);
    procedure Multiply(var t: TCoefsArray; const k, l, m, n: double);
    procedure Scale(var t: TCoefsArray; const s: double);
    procedure RandomizeCoefs(var t: TCoefsArray); // AV: for random flames
    function detC: double; inline;
    function detP: double; inline;

    procedure GetVariable(const name: string; var Value: double);
    procedure SetVariable(const name: string; var Value: double);
    procedure ResetVariable(const name: string);

    function GetVariableStr(const name: string): string;
    procedure SetVariableStr(const name: string; var Value: string);

    function ToXMLString: string;
    function FinalToXMLString(IsEnabled: boolean): string;

    function GetVariation(index : integer) : double;
    procedure SetVariation(index : integer; value : double);
    function NumVariations : integer;
  end;

implementation

uses
  SysUtils, Math, StrUtils;

const
  EPS: double = 1E-300;

function TXForm.NumVariations : integer;
begin
  Result := length(vars);
end;

function TXForm.GetVariation(index : integer) : double;
begin
  Result := vars[index];
end;

procedure TXForm.SetVariation(index : integer; value : double);
begin
  {
  if (vars[index] = 0) and (value <> 0) then begin
    // Activate var here
  end else begin
    // Deactivate var here
  end;
  }
  vars[index] := value;
end;


{ TXForm }

///////////////////////////////////////////////////////////////////////////////
constructor TXForm.Create;
var  i: Integer;
begin
  AddRegVariations;
  BuildFunctionlist;
  SetLength(vars, NrVar); // <-- AV // NRLOCVAR + Length(FRegVariations));
  // AV: set default variations order
  ifs := TStringList.Create;
  for i := 0 to NrVar-1 do ifs.Add(Varnames(i));

  Clear;
end;

procedure TXForm.Clear(keepXaos: boolean = false);
var
  i: Integer;
begin
  density := 0;
  color := 0;
  symmetry := 0;
  postXswap := false;
  autoZscale := false;

  c[0, 0] := 1;
  c[0, 1] := 0;
  c[1, 0] := 0;
  c[1, 1] := 1;
  c[2, 0] := 0;
  c[2, 1] := 0;

  p[0, 0] := 1;
  p[0, 1] := 0;
  p[1, 0] := 0;
  p[1, 1] := 1;
  p[2, 0] := 0;
  p[2, 1] := 0;

  vars[0] := 1;
  for i := 1 to High(vars) do
    vars[i] := 0;

  if not keepXaos then  // AV: for linked xforms
    for i := 0 to NXFORMS do
      modWeights[i] := 1;

   for i := 0 to NrVar-1 do  // restore default order
     ifs.Strings[i] := Varnames(i);

  transOpacity := 1;
  pluginColor := 1;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Prepare;
var
  i, v: integer;
  CalculateAngle, CalculateSinCos, CalculateLength: boolean;
  mode: double;
  vn: string;
  UsedVars: TStringList;
begin
  c00 := c[0][0];
  c01 := c[0][1];
  c10 := c[1][0];
  c11 := c[1][1];
  c20 := c[2][0];
  c21 := c[2][1];

  colorC1 := (1 + symmetry)/2;
  colorC2 := color*(1 - symmetry)/2;

  FNrFunctions := 0;

  for i := 0 to High(FRegVariations) do begin
    FRegVariations[i].FPX := @FPX;
    FRegVariations[i].FPY := @FPY;
    FRegVariations[i].FPz := @FPz;
    FRegVariations[i].FTX := @FTX;
    FRegVariations[i].FTY := @FTY;
    FRegVariations[i].FTz := @FTz;
    FRegVariations[i].a := c00;
    FRegVariations[i].b := c01;
    FRegVariations[i].c := c10;
    FRegVariations[i].d := c11;
    FRegVariations[i].e := c20;
    FRegVariations[i].f := c21;
    FRegVariations[i].color := @vc;

    FRegVariations[i].vvar := vars[i + NRLOCVAR];
    FRegVariations[i].Prepare;
    FRegVariations[i].GetCalcFunction(FFunctionList[NRLOCVAR + i]);
  end;

  SetLength(FCalcFunctionList, NrVar + 2);

  CalculateAngle := (vars[6] <> 0.0) or (vars[7] <> 0.0) or (vars[35] <> 0.0);
//  CalculateLength := False;
  CalculateSinCos := (vars[8] <> 0.0) or (vars[10] <> 0.0);

  UsedVars := TStringList.Create;

  // Pre- variations
  for vn in ifs do
  begin
    v := GetVariationIndex(vn);
    if (vars[v] <> 0.0) then
    begin
      UsedVars.Add(vn); // AV: remember all used variations in the right order
      if (LeftStr(vn, 4) = 'pre_') or (vn = 'flatten') then  // AV: flatten became pre_
      begin
        FCalcFunctionList[FNrFunctions] := FFunctionList[v];
        Inc(FNrFunctions);
      end
      // AV: added some universal variations
      else if (vn = 'affine3D') or (vn = 'trianglecrop') or (vn = 'projective')
      or (vn = 'spherecrop') then
      begin
        GetVariable(vn + '_mode', mode);
        if (mode = 0) then
        begin
          FCalcFunctionList[FNrFunctions] := FFunctionList[v];
          Inc(FNrFunctions);
        end;
      end;
    end;
  end;

  // Precalc must be called after pre- vars
  if CalculateAngle or CalculateSinCos then
  begin
    if CalculateAngle and CalculateSinCos then
      FCalcFunctionList[FNrFunctions] := PrecalcAll
    else if CalculateAngle then
      FCalcFunctionList[FNrFunctions] := PrecalcAngle
    else //if CalculateSinCos then
      FCalcFunctionList[FNrFunctions] := PrecalcSinCos;
    Inc(FNrFunctions);
  end;

  // Normal variations
  for vn in UsedVars do  // AV: iterate through used variations only
  begin
    if (LeftStr(vn, 4) = 'pre_') or (LeftStr(vn, 5) = 'post_') or (vn = 'flatten')
      then continue
    else if (vn = 'affine3D') or (vn = 'trianglecrop') or (vn = 'projective') 
    or (vn = 'spherecrop') then
    begin
      GetVariable(vn + '_mode', mode);
      if (mode <> 1) then continue;
    end;

    v := GetVariationIndex(vn);
    FCalcFunctionList[FNrFunctions] := FFunctionList[v];
    Inc(FNrFunctions);
  end;

  // Post- variations
  for vn in UsedVars do  // AV: iterate through used variations only
  begin
    if (LeftStr(vn, 5) = 'post_') then
    begin
      v := GetVariationIndex(vn);
      FCalcFunctionList[FNrFunctions] := FFunctionList[v];
      Inc(FNrFunctions);
    end
    // AV: added some universal variations
    else if (vn = 'affine3D') or (vn = 'trianglecrop') or (vn = 'projective')
    or (vn = 'spherecrop') then
    begin
      GetVariable(vn + '_mode', mode);
      if (mode = 2) then
      begin
        v := GetVariationIndex(vn);
        FCalcFunctionList[FNrFunctions] := FFunctionList[v];
        Inc(FNrFunctions);
      end;
    end;
  end;

  UsedVars.Free;

  polar_vpi := vars[6]/pi;
  disc_vpi := vars[7]/pi;
  polar2_vpi := vars[35]/pi;
	p2vv2 := polar2_vpi * 0.5;

  gauss_rnd[0] := random;
  gauss_rnd[1] := random;
  gauss_rnd[2] := random;
  gauss_rnd[3] := random;
  gauss_N := 0;

  rx_sin := sin(vars[22] * pi/2);
  rx_cos := cos(vars[22] * pi/2);
  ry_sin := sin(vars[23] * pi/2);
  ry_cos := cos(vars[23] * pi/2);

  px_sin := sin(vars[27] * pi/2);
  px_cos := cos(vars[27] * pi/2);
  py_sin := sin(vars[28] * pi/2);
  py_cos := cos(vars[28] * pi/2);

  if (p[0,0]<>1) or (p[0,1]<>0) or(p[1,0]<>0) or (p[1,1]<>1) or (p[2,0]<>0) or (p[2,1]<>0) then
  begin
    p00 := p[0][0];
    p01 := p[0][1];
    p10 := p[1][0];
    p11 := p[1][1];
    p20 := p[2][0];
    p21 := p[2][1];

    FCalcFunctionList[FNrFunctions] := DoPostTransform;
    Inc(FNrFunctions);
  end;

end;

procedure TXForm.PrepareInvalidXForm;
begin
  c00 := 1;
  c01 := 0;
  c10 := 0;
  c11 := 1;
  c20 := 0;
  c21 := 0;

  colorC1 := 1;
  colorC2 := 0;

  FNrFunctions := 1;
  SetLength(FCalcFunctionList, 1);
  FCalcFunctionList[0] := DoInvalidOperation;
end;

procedure TXForm.PrecalcAngle;
{$ifndef _ASM_}
begin
  FAngle := arctan2(FTx, FTy);
{$else}
asm
    fld     qword ptr [eax + FTx]
    fld     qword ptr [eax + FTy]
    fpatan
    fstp    qword ptr [eax + FAngle]
    //fwait
{$endif}
end;

procedure TXForm.PrecalcSinCos;
{$ifndef _ASM_}
begin
  FLength := sqrt(sqr(FTx) + sqr(FTy)) + EPS;
  FSinA := FTx / FLength;
  FCosA := FTy / FLength;
{$else}
asm
    fld     qword ptr [eax + FTx]
    fld     qword ptr [eax + FTy]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fsqrt
    fadd    qword ptr [EPS] // avoid divide by zero...(?)
    fdiv    st(1), st
    fdiv    st(2), st
    fstp    qword ptr [eax + FLength]
    fstp    qword ptr [eax + FCosA]
    fstp    qword ptr [eax + FSinA]
    //fwait
{$endif}
end;

procedure TXForm.PrecalcAll;
{$ifndef _ASM_}
begin
  FLength := sqrt(sqr(FTx) + sqr(FTy)) + EPS;
  FSinA := FTx / FLength;
  FCosA := FTy / FLength;
  FAngle := arctan2(FTx, FTy);
{$else}
asm
    fld     qword ptr [eax + FTx]
    fld     qword ptr [eax + FTy]
    fld     st(1)
    fld     st(1)
    fpatan
    fstp    qword ptr [eax + FAngle]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fsqrt
    fadd    qword ptr [EPS] // avoid divide by zero...(?)
    fdiv    st(1), st
    fdiv    st(2), st
    fstp    qword ptr [eax + FLength]
    fstp    qword ptr [eax + FCosA]
    fstp    qword ptr [eax + FSinA]
    //fwait
{$endif}
end;

procedure TXForm.DoPostTransform;
{$ifndef _ASM_}
var
  tmp: double;
begin
  tmp := FPx;
  FPx := p00 * FPx + p10 * FPy + p20;
  FPy := p01 * tmp + p11 * FPy + p21;
{$else}
asm
    fld     qword ptr [eax + FPy]
    fld     qword ptr [eax + FPx]
    fld     st(1)
    fmul    qword ptr [eax + p10]
    fld     st(1)
    fmul    qword ptr [eax + p00]
    faddp
    fadd    qword ptr [eax + p20]
    fstp    qword ptr [eax + FPx]
    fmul    qword ptr [eax + p01]
    fld     qword ptr [eax + p11]
    fmulp   st(2), st
    faddp
    fadd    qword ptr [eax + p21]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

procedure TXForm.DoInvalidOperation;
begin
  raise EMathError.Create('FCalcFunction not initialized!? Probably corrupted flame.');
end;

//--0--////////////////////////////////////////////////////////////////////////
procedure TXForm.Linear3D;
{$ifndef _ASM_}
begin
  FPx := FPx + vars[0] * FTx;
  FPy := FPy + vars[0] * FTy;
  FPz := FPz + vars[0] * FTz;
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx]

    fld     qword ptr [eax + FTz]
    fmul    st, st(1)
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]

    fld     qword ptr [eax + FTx]
    fmul    st, st(1)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fld     qword ptr [eax + FTy]
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Flatten;
begin
	// FPz := 0;
  // AV: changed to pre_mode for compatibility with "3D-Hack" flames
    FTz := 0;
end;

//--1--////////////////////////////////////////////////////////////////////////
procedure TXForm.PreBlur3D;  // AV
var
  r, sina, cosa, sinb, cosb: double;
begin
  Randomize; 
  SinCos(random * 2 * pi, sina, cosa);
  r := vars[2] * (gauss_rnd[0] + gauss_rnd[1] + gauss_rnd[2] + gauss_rnd[3] - 2);
  gauss_rnd[gauss_N] := random;
  gauss_N := (gauss_N+1) and $3;

  SinCos(random * pi, sinb, cosb);
  FTx := FTx + r * sinb * cosa;
  FTy := FTy + r * sinb * sina;
  FTz := FTz + r * cosb;
end;

//--2--////////////////////////////////////////////////////////////////////////
procedure TXForm.Spherical;
{$ifndef _ASM_}
var
  r: double;
begin
  r := vars[3] / (sqr(FTx) + sqr(FTy) + EPS);
  FPx := FPx + FTx * r;
  FPy := FPy + FTy * r;
  FPz := FPz + FTz * vars[3];
{$else}
asm
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fadd    qword ptr [EPS]
    mov     edx, [eax + vars]
    // AV: for Apo7X.15C compatibility
    fld     qword ptr [edx + 3*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    // main code
    fdivr   qword ptr [edx + 3*8]
    fmul    st(2), st
    fmulp
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//--3--////////////////////////////////////////////////////////////////////////
procedure TXForm.Swirl;
{$ifndef _ASM_}
var
  sinr, cosr: double;
begin
  SinCos(sqr(FTx) + sqr(FTy), sinr, cosr);
  FPx := FPx + vars[4] * (sinr * FTx - cosr * FTy);
  FPy := FPy + vars[4] * (cosr * FTx + sinr * FTy);
  FPz := FPz + FTz * vars[4];
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 4*8]
    // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fsincos
    fld     st(1)
    fmul    st, st(3)
    fld     st(1)
    fmul    st, st(5)
    fsubp   st(1), st
    fmul    st, st(5)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp    st(2), st
    fmulp    st(2), st
    faddp
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//--4--////////////////////////////////////////////////////////////////////////
procedure TXForm.Horseshoe;
{$ifndef _ASM_}
var
  r: double;
begin
  r := vars[5] / (sqrt(sqr(FTx) + sqr(FTy)) + EPS);
  FPx := FPx + (FTx - FTy) * (FTx + FTy) * r;
  FPy := FPy + (2*FTx*FTy) * r;
  FPz := FPz + FTz * vars[5];
{$else}
asm
    fld     qword ptr [eax + FTx]
    fld     qword ptr [eax + FTy]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fsqrt
    fadd    qword ptr [EPS]
    mov     edx, [eax + vars]
    // AV: for Apo7X.15C compatibility
    fld     qword ptr [edx + 5*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    // main code
    fdivr   qword ptr [edx + 5*8]
    fld     st(2)
    fadd    st, st(2)
    fld     st(3)
    fsub    st, st(3)
    fmulp
    fmul    st, st(1)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp
    fmulp
    fadd    st, st
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//--5--////////////////////////////////////////////////////////////////////////
procedure TXForm.Polar;
{$ifndef _ASM_}
begin
  FPx := FPx + polar_vpi * FAngle; //vars[5] * FAngle / PI;
  FPy := FPy + vars[6] * (sqrt(sqr(FTx) + sqr(FTy)) - 1.0);
  FPz := FPz + FTz * vars[6];
 {$else}
asm
    fld     qword ptr [eax + FAngle]
    fmul    qword ptr [eax + polar_vpi]
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fld     qword ptr [eax + FTx]
    fmul    st, st
    fld     qword ptr [eax + FTy]
    fmul    st, st
    faddp
    fsqrt
    fld1
    fsubp   st(1), st
    mov     edx, [eax + vars]
     // AV: for Apo7X.15C compatibility
    fld     qword ptr [edx + 6*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    // main code
    fmul    qword ptr [edx + 6*8]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//--6--////////////////////////////////////////////////////////////////////////
procedure TXForm.Disc;
{$ifndef _ASM_}
var
  r, sinr, cosr: double;
begin
  SinCos(PI * sqrt(sqr(FTx) + sqr(FTy)), sinr, cosr);
  r := disc_vpi * FAngle; //vars[7] * FAngle / PI;
  FPx := FPx + sinr * r;
  FPy := FPy + cosr * r;
  FPz := FPz + FTz * vars[7];
{$else}
asm
    // AV: 3D stuff
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 7*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    fld     qword ptr [eax + disc_vpi]
    fmul    qword ptr [eax + FAngle]
    fld     qword ptr [eax + FTx]
    fmul    st, st
    fld     qword ptr [eax + FTy]
    fmul    st, st
    faddp
    fsqrt
    fldpi
    fmulp
    fsincos
    fmul    st, st(2)
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fmulp
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fwait
{$endif}
end;

//--7--////////////////////////////////////////////////////////////////////////
procedure TXForm.Spiral;
{$ifndef _ASM_}
var
  r, sinr, cosr: double;
begin
  r := Flength + EPS; // 1E-6;
  SinCos(r, sinr, cosr);
  r := vars[8] / r;
  FPx := FPx + (FCosA + sinr) * r;
  FPy := FPy + (FsinA - cosr) * r;
  FPz := FPz + FTz * vars[8];
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 8*8]
     // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    fld     qword ptr [eax + FLength]
    fadd    qword ptr [EPS]
    fdiv    st(1), st
    fsincos
    fsubr   qword ptr [eax + FSinA]
    fmul    st, st(2)
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fadd    qword ptr [eax + FCosA]
    fmulp
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fwait
{$endif}
end;

//--10--///////////////////////////////////////////////////////////////////////
procedure TXForm.Hyperbolic;
{$ifndef _ASM_}
begin
  FPx := FPx + vars[9] * FTx / (sqr(FTx) + sqr(FTy) + EPS);
  FPy := FPy + vars[9] * FTy;
  FPz := FPz + FTz * vars[9];
  {$else}
 asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 9*8]
      // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fadd    qword ptr [EPS]
    fdivp   st(1), st
    fmul    st, st(2)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//--11--///////////////////////////////////////////////////////////////////////
procedure TXForm.Diamond;  // AV: Diamond, i.e. rotated Square
 {$ifndef _ASM_}
var
  sinr, cosr: double;
begin
  SinCos(FLength, sinr, cosr);
  FPx := FPx + vars[10] * FSinA * cosr;
  FPy := FPy + vars[10] * FCosA * sinr;
  FPz := FPz + FTz * vars[10];
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 10*8]
    // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    fld     qword ptr [eax + FLength]
    fsincos
    fmul    qword ptr [eax + FSinA]
    fmul    st, st(2)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmul    qword ptr [eax + FCosA]
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//--12--///////////////////////////////////////////////////////////////////////
procedure TXForm.Eyefish;
 {$ifndef _ASM_}
var
  r: double;
begin
  r := 2 * vars[11] / (sqrt(sqr(FTx) + sqr(FTy)) + 1);
  FPx := FPx + r * FTx;
  FPy := FPy + r * FTy;
  FPz := FPz + FTz * vars[11];
 {$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 11*8]
      // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    fadd    st, st
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fsqrt
    fld1
    faddp
    fdivp   st(3), st
    fmul    st, st(2)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Bubble;
{$ifndef _ASM_}
var
  r: double;
begin
  r := (sqr(FTx) + sqr(FTy))/4 + 1;
  FPz := FPz + vars[12] * (2 / r - 1);

  r := vars[12] / r;

  FPx := FPx + r * FTx;
  FPy := FPy + r * FTy;
{$else}
asm
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fld1
    fadd    st, st
    fadd    st, st
    fdivp   st(1), st

    mov     edx, [eax + vars]
    fld     qword ptr [edx + 12*8]

    fld1
    fadd    st(2), st
    fdivr   st(2), st

    fld     st(2)
    fadd    st, st
    fsubrp  st(1), st
    fmul    st, st(1)
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]

    fmulp

    fmul    st(2), st
    fmulp
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Cylinder;
{$ifndef _ASM_}
begin
  FPx := FPx + vars[13] * sin(FTx);
  FPy := FPy + vars[13] * FTy;
  FPz := FPz + vars[13] * cos(FTx);
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 13*8]
    fld     qword ptr [eax + FTx]
    fsincos
    fmul    st, st(2)
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    fld     qword ptr [eax + FTy]
    fmul    st, st(2)
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fmulp
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Noise;
{$ifndef _ASM_}
var
  r, s, sinr, cosr: double;
begin
  // Randomize here = HACK! Fix me...
  Randomize; SinCos(random * 2*pi, sinr, cosr);
  s := vars[14];
  r := s * random;
  FPx := FPx + FTx * r * cosr;
  FPy := FPy + FTy * r * sinr;
  FPz := FPz + FTz * s;
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 14*8]
    // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    call    AsmRandExt
    fmulp
    call    AsmRandExt
    fadd    st, st
    fldpi
    fmulp
    fsincos
    fmul    st, st(2)
    fmul    qword ptr [eax + FTx]
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp
    fmul    qword ptr [eax + FTy]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Blur;
{$ifndef _ASM_}
var
  r, sina, cosa: double;
begin
  // Randomize here = HACK! Fix me...
  Randomize; SinCos(random * 2*pi, sina, cosa);
  r := vars[15] * random;
  FPx := FPx + r * cosa;
  FPy := FPy + r * sina;
  FPz := FPz + vars[15] * FTz;
{$else}
asm
    mov     edx, [eax + vars]
    fld     qword ptr [edx + 15*8]
    // AV: 3D stuff
    fld     st
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
     // main calculations
    call    AsmRandExt
    fmulp
    call    AsmRandExt
    fadd    st, st
    fldpi
    fmulp
    fsincos
    fmul    st, st(2)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Gaussian;
{$ifndef _ASM_}
var
  r, sina, cosa: double;
begin
  // Randomize here = HACK! Fix me...
  Randomize; SinCos(random * 2*pi, sina, cosa);
  r := vars[16] * (gauss_rnd[0] + gauss_rnd[1] + gauss_rnd[2] + gauss_rnd[3] - 2);
  gauss_rnd[gauss_N] := random;
  gauss_N := (gauss_N+1) and $3;

  FPx := FPx + r * cosa;
  FPy := FPy + r * sina;
  //FPz := FPz + vars[16] * FTz;  // AV removed
{$else}
asm
    fld     qword ptr [eax + gauss_rnd]
    fadd    qword ptr [eax + gauss_rnd+8]
    fadd    qword ptr [eax + gauss_rnd+16]
    fadd    qword ptr [eax + gauss_rnd+24]
    fld1
    fadd    st,st
    fsubp   st(1),st
    mov     edx, [eax + vars]
    // AV: removed 3D-support since we have zcale and blur3D
    {
	fld     qword ptr [edx + 16*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz] 
	}
    fmul    qword ptr [edx + 16*8]
    call    AsmRandExt
    mov     edx, [eax + gauss_N]
    fst     qword ptr [eax + gauss_rnd + edx*8]
    inc     edx
    and     edx,$03
    mov     [eax + gauss_N], edx 

    fadd    st, st
    fldpi
    fmulp
    fsincos
    fmul    st, st(2)
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fmulp
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.ZBlur;
{$ifndef _ASM_}
begin
  FPz := FPz + vars[17] * (gauss_rnd[0] + gauss_rnd[1] + gauss_rnd[2] + gauss_rnd[3] - 2);
  gauss_rnd[gauss_N] := random;
  gauss_N := (gauss_N+1) and $3;
{$else}
asm
    fld     qword ptr [eax + gauss_rnd]
    fadd    qword ptr [eax + gauss_rnd+8]
    fadd    qword ptr [eax + gauss_rnd+16]
    fadd    qword ptr [eax + gauss_rnd+24]
    fld1
    fadd    st,st
    fsubp   st(1),st
    mov     edx, [eax + vars]
    fmul    qword ptr [edx + 17*8]
    call    AsmRandExt
    mov     edx, [eax + gauss_N]
    fstp    qword ptr [eax + gauss_rnd + edx*8]
    inc     edx
    and     edx,$03
    mov     [eax + gauss_N], edx 

    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Blur3D;
var
  r, sina, cosa, sinb, cosb: double;
begin
  // Randomize here = HACK! Fix me...
  Randomize; SinCos(random * 2*pi, sina, cosa);
  r := vars[18] * (gauss_rnd[0] + gauss_rnd[1] + gauss_rnd[2] + gauss_rnd[3] - 2);
  gauss_rnd[gauss_N] := random;
  gauss_N := (gauss_N+1) and $3;

  SinCos(random * pi, sinb, cosb);
  FPx := FPx + r * sinb * cosa;
  FPy := FPy + r * sinb * sina;
  FPz := FPz + r * cosb;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PreBlur;
{$ifndef _ASM_}
var
  r, sina, cosa: double;
begin
  // Randomize here = HACK! Fix me...
  Randomize; SinCos(random * 2*pi, sina, cosa);
  r := vars[19] * (gauss_rnd[0] + gauss_rnd[1] + gauss_rnd[2] + gauss_rnd[3] - 2);
  gauss_rnd[gauss_N] := random;
  gauss_N := (gauss_N+1) and $3;

  FTx := FTx + r * cosa;
  FTy := FTy + r * sina;
{$else}
asm
    fld     qword ptr [eax + gauss_rnd]
    fadd    qword ptr [eax + gauss_rnd+8]
    fadd    qword ptr [eax + gauss_rnd+16]
    fadd    qword ptr [eax + gauss_rnd+24]
    fld1
    fadd    st,st
    fsubp   st(1),st
    mov     edx, [eax + vars]
    fmul    qword ptr [edx + 19*8]
    call    AsmRandExt
    mov     edx, [eax + gauss_N]
    fst     qword ptr [eax + gauss_rnd + edx*8]
    inc     edx
    and     edx,$03
    mov     [eax + gauss_N], edx  

    fadd    st, st
    fldpi
    fmulp
    fsincos
    fmul    st, st(2)
    fadd    qword ptr [eax + FTx]
    fstp    qword ptr [eax + FTx]
    fmulp
    fadd    qword ptr [eax + FTy]
    fstp    qword ptr [eax + FTy]
    fwait
{$endif}
end;


///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PreZScale;
begin
  FTz := FTz * vars[20];
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PreZTranslate;
begin
  FTz := FTz + vars[21];
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PreRotateX;
var
  z: double;
begin
    z := rx_cos * FTz - rx_sin * FTy;
  FTy := rx_sin * FTz + rx_cos * FTy;
  FTz := z;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PreRotateY;
var
  x: double;
begin
    x := ry_cos * FTx - ry_sin * FTz;
  FTz := ry_sin * FTx + ry_cos * FTz;
  FTx := x;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.ZScale;
{$ifndef _ASM_}
begin
  FPz := FPz + vars[24] * FTz;
{$else}
asm
    fld     qword ptr [eax + FTz]
    mov     edx, [eax + vars]
    fmul    qword ptr [edx + 24*8]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.ZTranslate;
begin
  FPz := FPz + vars[25];
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.ZCone;
{$ifndef _ASM_}
begin
  FPz := FPz + vars[26] * sqrt(sqr(FTx) + sqr(FTy));
{$else}
asm
    fld     qword ptr [eax + FTx]
    fmul    st,st
    fld     qword ptr [eax + FTy]
    fmul    st,st
    faddp
    fsqrt
    mov     edx, [eax + vars]
    fmul    qword ptr [edx + 26*8]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    fwait
{$endif}
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PostRotateX;
var
  z: double;
begin
    z := px_cos * FPz - px_sin * FPy;
  FPy := px_sin * FPz + px_cos * FPy;
  FPz := z;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PostRotateY;
var
  x: double;
begin
    x := py_cos * FPx - py_sin * FPz;
  FPz := py_sin * FPx + py_cos * FPz;
  FPx := x;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PostMirrorX;
begin
  FPx := abs(FPx);
  if (random(2) = 1) then FPx := -FPx;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PostMirrorY;
begin
  FPy := abs(FPy);
  if (random(2) = 1) then FPy := -FPy;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.PostMirrorZ;
begin
  FPz := abs(FPz);
  if (random(2) = 1) then FPz := -FPz;
end;

/////////////////////////////////
procedure TXForm.Hemisphere;
{$ifndef _ASM_}
var
  t: double;
begin
  t := vars[32] / sqrt(sqr(FTx) + sqr(FTy) + 1);

  FPx := FPx + FTx * t;
  FPy := FPy + FTy * t;
  FPz := FPz + t;
{$else}
  asm    // rewritten by AV 
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st(1)
    fmul    st, st
    fld     st(1)
    fmul    st, st
    faddp
    fld1
    faddp
    fsqrt
    mov     edx, [eax + vars]
    fdivr   qword ptr [edx + 32*8]
    fmul    st(2), st
    fmul    st(1), st
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

//////////////////////////////
procedure TXForm.Cross;
const epsv: double = 1.0e-20;
 {$ifndef _ASM_}
var
  r: double;
begin
  r := abs((FTx - FTy) * (FTx + FTy)) + epsv; // AV: fixed
  // AV: abs cannot be negative, so it's useless
  //if (r < 0) then r := r * -1.0;
  r := vars[33] / r;

  FPx := FPx + FTx * r;
  FPy := FPy + FTy * r;
  FPz := FPz + vars[33] * FTz;
{$else}
  asm    //written by AV  
    fld     qword ptr [eax + FTy]
    fld     qword ptr [eax + FTx]
    fld     st
    fsub    st, st(2)
    fld     st(1)
    fadd    st, st(3)
    fmulp
    fabs
    fadd    epsv
    mov     edx, [eax + vars]
    // for Apo7X.15C compatibility
    fld     qword ptr [edx + 33*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    // main code
    fdivr   qword ptr [edx + 33*8]
    fmul    st(2), st
    fmulp
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;

/////////////////////////////
procedure TXForm.Pyramid;
var
	x, y, z, r: double;
begin
	x := IntPower(FTx, 3);
	y := IntPower(FTy, 3);
	z := IntPower(abs(FTz), 3);
	r := vars[34] / (abs(x) + abs(y) + z + 1E-9);
	FPx := FPx + x * r;
	FPy := FPy + y * r;
	FPz := FPz + z * r;
end;
////////////////////////////////////////////////

procedure TXForm.Polar2;
{$ifndef _ASM_}
begin
  FPx := FPx + polar2_vpi * FAngle;
  FPy := FPy + p2vv2 * Ln(sqr(FTx) + sqr(FTy) + EPS);
  FPz := FPz + FTz * vars[35];
 {$else}
asm   // written by AV
    fld     qword ptr [eax + FAngle]
    fmul    qword ptr [eax + polar2_vpi]
    fadd    qword ptr [eax + FPx]
    fstp    qword ptr [eax + FPx]

    fld     qword ptr [eax + FTx]
    fmul    st, st
    fld     qword ptr [eax + FTy]
    fmul    st, st
    faddp
    fadd    qword ptr [EPS]
    fldln2
    fxch
    fyl2x
    fmul    qword ptr [eax + p2vv2]
    mov     edx, [eax + vars]
     // AV: for Apo7X.15C compatibility
    fld     qword ptr [edx + 35*8]
    fmul    qword ptr [eax + FTz]
    fadd    qword ptr [eax + FPz]
    fstp    qword ptr [eax + FPz]
    // main code
    fmul    qword ptr [edx + 35*8]
    fadd    qword ptr [eax + FPy]
    fstp    qword ptr [eax + FPy]
    fwait
{$endif}
end;
///////////////////////////////////////////////////////////////////////////////

procedure TXForm.NextPoint(var CPpoint: TCPpoint);
var
  i: Integer;
begin
  // first compute the color coord
  CPpoint.c := CPpoint.c * colorC1 + colorC2;
  vc := CPpoint.c;

  FTx := c00 * CPpoint.x + c10 * CPpoint.y + c20;
  FTy := c01 * CPpoint.x + c11 * CPpoint.y + c21;
  FTz := CPpoint.z;

  Fpx := 0;
  Fpy := 0;
  Fpz := 0;

  for i:= 0 to FNrFunctions-1 do
    FCalcFunctionList[i];

  CPpoint.c := CPpoint.c + pluginColor * (vc - CPpoint.c);
  CPpoint.x := FPx;
  CPpoint.y := FPy;
  CPPoint.z := FPz;
end;

procedure TXForm.NextPointTo(var CPpoint, ToPoint: TCPpoint);
var
  i: Integer;
begin
  ToPoint.c := CPpoint.c * colorC1 + colorC2;
  vc := ToPoint.c;

  FTx := c00 * CPpoint.x + c10 * CPpoint.y + c20;
  FTy := c01 * CPpoint.x + c11 * CPpoint.y + c21;
  FTz := CPpoint.z;

  Fpx := 0;
  Fpy := 0;
  Fpz := 0;

  for i:= 0 to FNrFunctions-1 do
    FCalcFunctionList[i];

  ToPoint.c := ToPoint.c + pluginColor * (vc - ToPoint.c);
  ToPoint.x := FPx;
  ToPoint.y := FPy;
  ToPoint.z := FPz; 
end;

///////////////////////////////////////////////////////////////////////////////
(*
procedure TXForm.NextPoint2C(var p: T2CPoint);
var
  i: Integer;
begin
  // first compute the color coord
  p.c1 := p.c1 * colorC1 + colorC2;
  p.c2 := p.c2 * colorC1 + colorC2;

  FTx := c00 * p.x + c10 * p.y + c20;
  FTy := c01 * p.x + c11 * p.y + c21;

  Fpx := 0;
  Fpy := 0;

  for i:= 0 to FNrFunctions-1 do
    FCalcFunctionList[i];

  p.x := FPx;
  p.y := FPy;
end;
*)
///////////////////////////////////////////////////////////////////////////////
procedure TXForm.NextPointXY(var px, py: double);
var
  i: integer;
begin
  FTx := c00 * px + c10 * py + c20;
  FTy := c01 * px + c11 * py + c21;
  FTz := 0;  

  Fpx := 0;
  Fpy := 0;

  for i:= 0 to FNrFunctions-1 do
    FCalcFunctionList[i];

  px := FPx;
  py := FPy;
end;

//************ Math utils ***************************************//

function TXForm.detC;
begin
  Result := c[0,0] * c[1,1] - c[0,1] * c[1,0];
end;

function TXForm.detP;
begin
  Result := p[0,0] * p[1,1] - p[0,1] * p[1,0];
end;

//************ Matrix multiplication ***************************************//

procedure TXForm.Multiply(var t: TCoefsArray; const k, l, m, n: double); // AV
var
  ta, tb, tc, td: double;
begin
  ta := t[0, 0];
  tb := -t[1, 0];
  tc := -t[0, 1];
  td := t[1, 1];

{
    [a, b][e ,f]   [a*e+b*g, a*f+b*h]
    [    ][    ] = [                ]
    [c, d][g, h]   [c*e+d*g, c*f+d*h]
}

  t[0, 0] := ta * k + tc * l;
  t[0, 1] := -(ta * m + tc * n);
  t[1, 0] := -(tb * k + td * l);
  t[1, 1] := tb * m + td * n;
end;

procedure TXForm.Rotate(var t: TCoefsArray; const degrees: double); // AV
var
  v, sv, cv: double;
begin
  v := DegToRad(degrees);
  SinCos(v, sv, cv);
  Multiply(t, cv, -sv, sv, cv);
end;

procedure TXForm.Scale(var t: TCoefsArray; const s: double);
begin
  t[0, 0] := t[0, 0] * s;
  t[0, 1] := t[0, 1] * s;
  t[1, 0] := t[1, 0] * s;
  t[1, 1] := t[1, 1] * s;
end;

procedure TXForm.Translate(var t: TCoefsArray; const x, y: double);
begin
	  t[2,0] := t[2,0] + x;
    t[2,1] := t[2,1] - y;  // AV: notice the sign here
end;

procedure TXForm.RandomizeCoefs(var t: TCoefsArray); // AV
begin
  t[0][0] := 2 * random - 1;
  t[0][1] := 2 * random - 1;
  t[1][0] := 2 * random - 1;
  t[1][1] := 2 * random - 1;
  t[2][0] := 4 * random - 2;
  t[2][1] := 4 * random - 2;
end;

(*
///////////////////////////////////////////////////////////////////////////////

{
[00 01 02]
[10 11 12]
[20 21 22]

[a  b  e ]
[c  d  f ]
[0  0  1 ]
}

function TXForm.Mul33(const M1, M2: TMatrix): TMatrix;
begin
  result[0, 0] := M1[0][0] * M2[0][0] + M1[0][1] * M2[1][0] + M1[0][2] * M2[2][0];
  result[0, 1] := M1[0][0] * M2[0][1] + M1[0][1] * M2[1][1] + M1[0][2] * M2[2][1];
  result[0, 2] := M1[0][0] * M2[0][2] + M1[0][1] * M2[1][2] + M1[0][2] * M2[2][2];
  result[1, 0] := M1[1][0] * M2[0][0] + M1[1][1] * M2[1][0] + M1[1][2] * M2[2][0];
  result[1, 1] := M1[1][0] * M2[0][1] + M1[1][1] * M2[1][1] + M1[1][2] * M2[2][1];
  result[1, 2] := M1[1][0] * M2[0][2] + M1[1][1] * M2[1][2] + M1[1][2] * M2[2][2];
  result[2, 0] := M1[2][0] * M2[0][0] + M1[2][1] * M2[1][0] + M1[2][2] * M2[2][0];
  result[2, 1] := M1[2][0] * M2[0][1] + M1[2][1] * M2[1][1] + M1[2][2] * M2[2][1];  // AV: fixed indices
  result[2, 2] := M1[2][0] * M2[0][2] + M1[2][1] * M2[1][2] + M1[2][2] * M2[2][2];  // AV: fixed indices
end;

function TXForm.Identity: TMatrix;
var
  i, j: integer;
begin
  for i := 0 to 2 do
    for j := 0 to 2 do
      Result[i, j] := 0;
  Result[0][0] := 1;
  Result[1][1] := 1;
  Result[2][2] := 1;
end;

///////////////////////////////////////////////////////////////////////////////

procedure TXForm.Rotate(const degrees: double);
var
  r: double;
  Matrix, M1: TMatrix;
begin
  r := degrees * pi / 180;
  M1 := Identity;
  M1[0, 0] := cos(r);
  M1[0, 1] := -sin(r);
  M1[1, 0] := sin(r);
  M1[1, 1] := cos(r);
  Matrix := Identity;

  Matrix[0][0] := c[0, 0];
  Matrix[0][1] := c[0, 1];
  Matrix[1][0] := c[1, 0];
  Matrix[1][1] := c[1, 1];
  Matrix[0][2] := c[2, 0];
  Matrix[1][2] := c[2, 1];
  Matrix := Mul33(Matrix, M1);
  c[0, 0] := Matrix[0][0];
  c[0, 1] := Matrix[0][1];
  c[1, 0] := Matrix[1][0];
  c[1, 1] := Matrix[1][1];
  c[2, 0] := Matrix[0][2];
  c[2, 1] := Matrix[1][2];
end;

procedure TXForm.Translate(const x, y: double);
var
  Matrix, M1: TMatrix;
begin
  M1 := Identity;
  M1[0, 2] := x;
  M1[1, 2] := y;
  Matrix := Identity;

  Matrix[0][0] := c[0, 0];
  Matrix[0][1] := c[0, 1];
  Matrix[1][0] := c[1, 0];
  Matrix[1][1] := c[1, 1];
  Matrix[0][2] := c[2, 0];
  Matrix[1][2] := c[2, 1];
  Matrix := Mul33(Matrix, M1);
  c[0, 0] := Matrix[0][0];
  c[0, 1] := Matrix[0][1];
  c[1, 0] := Matrix[1][0];
  c[1, 1] := Matrix[1][1];
  c[2, 0] := Matrix[0][2];
  c[2, 1] := Matrix[1][2];
end;

procedure TXForm.Multiply(const a, b, c, d: double);
var
  Matrix, M1: TMatrix;
begin
  M1 := Identity;
  M1[0, 0] := a;
  M1[0, 1] := b;
  M1[1, 0] := c;
  M1[1, 1] := d;
  Matrix := Identity;
  Matrix[0][0] := Self.c[0, 0];
  Matrix[0][1] := Self.c[0, 1];
  Matrix[1][0] := Self.c[1, 0];
  Matrix[1][1] := Self.c[1, 1];
  Matrix[0][2] := Self.c[2, 0];
  Matrix[1][2] := Self.c[2, 1];
  Matrix := Mul33(Matrix, M1);
  Self.c[0, 0] := Matrix[0][0];
  Self.c[0, 1] := Matrix[0][1];
  Self.c[1, 0] := Matrix[1][0];
  Self.c[1, 1] := Matrix[1][1];
  Self.c[2, 0] := Matrix[0][2];
  Self.c[2, 1] := Matrix[1][2];
end;

procedure TXForm.Scale(const s: double);
var
  Matrix, M1: TMatrix;
begin
  M1 := Identity;
  M1[0, 0] := s;
  M1[1, 1] := s;
  Matrix := Identity;
  Matrix[0][0] := c[0, 0];
  Matrix[0][1] := c[0, 1];
  Matrix[1][0] := c[1, 0];
  Matrix[1][1] := c[1, 1];
  Matrix[0][2] := c[2, 0];
  Matrix[1][2] := c[2, 1];
  Matrix := Mul33(Matrix, M1);
  c[0, 0] := Matrix[0][0];
  c[0, 1] := Matrix[0][1];
  c[1, 0] := Matrix[1][0];
  c[1, 1] := Matrix[1][1];
  c[2, 0] := Matrix[0][2];
  c[2, 1] := Matrix[1][2];
end;
*)
///////////////////////////////////////////////////////////////////////////////
destructor TXForm.Destroy;
var
  i: integer;
begin

  for i := 0 to High(FRegVariations) do
    FRegVariations[i].Free;

  if assigned(ifs) then ifs.Clear;
  ifs.Free;  // AV

  inherited;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.BuildFunctionlist;
begin
  // AV: why NRVAR is used here? maybe NRLOCVAR instead?
  //SetLength(FFunctionList, NrVar + Length(FRegVariations));
   SetLength(FFunctionList, NrVar); // <-- AV: reduced the length to actually used

  //fixed
  FFunctionList[0] := Linear3D;
  FFunctionList[1] := Flatten;   // AV: replased 2D-Linear
  FFunctionList[2] := PreBlur3D;  // AV: replaced Sinusoidal
  FFunctionList[3] := Spherical;
  FFunctionList[4] := Swirl;
  FFunctionList[5] := Horseshoe;
  FFunctionList[6] := Polar;
  FFunctionList[7] := Disc;
  FFunctionList[8] := Spiral;
  FFunctionList[9] := Hyperbolic;
  FFunctionList[10] := Diamond; // AV: renamed from Square
  FFunctionList[11] := Eyefish;
  FFunctionList[12] := Bubble;
  FFunctionList[13] := Cylinder;
  FFunctionList[14] := Noise;
  FFunctionList[15] := Blur;
  FFunctionList[16] := Gaussian;
  FFunctionList[17] := ZBlur;
  FFunctionList[18] := Blur3D;

  FFunctionList[19] := PreBlur;
  FFunctionList[20] := PreZScale; // AV: index is used by auto-pre_zscale option
  FFunctionList[21] := PreZTranslate;
  FFunctionList[22] := PreRotateX;
  FFunctionList[23] := PreRotateY;

  FFunctionList[24] := ZScale;
  FFunctionList[25] := ZTranslate;
  FFunctionList[26] := ZCone;

  FFunctionList[27] := PostRotateX;
  FFunctionList[28] := PostRotateY;
  // AV: new local variations
  FFunctionList[29] := PostMirrorX;
  FFunctionList[30] := PostMirrorY;
  FFunctionList[31] := PostMirrorZ;

  FFunctionList[32] := Hemisphere;
  FFunctionList[33] := Cross;
  FFunctionList[34] := Pyramid;
  FFunctionList[35] := Polar2;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.AddRegVariations;
var
  i: integer;
begin
  SetLength(FRegVariations, GetNrRegisteredVariations);
  for i := 0 to GetNrRegisteredVariations - 1 do begin
    FRegVariations[i] := GetRegisteredVariation(i).GetInstance;
  end;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.Assign(XForm: TXForm);
var
  i,j: integer;
  Name: string;
  Value: double;
begin
  if Not assigned(XForm) then
    Exit;

  for i := 0 to High(vars) do
    vars[i] := XForm.vars[i];

  c := Xform.c;
  p := Xform.p;
  density := XForm.density;
  color := XForm.color;
  color2 := XForm.color2;
  symmetry := XForm.symmetry;
 // Orientationtype := XForm.Orientationtype;
  TransformName := XForm.TransformName;

  postXswap := Xform.postXswap;
  autoZscale := Xform.autoZscale;

  for i := 0 to High(FRegVariations)  do begin
    for j := 0 to FRegVariations[i].GetNrVariables - 1 do begin
      Name := FRegVariations[i].GetVariableNameAt(j);
      XForm.FRegVariations[i].GetVariable(Name, Value);
      FRegVariations[i].SetVariable(Name, Value);
    end;
  end;

  for i := 0 to High(modWeights) do
    modWeights[i] := xform.modWeights[i];

  ifs.Assign(xform.ifs);  // AV

  transOpacity := xform.transOpacity;
  pluginColor := xform.pluginColor;
end;

///////////////////////////////////////////////////////////////////////////////
function TXForm.ToXMLString: string;
var
  i, j: integer;
  Name: string;
  Value: double;
  numChaos: integer;
  strvar: string;
begin
  result := Format('   <xform weight="%g" color="%g" ', [density, color]);
  if symmetry <> 0 then result := result + format('symmetry="%g" ', [symmetry]);

  // AV: write variation order
  strvar := '';
  for Name in ifs do begin
    if vars[GetVariationIndex(Name)] <> 0 then
      strvar := strvar + Name + #32;
  end;
  if (strvar <> '') and (pos(#32, strvar) < length(strvar)) then
	  Result := Result + format('var_order="%s" ', [strvar]);
  ///////////////////////
(*
  for i := 0 to nrvar - 1 do begin
    if vars[i] <> 0 then
      Result := Result + varnames(i) + format('="%g" ', [vars[i]]);
  end;
  *)
  for Name in ifs do  // AV: write in correct order
  begin
	  Value := vars[GetVariationIndex(Name)];
	  if Value <> 0 then
      Result := Result + Name + format('="%g" ', [Value]);
  end;

  Result := Result + Format('coefs="%g %g %g %g %g %g" ', [c[0,0], c[0,1], c[1,0], c[1,1], c[2,0], c[2,1]]);
  if (p[0,0]<>1) or (p[0,1]<>0) or(p[1,0]<>0) or (p[1,1]<>1) or (p[2,0]<>0) or (p[2,1]<>0) then
    Result := Result + Format('post="%g %g %g %g %g %g" ', [p[0,0], p[0,1], p[1,0], p[1,1], p[2,0], p[2,1]]);

  for i := 0 to High(FRegVariations)  do begin
    if vars[i+NRLOCVAR] <> 0 then
      for j := 0 to FRegVariations[i].GetNrVariables - 1 do begin
        Name := FRegVariations[i].GetVariableNameAt(j);
        Result := Result + Format('%s="%s" ', [name, FRegVariations[i].GetVariableStr(Name)]);
      end;
  end;

  numChaos := -1;
  for i := NXFORMS-1 downto 0 do
    if modWeights[i] <> 1 then begin
      numChaos := i;
      break;
    end;
  if numChaos >= 0 then begin
    Result := Result + 'chaos="';
    for i := 0 to numChaos do
      Result := Result + Format('%g ', [modWeights[i]]);
    Result := Result + '" ';
  end;

  Result := Result + Format('opacity="%g" ', [transOpacity]);
  
  if TransformName <> '' then
    Result := Result + 'name="' + TransformName + '"';

  if pluginColor <> 1 then
    Result := Result + Format('var_color="%g" ', [pluginColor]);

  Result := Result + '/>';
end;

function TXForm.FinalToXMLString(IsEnabled: boolean): string;
var
  i, j: integer;
  Name: string;
  Value: double;
  strvar: string;
begin
  //  result := Format('   <finalxform enabled="%d" color="%g" symmetry="%g" ',
//                   [ifthen(IsEnabled, 1, 0), color, symmetry]);
  result := Format('   <finalxform color="%g" ', [color]);
  if symmetry <> 0 then result := result + format('symmetry="%g" ', [symmetry]);

  // AV: write variation order
  strvar := '';
  for Name in ifs do begin
    if vars[GetVariationIndex(Name)] <> 0 then
      strvar := strvar + Name + #32;
  end;
  if (strvar <> '') and (pos(#32, strvar) < length(strvar)) then
	  Result := Result + format('var_order="%s" ', [strvar]);
  //////////////////////////////////////////
  (*
  for i := 0 to nrvar - 1 do begin
    if vars[i] <> 0 then
      Result := Result + varnames(i) + format('="%g" ', [vars[i]]);
  end;
  *)
  for Name in ifs do  // AV: write in correct order
  begin
	  Value := vars[GetVariationIndex(Name)];
	  if Value <> 0 then
      Result := Result + Name + format('="%g" ', [Value]);
  end;
  
  Result := Result + Format('coefs="%g %g %g %g %g %g" ', [c[0,0], c[0,1], c[1,0], c[1,1], c[2,0], c[2,1]]);
  if (p[0,0]<>1) or (p[0,1]<>0) or(p[1,0]<>0) or (p[1,1]<>1) or (p[2,0]<>0) or (p[2,1]<>0) then
    Result := Result + Format('post="%g %g %g %g %g %g" ', [p[0,0], p[0,1], p[1,0], p[1,1], p[2,0], p[2,1]]);
  if pluginColor <> 1 then
    Result := Result + Format('var_color="%g" ', [pluginColor]);

  for i := 0 to High(FRegVariations)  do begin
    if vars[i+NRLOCVAR] <> 0 then
      for j := 0 to FRegVariations[i].GetNrVariables - 1 do begin
        Name := FRegVariations[i].GetVariableNameAt(j);
        Result := Result + Format('%s="%s" ', [name, FRegVariations[i].GetVariableStr(Name)]);
      end;
  end;

  Result := Result + '/>';
end;

///////////////////////////////////////////////////////////////////////////////
procedure TXForm.GetVariable(const name: string; var Value: double);
var
  i: integer;
begin
  for i := 0 to High(FRegVariations) do
    if FRegVariations[i].GetVariable(name, value) then
      break;
end;

procedure TXForm.SetVariable(const name: string; var Value: double);
var
  i: integer;
begin
  for i := 0 to High(FRegVariations) do
    if FRegVariations[i].SetVariable(name, value) then
      break;
end;

procedure TXForm.ResetVariable(const name: string);
var
  i: integer;
begin
  for i := 0 to High(FRegVariations) do
    if FRegVariations[i].ResetVariable(name) then
      break;
end;

///////////////////////////////////////////////////////////////////////////////
function TXForm.GetVariableStr(const name: string): string;
var
  i: integer;
begin
  for i := 0 to High(FRegVariations) do begin
    Result := FRegVariations[i].GetVariableStr(name);
    if Result <> '' then break;
  end;
end;

procedure TXForm.SetVariableStr(const name: string; var Value: string);
var
  i: integer;
begin
  for i := 0 to High(FRegVariations) do begin
    if FRegVariations[i].SetVariableStr(name, value) then break;
  end;
end;

end.
