{ Apophysis AV "Phoenix Edition" Copyright (C) 2021 Alice V. Koryagina }

unit varJulia;

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

{$ifdef CPUX86}
  {$define _ASM_}
{$endif}
  
type
  TVariationJulia = class(TBaseVariation)
  private
    old: byte;
    vvar2: double;
	  procedure CalcPlugin;
  public
    constructor Create;

    class function GetName: string; override;
    class function GetInstance: TBaseVariation; override;

    function GetNrVariables: integer; override;
    function GetVariableNameAt(const IndJulia: integer): string; override;

    function SetVariable(const Name: string; var value: double): boolean; override;
    function GetVariable(const Name: string; var value: double): boolean; override;
    function ResetVariable(const Name: string): boolean; override;

    procedure Prepare; override;
	  procedure CalcFunction; override;
	  procedure GetCalcFunction(var f: TCalcFunction); override;
  end;


implementation

uses
  Math;
////////////////////////
procedure TVariationJulia.Prepare;
begin
  vvar2 := vvar * sqrt(2) * 0.5;
end;

procedure TVariationJulia.GetCalcFunction(var f: TCalcFunction);
begin
	if old = 1 then f := CalcFunction
	else f := CalcPlugin;
end;

procedure TVariationJulia.CalcFunction;
 {$ifndef _ASM_}
var
  r: double;
begin
(*
  // AV: oriiginal code was:

  FAngle := arctan2(FTx^, FTy^) / 2;
  SinCos(FAngle + pi * random(2), sina, cosa);
  r := vvar * sqrt(sqrt(sqrt(sqr(FTx^) + sqr(FTy^)));
  FPx^ := FPx^ + r * cosa;
  FPy^ := FPy^ + r * sina;

  // AV: I simplified calculations
*)

  r := sqrt(sqrt(sqr(FTx^) + sqr(FTy^)) + FTy^ );

  if random(2) = 0 then begin
    FPx^ := FPx^ + vvar2 * r;
    FPy^ := FPy^ + vvar2 / r * FTx^;
  end
  else begin
    FPx^ := FPx^ - vvar2 * r;
    FPy^ := FPy^ - vvar2 / r * FTx^;
  end;
{$else}
  asm
    mov     edx, [eax + FTx]
    fld     qword ptr [edx] 	// FTx
    fld     qword ptr [edx + 8] // FTy
    fld     st(1)  // FTx
    fmul    st,st  // sqr(FTx) 
    fld     st(1)  // FTy
    fmul    st,st  // sqr(FTy)
    faddp
    fsqrt
    faddp
    fsqrt

    fld     qword ptr [eax + vvar2]
    mov     ecx,eax
    mov     eax,2
    call    AsmRandInt
    shr eax,1
    jc      @skip
    fchs
@skip:

    fmul    st(2),st
    fmul    st,st(1)

    mov     edx, [ecx + FPx]
    fadd    qword ptr [edx]
    fstp    qword ptr [edx]
    fdivp   st(1),st
    fadd    qword ptr [edx + 8]
    fstp    qword ptr [edx + 8]
    fwait
{$endif} 
 end;

procedure TVariationJulia.CalcPlugin;
{$ifndef _ASM_}
var
  r: double;
begin
  r := sqrt(sqrt(sqr(FTx^) + sqr(FTy^)) + FTx^ );

  if random(2) = 0 then begin
    FPx^ := FPx^ + vvar2 * r;
    FPy^ := FPy^ + vvar2 / r * FTy^;
  end
  else begin
    FPx^ := FPx^ - vvar2 * r;
    FPy^ := FPy^ - vvar2 / r * FTy^;
  end;
{$else}
  asm
    mov     edx, [eax + FTx]
    fld     qword ptr [edx + 8] // FTy
    fld     qword ptr [edx]     // FTx
    fld     st(1)
    fmul    st,st
    fld     st(1)
    fmul    st,st
    faddp
    fsqrt
    faddp
    fsqrt

    fld     qword ptr [eax + vvar2]
    mov     ecx,eax
    mov     eax,2
    call    AsmRandInt
    shr eax,1
    jc      @skip
    fchs
@skip:

    fmul    st(2),st
    fmul    st,st(1)

    mov     edx, [ecx + FPx]
    fadd    qword ptr [edx]
    fstp    qword ptr [edx]
    fdivp   st(1),st
    fadd    qword ptr [edx + 8]
    fstp    qword ptr [edx + 8]
    fwait
{$endif}
end;

constructor TVariationJulia.Create;
begin
  old := 1;
end;

class function TVariationJulia.GetInstance: TBaseVariation;
begin
   Result := TVariationJulia.Create;
end;

class function TVariationJulia.GetName: string;
begin
   Result := 'julia';
end;

function TVariationJulia.GetNrVariables: integer;
begin
   Result := 1;
end;

function TVariationJulia.GetVariable(const Name: string;
  var value: double): boolean;
begin
  Result := False;
  if Name = 'julia_old' then begin
    value := old;
    Result := True;
  end;
end;

function TVariationJulia.GetVariableNameAt(const IndJulia: integer): string;
begin
  case IndJulia of
    0: Result := 'julia_old';
  else
    Result := '';
  end;
end;

function TVariationJulia.SetVariable(const Name: string;
  var value: double): boolean;
begin
  Result := False;
  if Name = 'julia_old' then begin
    if Value < 0 then Value := 0;
	if Value > 1 then Value := 1;
    old := Round(value);
    Result := True;
  end;
end;

function TVariationJulia.ResetVariable(const Name: string): boolean;
begin
  Result := False;
  if Name = 'julia_old' then begin
    old := 1;
    Result := True;
  end;
end;

//////////////////////////////
initialization
  RegisterVariation(TVariationClassLoader.Create(TVariationJulia), false, false);

end.
