/*****************************************************************************
* fractal.c
*
* This module implements the fractal sets primitive.
*
* This file was written by Pascal Massimino.
*
* from Persistence of Vision(tm) Ray Tracer
* Copyright 1996 Persistence of Vision Team
*---------------------------------------------------------------------------
* NOTICE: This source code file is provided so that users may experiment
* with enhancements to POV-Ray and to port the software to platforms other
* than those supported by the POV-Ray Team. There are strict rules under
* which you are permitted to use this file. The rules are in the file
* named POVLEGAL.DOC which should be distributed with this file. If
* POVLEGAL.DOC is not available or for more info please contact the POV-Ray
* Team Coordinator by leaving a message in CompuServe's Graphics Developer's
* Forum. The latest version of POV-Ray may be found there as well.
*
* This program is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
*****************************************************************************/
#include "frame.h"
#include "povray.h"
#include "vector.h"
#include "povproto.h"
#include "bbox.h"
#include "matrices.h"
#include "objects.h"
#include "spheres.h"
#include "fractal.h"
#include "quatern.h"
#include "hcmplx.h"
/*****************************************************************************
* Local preprocessor defines
******************************************************************************/
#ifndef Fractal_Tolerance
#define Fractal_Tolerance 1e-7
#endif
/*****************************************************************************
* Local typedefs
******************************************************************************/
/*****************************************************************************
* Static functions
******************************************************************************/
static int All_Fractal_Intersections PARAMS((OBJECT * Object, RAY * Ray, ISTACK * Depth_Stack));
static int Inside_Fractal PARAMS((VECTOR IPoint, OBJECT * Object));
static void Fractal_Normal PARAMS((VECTOR Result, OBJECT * Object, INTERSECTION * Intersect));
static void *Copy_Fractal PARAMS((OBJECT * Object));
static void Translate_Fractal PARAMS((OBJECT * Object, VECTOR Vector, TRANSFORM *Trans));
static void Rotate_Fractal PARAMS((OBJECT * Object, VECTOR Vector, TRANSFORM *Trans));
static void Scale_Fractal PARAMS((OBJECT * Object, VECTOR Vector, TRANSFORM *Trans));
static void Transform_Fractal PARAMS((OBJECT * Object, TRANSFORM * Trans));
static void Invert_Fractal PARAMS((OBJECT * Object));
static void Destroy_Fractal PARAMS((OBJECT * Object));
static void Compute_Fractal_BBox PARAMS((FRACTAL * Fractal));
/*****************************************************************************
* Local variables
******************************************************************************/
static METHODS Fractal_Methods =
{
All_Fractal_Intersections,
Inside_Fractal, Fractal_Normal,
Copy_Fractal,
Translate_Fractal, Rotate_Fractal,
Scale_Fractal, Transform_Fractal, Invert_Fractal,
Destroy_Fractal
};
static int Allocated_Iteration_Stack_Length = 0;
DBL *Sx = NULL, *Sy = NULL, *Sz = NULL, *Sw = NULL;
DBL Precision;
VECTOR Direction;
static COMPLEX_FUNCTION_METHOD Complex_Function_List[] =
{
/* must match STYPE list in fractal.h */
Complex_Exp,
Complex_Log,
Complex_Sin,
Complex_ASin,
Complex_Cos,
Complex_ACos,
Complex_Tan,
Complex_ATan,
Complex_Sinh,
Complex_ASinh,
Complex_Cosh,
Complex_ACosh,
Complex_Tanh,
Complex_ATanh,
Complex_Pwr
};
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static int All_Fractal_Intersections(Object, Ray, Depth_Stack)
OBJECT *Object;
RAY *Ray;
ISTACK *Depth_Stack;
{
int Intersection_Found;
int Last = 0;
int CURRENT, NEXT;
DBL Depth, Depth_Max;
DBL Dist, Dist_Next, Len;
VECTOR IPoint, Mid_Point, Next_Point, Real_Pt;
VECTOR Real_Normal, F_Normal;
RAY New_Ray;
FRACTAL *Fractal = (FRACTAL *) Object;
Increase_Counter(stats[Ray_Fractal_Tests]);
Intersection_Found = FALSE;
Precision = Fractal->Precision;
/* Get into Fractal's world. */
if (Fractal->Trans != NULL)
{
MInvTransDirection(Direction, Ray->Direction, Fractal->Trans);
VDot(Len, Direction, Direction);
if (Len == 0.0)
{
return (FALSE);
}
if (Len != 1.0)
{
Len = 1.0 / sqrt(Len);
VScaleEq(Direction, Len);
}
Assign_Vector(New_Ray.Direction, Direction);
MInvTransPoint(New_Ray.Initial, Ray->Initial, Fractal->Trans);
}
else
{
Assign_Vector(Direction, Ray->Direction);
New_Ray = *Ray;
Len = 1.0;
}
/* Bound fractal. */
if (!F_Bound(&New_Ray, Fractal, &Depth, &Depth_Max))
{
return (FALSE);
}
if (Depth_Max < Fractal_Tolerance)
{
return (FALSE);
}
if (Depth < Fractal_Tolerance)
{
Depth = Fractal_Tolerance;
}
/* Jump to starting point */
VScale(Next_Point, Direction, Depth);
VAddEq(Next_Point, New_Ray.Initial);
CURRENT = D_Iteration(Next_Point, Fractal, &Dist);
/* Light ray starting inside ? */
if (CURRENT)
{
VAddScaledEq(Next_Point, 2.0 * Fractal_Tolerance, Direction);
Depth += 2.0 * Fractal_Tolerance;
if (Depth > Depth_Max)
{
return (FALSE);
}
CURRENT = D_Iteration(Next_Point, Fractal, &Dist);
}
/* Ok. Trace it */
while (Depth < Depth_Max)
{
/*
* Get close to the root: Advance with Next_Point, keeping track of last
* position in IPoint...
*/
while (1)
{
if (Dist < Precision)
{
Dist = Precision;
}
Depth += Dist;
if (Depth > Depth_Max)
{
if (Intersection_Found)
{
Increase_Counter(stats[Ray_Fractal_Tests_Succeeded]);
}
return (Intersection_Found);
}
Assign_Vector(IPoint, Next_Point);
VAddScaledEq(Next_Point, Dist, Direction);
NEXT = D_Iteration(Next_Point, Fractal, &Dist_Next);
if (NEXT != CURRENT)
{
/* Set surface was crossed... */
Depth -= Dist;
break;
}
else
{
Dist = Dist_Next; /* not reached */
}
}
/* then, polish the root via bisection method... */
while (Dist > Fractal_Tolerance)
{
Dist *= 0.5;
VAddScaled(Mid_Point, IPoint, Dist, Direction);
Last = Iteration(Mid_Point, Fractal);
if (Last == CURRENT)
{
Assign_Vector(IPoint, Mid_Point);
Depth += Dist;
if (Depth > Depth_Max)
{
if (Intersection_Found)
{
Increase_Counter(stats[Ray_Fractal_Tests_Succeeded]);
}
return (Intersection_Found);
}
}
}
if (CURRENT == FALSE) /* Mid_Point isn't inside the set */
{
VAddScaledEq(IPoint, Dist, Direction);
Depth += Dist;
Iteration(IPoint, Fractal);
}
else
{
if (Last != CURRENT)
{
Iteration(IPoint, Fractal);
}
}
if (Fractal->Trans != NULL)
{
MTransPoint(Real_Pt, IPoint, Fractal->Trans);
Normal_Calc(Fractal, F_Normal);
MTransNormal(Real_Normal, F_Normal, Fractal->Trans);
}
else
{
Assign_Vector(Real_Pt, IPoint);
Normal_Calc(Fractal, Real_Normal);
}
if (Point_In_Clip(Real_Pt, Object->Clip))
{
VNormalize(Real_Normal, Real_Normal);
push_normal_entry(Depth * Len, Real_Pt, Real_Normal, Object, Depth_Stack);
Intersection_Found = TRUE;
/* If fractal isn't used with CSG we can exit now. */
if (!(Fractal->Type & IS_CHILD_OBJECT))
{
break;
}
}
/* Start over where work was left */
Assign_Vector(IPoint, Next_Point);
Dist = Dist_Next;
CURRENT = NEXT;
}
if (Intersection_Found)
{
Increase_Counter(stats[Ray_Fractal_Tests_Succeeded]);
}
return (Intersection_Found);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static int Inside_Fractal(IPoint, Object)
VECTOR IPoint;
OBJECT *Object;
{
FRACTAL *Fractal = (FRACTAL *) Object;
int Result;
static VECTOR New_Point;
if (Fractal->Trans != NULL)
{
MInvTransPoint(New_Point, IPoint, Fractal->Trans);
Result = Iteration(New_Point, Fractal);
}
else
{
Result = Iteration(IPoint, Fractal);
}
if (Fractal->Inverted)
{
return (!Result);
}
else
{
return (Result);
}
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void Fractal_Normal(Result, Object, Intersect)
OBJECT *Object;
VECTOR Result;
INTERSECTION *Intersect;
{
Assign_Vector(Result, Intersect->INormal);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void Translate_Fractal(Object, Vector, Trans)
OBJECT *Object;
VECTOR Vector;
TRANSFORM *Trans;
{
Transform_Fractal(Object, Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void Rotate_Fractal(Object, Vector, Trans)
OBJECT *Object;
VECTOR Vector;
TRANSFORM *Trans;
{
Transform_Fractal(Object, Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void Scale_Fractal(Object, Vector, Trans)
OBJECT *Object;
VECTOR Vector;
TRANSFORM *Trans;
{
Transform_Fractal(Object, Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
* Mar 1996 : Moved call to Recompute_BBox to Compute_Fractal_BBox() (TW)
*
******************************************************************************/
static void Transform_Fractal(Object, Trans)
OBJECT *Object;
TRANSFORM *Trans;
{
FRACTAL *Fractal = (FRACTAL *) Object;
if (((FRACTAL *) Object)->Trans == NULL)
{
((FRACTAL *) Object)->Trans = Create_Transform();
}
Compose_Transforms(Fractal->Trans, Trans);
Compute_Fractal_BBox((FRACTAL *) Object);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void Invert_Fractal(Object)
OBJECT *Object;
{
((FRACTAL *) Object)->Inverted ^= TRUE;
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
* Mar 1996 : Added call to recompute_BBox() to bottom (TW)
*
******************************************************************************/
static void Compute_Fractal_BBox(Fractal)
FRACTAL *Fractal;
{
DBL R;
switch (Fractal->Algebra)
{
case QUATERNION_TYPE:
R = 1.0 + sqrt(Sqr(Fractal->Julia_Parm[X]) + Sqr(Fractal->Julia_Parm[Y]) + Sqr(Fractal->Julia_Parm[Z]) + Sqr(Fractal->Julia_Parm[T]));
R += Fractal_Tolerance; /* fix bug when Julia_Parameter exactly 0 */
if (R > 2.0)
{
R = 2.0;
}
Fractal->Exit_Value = Sqr(R);
break;
case HYPERCOMPLEX_TYPE:
default:
R = 4.0;
Fractal->Exit_Value = 16.0;
break;
}
Fractal->Radius_Squared = Sqr(R);
Fractal->Inverted = FALSE;
Make_BBox(Fractal->BBox, -R, -R, -R, 2.0 * R, 2.0 * R, 2.0 * R);
Recompute_BBox(&Fractal->BBox, Fractal->Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
FRACTAL *Create_Fractal()
{
FRACTAL *New;
New = (FRACTAL *) POV_MALLOC(sizeof(FRACTAL), "Fractal Set");
INIT_OBJECT_FIELDS(New, BASIC_OBJECT, &Fractal_Methods)
New->Trans = NULL;
Make_Vector(New->Center, 0.0, 0.0, 0.0);
New->Julia_Parm[X] = 1.0;
New->Julia_Parm[Y] = 0.0;
New->Julia_Parm[Z] = 0.0;
New->Julia_Parm[T] = 0.0;
New->Slice[X] = 0.0;
New->Slice[Y] = 0.0;
New->Slice[Z] = 0.0;
New->Slice[T] = 1.0;
New->SliceDist = 0.0;
New->Exit_Value = 4.0;
New->n = 20;
New->Precision = 1.0 / 20.0;
New->Inverted = FALSE;
New->Algebra = QUATERNION_TYPE;
New->Sub_Type = SQR_STYPE;
New->Bound = NULL;
New->Clip = NULL;
New->Normal_Calc_Method = NULL;
New->Iteration_Method = NULL;
New->D_Iteration_Method = NULL;
New->F_Bound_Method = NULL;
New->Complex_Function_Method = NULL;
New->Radius_Squared = 0.0;
New->exponent.x = 0.0;
New->exponent.y = 0.0;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void *Copy_Fractal(Object)
OBJECT *Object;
{
FRACTAL *New;
New = Create_Fractal();
*New = *((FRACTAL *) Object);
New->Trans = Copy_Transform(((FRACTAL *) Object)->Trans);
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
static void Destroy_Fractal(Object)
OBJECT *Object;
{
Destroy_Transform(((FRACTAL *) Object)->Trans);
POV_FREE(Object);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void SetUp_Fractal(Fractal)
FRACTAL *Fractal;
{
switch (Fractal->Algebra)
{
case QUATERNION_TYPE:
switch(Fractal->Sub_Type)
{
case CUBE_STYPE:
Fractal->Iteration_Method = Iteration_z3;
Fractal->Normal_Calc_Method = Normal_Calc_z3;
Fractal->D_Iteration_Method = D_Iteration_z3;
break;
case SQR_STYPE:
Fractal->Iteration_Method = Iteration_Julia;
Fractal->D_Iteration_Method = D_Iteration_Julia;
Fractal->Normal_Calc_Method = Normal_Calc_Julia;
break;
default:
Error("illegal function: quaternion only supports sqr and cube");
}
Fractal->F_Bound_Method = F_Bound_Julia;
break;
case HYPERCOMPLEX_TYPE:
switch (Fractal->Sub_Type)
{
case RECIPROCAL_STYPE:
Fractal->Iteration_Method = Iteration_HCompl_Reciprocal;
Fractal->Normal_Calc_Method = Normal_Calc_HCompl_Reciprocal;
Fractal->D_Iteration_Method = D_Iteration_HCompl_Reciprocal;
Fractal->F_Bound_Method = F_Bound_HCompl_Reciprocal;
break;
case EXP_STYPE:
case LOG_STYPE:
case SIN_STYPE:
case ASIN_STYPE:
case COS_STYPE:
case ACOS_STYPE:
case TAN_STYPE:
case ATAN_STYPE:
case SINH_STYPE:
case ASINH_STYPE:
case COSH_STYPE:
case ACOSH_STYPE:
case TANH_STYPE:
case ATANH_STYPE:
case PWR_STYPE:
Fractal->Iteration_Method = Iteration_HCompl_Func;
Fractal->Normal_Calc_Method = Normal_Calc_HCompl_Func;
Fractal->D_Iteration_Method = D_Iteration_HCompl_Func;
Fractal->F_Bound_Method = F_Bound_HCompl_Func;
Fractal->Complex_Function_Method = Complex_Function_List[Fractal->Sub_Type];
break;
case CUBE_STYPE:
Fractal->Iteration_Method = Iteration_HCompl_z3;
Fractal->Normal_Calc_Method = Normal_Calc_HCompl_z3;
Fractal->D_Iteration_Method = D_Iteration_HCompl_z3;
Fractal->F_Bound_Method = F_Bound_HCompl_z3;
break;
default: /* SQR_STYPE or else... */
Fractal->Iteration_Method = Iteration_HCompl;
Fractal->Normal_Calc_Method = Normal_Calc_HCompl;
Fractal->D_Iteration_Method = D_Iteration_HCompl;
Fractal->F_Bound_Method = F_Bound_HCompl;
break;
}
break;
default:
Error("Algebra unknown in fractal.");
}
Allocate_Iteration_Stack(Fractal->n);
Compute_Fractal_BBox(Fractal);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void Allocate_Iteration_Stack(n)
int n;
{
if (n > Allocated_Iteration_Stack_Length)
{
Sx = (DBL *) POV_REALLOC(Sx, (n + 1) * sizeof(DBL), "x iteration stack");
Sy = (DBL *) POV_REALLOC(Sy, (n + 1) * sizeof(DBL), "y iteration stack");
Sz = (DBL *) POV_REALLOC(Sz, (n + 1) * sizeof(DBL), "w iteration stack");
Sw = (DBL *) POV_REALLOC(Sw, (n + 1) * sizeof(DBL), "z iteration stack");
Allocated_Iteration_Stack_Length = n;
}
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Pascal Massimino
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void Free_Iteration_Stack()
{
if (Sx != NULL)
{
POV_FREE(Sx);
POV_FREE(Sy);
POV_FREE(Sz);
POV_FREE(Sw);
}
Sx = NULL;
Sy = NULL;
Sz = NULL;
Sw = NULL;
Allocated_Iteration_Stack_Length = 0;
}