/**
 * Mandelbulber v2, a 3D fractal generator  _%}}i*<.         ______
 * Copyright (C) 2020 Mandelbulber Team   _>]|=||i=i<,      / ____/ __    __
 *                                        \><||i|=>>%)     / /   __/ /___/ /_
 * This file is part of Mandelbulber.     )<=i=]=|=i<>    / /__ /_  __/_  __/
 * The project is licensed under GPLv3,   -<>>=|><|||`    \____/ /_/   /_/
 * see also COPYING file in this folder.    ~+{i%+++
 *
 * MsltoeToroidalMulti
 * @reference http://www.fractalforums.com/theory/toroidal-coordinates/msg9428/
 */

#include "all_fractal_definitions.h"

cFractalMsltoeToroidalMulti::cFractalMsltoeToroidalMulti() : cAbstractFractal()
{
	nameInComboBox = "Msltoe - Toroidal Multi";
	internalName = "msltoe_toroidal_multi";
	internalID = fractal::msltoeToroidalMulti;
	DEType = analyticDEType;
	DEFunctionType = logarithmicDEFunction;
	cpixelAddition = cpixelEnabledByDefault;
	defaultBailout = 10.0;
	DEAnalyticFunction = analyticFunctionLogarithmic;
	coloringFunction = coloringFunctionDefault;
}

void cFractalMsltoeToroidalMulti::FormulaCode(
	CVector4 &z, const sFractal *fractal, sExtendedAux &aux)
{
	if (fractal->transformCommon.functionEnabledFalse
			&& aux.i >= fractal->transformCommon.startIterationsD
			&& aux.i < fractal->transformCommon.stopIterationsD1) // pre-scale
	{
		z *= fractal->transformCommon.scale3D111;
		aux.DE = aux.DE * z.Length() / aux.r;
	}
	// Toroidal bulb multi
	double th0 = fractal->bulb.betaAngleOffset;
	double ph0 = fractal->bulb.alphaAngleOffset;
	double v1, v2, v3;
	switch (fractal->sinTan2Trig.orderOfZYX)
	{
		case multi_OrderOfZYX_zyx:
		default:
			v1 = z.z;
			v2 = z.y;
			v3 = z.x;
			break;
		case multi_OrderOfZYX_zxy:
			v1 = z.z;
			v2 = z.x;
			v3 = z.y;
			break;
		case multi_OrderOfZYX_yzx:
			v1 = z.y;
			v2 = z.z;
			v3 = z.x;
			break;
		case multi_OrderOfZYX_yxz:
			v1 = z.y;
			v2 = z.x;
			v3 = z.z;
			break;
		case multi_OrderOfZYX_xzy:
			v1 = z.x;
			v2 = z.z;
			v3 = z.y;
			break;
		case multi_OrderOfZYX_xyz:
			v1 = z.x;
			v2 = z.y;
			v3 = z.z;
			break;
	}

	switch (fractal->sinTan2Trig.atan2OrAtan)
	{
		case multi_atan2OrAtan_atan2: ph0 += atan2(v2, v3); break;
		case multi_atan2OrAtan_atan: ph0 += atan(v2 / v3); break;
	}

	double r1 = fractal->transformCommon.minR05;
	double x1 = r1 * cos(ph0);
	double y1 = r1 * sin(ph0);

	aux.r = (z.x - x1) * (z.x - x1) + (z.y - y1) * (z.y - y1) + z.z * z.z; //+ 1e-061

	double sqrT = aux.r;
	if (fractal->transformCommon.functionEnabledAy) // sqrt
	{
		sqrT = sqrt(aux.r);
	}

	switch (fractal->sinTan2Trig.asinOrAcos)
	{
		case multi_asinOrAcos_asin: th0 += asin(v1 / sqrT); break;
		case multi_asinOrAcos_acos: th0 += acos(v1 / sqrT); break;
	}

	th0 *= fractal->transformCommon.pwr8; // default 8
	ph0 *= fractal->bulb.power;						// default 9 gives 8 symmetry

	double rp = pow(aux.r, fractal->transformCommon.pwr4);

	double costh = cos(th0);
	double sinth = sin(th0);
	double r1RpCosTh = r1 + rp * costh;
	double r1RpSinTh = r1 + rp * sinth;

	if (fractal->transformCommon.functionEnabledzFalse)
	{ // cosine mode
		z.x = r1RpSinTh * sin(ph0);
		z.y = r1RpSinTh * cos(ph0);
		z.z = -rp * costh;
	}
	else
	{ // sine mode default

		z.x = r1RpCosTh * cos(ph0);
		z.y = r1RpCosTh * sin(ph0);
		z.z = -rp * sinth;
	}

	// DEcalc
	if (!fractal->analyticDE.enabledFalse)
	{
		aux.DE = rp * aux.DE * (fractal->transformCommon.pwr4 + 1.0) + 1.0;
	}
	else
	{
		aux.DE = rp * aux.DE * (fractal->transformCommon.pwr4 + fractal->analyticDE.offset2)
							 * fractal->analyticDE.scale1
						 + fractal->analyticDE.offset1;
	}

	if (fractal->transformCommon.functionEnabledAxFalse) // spherical offset
	{
		double lengthTempZ = -z.Length();
		// if (lengthTempZ > -1e-21) lengthTempZ = -1e-21;   //  z is neg.)
		z *= 1.0 + fractal->transformCommon.offset / lengthTempZ;
		z *= fractal->transformCommon.scale;
		aux.DE = aux.DE * fabs(fractal->transformCommon.scale) + 1.0;
	}
	// then add Cpixel constant vector
}
