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

unit varCpow3;

interface

uses
  BaseVariation, XFormMan;

const
  sr = 'cpow3_r';
  sd = 'cpow3_d';
  sdivisor = 'cpow3_divisor';
  sspread = 'cpow3_spread';
    
type
  TVariationCpow3 = class(TBaseVariation)
  private
      r, d1, spread: double;
	  divisor: integer;
	
	  ang, a2, d2, half_d, c2, half_c, coeff: double;
  public
    constructor Create;

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

    function GetNrVariables: integer; override;
    function GetVariableNameAt(const Index: 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;
  end;

implementation

uses
  Math;

{ TVariationCpow3 }

//////////////////////////////////////////
procedure TVariationCpow3.Prepare;
var ca, sa: double;
begin
	ang :=  PI2 / divisor;
	if (d1 < 0) then 
		a2 := arctan2(-ln(-d1) * r, PI2)
	else
		a2 := arctan2(ln(d1) * r, PI2);
	SinCos(a2, sa, ca);
	c2 := ca * r * ca / divisor;
	d2 := ca * r * sa / divisor;
	half_c := c2 * 0.5;
	half_d := d2 * 0.5;
	if (d2 = 0) then
		coeff := 0
	else	
		coeff := -0.095 * spread / d2;
end;

//////////////////////////////////////////
procedure TVariationCpow3.CalcFunction;
var	t, sn, cn, r2, lnr2: double;
begin
  t := arctan2(FTy^, FTx^);
	if (t < 0) then t := t + PI2;
	if (cos(t * 0.5) < (2 * random - 1)) then 
		t := t - PI2;
	if (random < 0.5) then
		t := t + PI2 * round(ln(random) * coeff)
	else
		t := t - PI2 * round(ln(random) * coeff);
	lnr2 := ln(FTx^ * FTx^ + FTy^ * FTy^);
	r2 := vvar * exp(half_c * lnr2 - d2 * t);
	SinCos(c2 * t + half_d * lnr2 + ang * random(32767), sn, cn);

	FPx^ := FPx^ + r2 * cn;
  FPy^ := FPy^ + r2 * sn;
end;

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

constructor TVariationCpow3.Create;
begin
  r := 1;
  d1 := 1;
  divisor := 1;
  spread := 1;
end;

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

///////////////////////////////////////////////////////////////////////////////
class function TVariationCpow3.GetName: string;
begin
  Result := 'cpow3';
end;

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
function TVariationCpow3.GetVariableNameAt(const Index: integer): string;
begin
  case Index of
    0: Result := sr;
    1: Result := sd;
    2: Result := sdivisor;
	  3: Result := sspread;
  else
    Result := '';
  end
end;

///////////////////////////////////////////////////////////////////////////////
function TVariationCpow3.SetVariable(const Name: string; var value: double): boolean;
begin
  Result := False;
  if Name = sr then begin
    r := Value;
    Result := True;
  end else if Name = sd then begin
	if (Value = 0) then Value := 1;
    d1 := value;
    Result := True;
  end else if Name = sdivisor then begin    
    if (Value = 0) then Value := 1;	
    divisor := Round(Value);
    Result := True;
  end else if Name = sspread then begin
	spread := Value;
    Result := True;
  end;
end;

function TVariationCpow3.ResetVariable(const Name: string): boolean;
begin
  Result := False;
  if Name = sr then begin
    r := 1;
    Result := True;
  end else if Name = sd then begin
    d1 := 1;
    Result := True;
  end else if Name = sspread then begin
    spread := 1;
    Result := True;
  end else if Name = sdivisor then begin
    divisor := 1;
    Result := True;
  end;
end;

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

function TVariationCpow3.GetNrVariables: integer;
begin
  Result := 4;
end;

///////////////////////////////////////////////////////////////////////////////
function TVariationCpow3.GetVariable(const Name: string; var value: double): boolean;
begin
  Result := False;
  if Name = sr then begin
    Value := r;
    Result := true;
  end
  else if Name = sd then begin
    Value := d1;
    Result := true;
  end else if Name = sspread then begin
    Value := spread;
    Result := true;
  end else if Name = sdivisor then begin
    Value := divisor;
    Result := true;
  end;
end;

///////////////////////////////////////////////////////////////////////////////
initialization
  RegisterVariation(TVariationClassLoader.Create(TVariationCpow3), false, false);
end.
