/*******************************************************************************
 * pattern.cpp
 *
 * This module implements texturing functions that return a value to be
 * used in a pigment or normal.
 *
 * ---------------------------------------------------------------------------
 * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
 * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
 *
 * POV-Ray is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * POV-Ray 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * ---------------------------------------------------------------------------
 * POV-Ray 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.
 * ---------------------------------------------------------------------------
 * $File: //depot/public/povray/3.x/source/backend/pattern/pattern.cpp $
 * $Revision: #1 $
 * $Change: 6069 $
 * $DateTime: 2013/11/06 11:59:40 $
 * $Author: chrisc $
 *******************************************************************************/

/*
 * Some texture ideas garnered from SIGGRAPH '85 Volume 19 Number 3,
 * "An Image Synthesizer" By Ken Perlin.
 * Further Ideas Garnered from "The RenderMan Companion" (Addison Wesley).
 */

// frame.h must always be the first POV file included (pulls in platform config)
#include "backend/frame.h"
#include "backend/pattern/pattern.h"
#include "backend/texture/texture.h"
#include "backend/texture/pigment.h"
#include "backend/scene/objects.h"
#include "backend/scene/scene.h"
#include "backend/support/imageutil.h"
#include "backend/colour/colour.h"
#include "backend/parser/parse.h"
#include "backend/math/vector.h"
#include "backend/math/matrices.h"
#include "backend/vm/fnpovfpu.h"
#include "backend/support/fileutil.h"
#include "base/fileinputoutput.h"

#include <algorithm>
#include <climits>

// this must be the last file included
#include "base/povdebug.h"

namespace pov
{

using namespace pov_base;

/*****************************************************************************
* Local preprocessor defines
******************************************************************************/

#define CLIP_DENSITY(r) { if((r) < 0.0) { (r) = 1.0; } else if((r) > 1.0) { (r) = 0.0; } else { (r) = 1.0 - (r); } }

const int FRACTAL_MAX_EXPONENT = 33;

/*****************************************************************************
* Local variables
******************************************************************************/

static RandomDoubleSequence PatternRands(0.0, 1.0, 32768);
static int BinomialCoefficients[((FRACTAL_MAX_EXPONENT+1)*(FRACTAL_MAX_EXPONENT+2))/2]; // GLOBAL VARIABLE
boost::hash<Crackle_Cell_Coord> Crackle_Cell_Hasher;
static int CrackleCubeTable[81*3];

/*****************************************************************************
* Static functions
******************************************************************************/

static DBL agate_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator);
static DBL boxed_pattern (const VECTOR EPoint);
static DBL brick_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL cells_pattern (const VECTOR EPoint);
static DBL checker_pattern (const VECTOR EPoint);
static DBL crackle_pattern (const VECTOR EPoint, const TPATTERN *TPat, TraceThreadData *Thread);
static DBL cylindrical_pattern (const VECTOR EPoint);
static DBL dents_pattern (const VECTOR EPoint, int noise_generator);
static DBL density_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL function_pattern (const VECTOR EPoint, const TPATTERN *TPat, TraceThreadData *Thread);
static DBL gradient_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL granite_pattern (const VECTOR EPoint, int noise_generator);
static DBL hexagon_pattern (const VECTOR EPoint);
static DBL square_pattern (const VECTOR EPoint);
static DBL triangular_pattern (const VECTOR EPoint);
static DBL cubic_pattern (const VECTOR EPoint);
static DBL julia_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL julia3_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL julia4_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL juliax_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL leopard_pattern (const VECTOR EPoint);
static DBL magnet1m_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL magnet1j_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL magnet2m_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL magnet2j_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL mandel_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL mandel3_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL mandel4_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL mandelx_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL marble_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator);
static DBL object_pattern (const VECTOR EPoint, const TPATTERN *TPat, TraceThreadData *Thread);
static DBL onion_pattern (const VECTOR EPoint);
static DBL pavement_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL pigment_pattern (const VECTOR EPoint, const TPATTERN *TPat, const Intersection *isect, const Ray *ray, TraceThreadData *Thread);
static DBL planar_pattern (const VECTOR EPoint);
static DBL quilted_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL radial_pattern (const VECTOR EPoint);
static DBL ripples_pattern (const VECTOR EPoint, const TPATTERN *TPat, const TraceThreadData *Thread);
static DBL slope_pattern (const VECTOR EPoint, const TPATTERN *TPat, const Intersection *Intersection);
static DBL aoi_pattern (const Intersection *Intersection, const Ray *ray);
static DBL spiral1_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator);
static DBL spiral2_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator);
static DBL spherical_pattern (const VECTOR EPoint);
static DBL tiling_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL waves_pattern (const VECTOR EPoint, const TPATTERN *TPat, const TraceThreadData *Thread);
static DBL wood_pattern (const VECTOR EPoint, const TPATTERN *TPat);
static DBL wrinkles_pattern (const VECTOR EPoint, int noise_generator);

static DBL fractal_exterior_color(const TPATTERN *TPat, int iters, DBL a, DBL b);
static DBL fractal_interior_color(const TPATTERN *TPat, int iters, DBL a, DBL b, DBL mindist2);
static const TURB *Search_For_Turb(const WARP *Warps);
static unsigned short readushort(IStream *infile);
static unsigned int readuint(IStream *infile);

#define SQRT3_2     0.86602540378443864676372317075294  ///< sqrt(3)/2
#define SQRT3       1.7320508075688772935274463415059   ///< sqrt(3)
#define SQRT2       1.4142135623730950488016887242097   ///< sqrt(2)
#define SQRT2_2     0.70710678118654752440084436210485  ///< sqrt(2)/2

#define SIN18       0.30901699437494742410229341718282  ///< sin(18 deg)
#define SIN36       0.58778525229247312916870595463907  ///< sin(36 deg)
#define SIN54       0.80901699437494742410229341718282  ///< sin(54 deg)
#define SIN72       0.95105651629515357211643933337938  ///< sin(72 deg)
#define SIN108      SIN72                               ///< sin(108 deg)
#define SIN144      SIN36                               ///< sin(144 deg)
#define SIN162      SIN18                               ///< sin(162 deg)
#define COS18       SIN72                               ///< cos(18 deg)
#define COS36       SIN54                               ///< cos(36 deg)
#define COS54       SIN36                               ///< cos(54 deg)
#define COS72       SIN18                               ///< cos(72 deg)
#define COS108      (-COS72)                            ///< cos(108 deg)
#define COS126      (-COS54)                            ///< cos(126 deg)
#define COS144      (-COS36)                            ///< cos(144 deg)
#define COS162      (-COS18)                            ///< cos(162 deg)
#define TAN18       0.32491969623290632615587141221513  ///< tan(18 deg)
#define TAN36       0.72654252800536088589546675748062  ///< tan(36 deg)
#define TAN54       1.3763819204711735382072095819109   ///< tan(54 deg)
#define TAN72       3.0776835371752534025702905760369   ///< tan(72 deg)
#define TAN108      (-TAN72)                            ///< tan(108 deg)
#define TAN126      (-TAN54)                            ///< tan(126 deg)
#define TAN144      (-TAN36)                            ///< tan(144 deg)
#define TAN162      (-TAN18)                            ///< tan(162 deg)

#define PHI         1.6180339887498948482045868343656   ///< golden ratio = (1+sqrt(5))/2
#define INVPHI      0.61803398874989484820458683436564  ///< inverse of golden ratio (= golden ratio -1)
#define SQRPHI      2.6180339887498948482045868343656   ///< square of golden ratio (= golden ratio +1)
#define INVSQRPHI   0.38196601125010515179541316563436  ///< inverse square of golden ratio (= 2 - golden ratio)

#define TILING_EPSILON 1e-6

/*****************************************************************************/

int GetNoiseGen (const TPATTERN *TPat, const TraceThreadData *Thread)
{
	int noise_gen = (TPat->Flags & NOISE_FLAGS) / NOISE_FLAG_1;
	if (!noise_gen)
		noise_gen = Thread->GetSceneData()->noiseGenerator;
	return noise_gen;
}

/*****************************************************************************
*
* FUNCTION
*
*   Evaluate_Pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*   Intersection - intersection structure
*   
* OUTPUT
*   
* RETURNS
*
*   DBL result usual 0.0 to 1.0 but may be 2.0 in hexagon
*   
* AUTHOR
*
*   Adapted from Add_Pigment by Chris Young
*   
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

DBL Evaluate_TPat (const TPATTERN *TPat, const VECTOR EPoint, const Intersection *Isection, const Ray *ray, TraceThreadData *Thread)
{
	DBL value = 0.0;

	/* NK 19 Nov 1999 removed Warp_EPoint call */

	switch(TPat->Type)
	{
		case AGATE_PATTERN:       value = agate_pattern      (EPoint, TPat, GetNoiseGen(TPat, Thread)); break;
		case BOZO_PATTERN:
		case SPOTTED_PATTERN:
		case BUMPS_PATTERN:       value = Noise              (EPoint,       GetNoiseGen(TPat, Thread)); break;
		case BRICK_PATTERN:       value = brick_pattern      (EPoint, TPat);                            break;
		case CELLS_PATTERN:       value = cells_pattern      (EPoint);                                  break;
		case CHECKER_PATTERN:     value = checker_pattern    (EPoint);                                  break;
		case CRACKLE_PATTERN:     value = crackle_pattern    (EPoint, TPat, Thread);                    break;
		case GRADIENT_PATTERN:    value = gradient_pattern   (EPoint, TPat);                            break;
		case GRANITE_PATTERN:     value = granite_pattern    (EPoint,       GetNoiseGen(TPat, Thread)); break;
		case HEXAGON_PATTERN:     value = hexagon_pattern    (EPoint);                                  break;
		case SQUARE_PATTERN:      value = square_pattern     (EPoint);                                  break;
		case TRIANGULAR_PATTERN:  value = triangular_pattern (EPoint);                                  break;
		case CUBIC_PATTERN:       value = cubic_pattern      (EPoint);                                  break;
		case JULIA_PATTERN:       value = julia_pattern      (EPoint, TPat);                            break;
		case JULIA3_PATTERN:      value = julia3_pattern     (EPoint, TPat);                            break;
		case JULIA4_PATTERN:      value = julia4_pattern     (EPoint, TPat);                            break;
		case JULIAX_PATTERN:      value = juliax_pattern     (EPoint, TPat);                            break;
		case LEOPARD_PATTERN:     value = leopard_pattern    (EPoint);                                  break;
		case MAGNET1M_PATTERN:    value = magnet1m_pattern   (EPoint, TPat);                            break;
		case MAGNET1J_PATTERN:    value = magnet1j_pattern   (EPoint, TPat);                            break;
		case MAGNET2M_PATTERN:    value = magnet2m_pattern   (EPoint, TPat);                            break;
		case MAGNET2J_PATTERN:    value = magnet2j_pattern   (EPoint, TPat);                            break;
		case MANDEL_PATTERN:      value = mandel_pattern     (EPoint, TPat);                            break;
		case MANDEL3_PATTERN:     value = mandel3_pattern    (EPoint, TPat);                            break;
		case MANDEL4_PATTERN:     value = mandel4_pattern    (EPoint, TPat);                            break;
		case MANDELX_PATTERN:     value = mandelx_pattern    (EPoint, TPat);                            break;
		case MARBLE_PATTERN:      value = marble_pattern     (EPoint, TPat, GetNoiseGen(TPat, Thread)); break;
		case ONION_PATTERN:       value = onion_pattern      (EPoint);                                  break;
		case RADIAL_PATTERN:      value = radial_pattern     (EPoint);                                  break;
		case SPIRAL1_PATTERN:     value = spiral1_pattern    (EPoint, TPat, GetNoiseGen(TPat, Thread)); break;
		case SPIRAL2_PATTERN:     value = spiral2_pattern    (EPoint, TPat, GetNoiseGen(TPat, Thread)); break;
		case WOOD_PATTERN:        value = wood_pattern       (EPoint, TPat);                            break;
		case WAVES_PATTERN:       value = waves_pattern      (EPoint, TPat, Thread);                    break;
		case RIPPLES_PATTERN:     value = ripples_pattern    (EPoint, TPat, Thread);                    break;
		case WRINKLES_PATTERN:    value = wrinkles_pattern   (EPoint,       GetNoiseGen(TPat, Thread)); break;
		case DENTS_PATTERN:       value = dents_pattern      (EPoint,       GetNoiseGen(TPat, Thread)); break;
		case QUILTED_PATTERN:     value = quilted_pattern    (EPoint, TPat);                            break;
		case FUNCTION_PATTERN:    value = function_pattern   (EPoint, TPat, Thread);                    break;
		case PLANAR_PATTERN:      value = planar_pattern     (EPoint);                                  break;
		case BOXED_PATTERN:       value = boxed_pattern      (EPoint);                                  break;
		case SPHERICAL_PATTERN:   value = spherical_pattern  (EPoint);                                  break;
		case CYLINDRICAL_PATTERN: value = cylindrical_pattern(EPoint);                                  break;
		case DENSITY_FILE_PATTERN:value = density_pattern    (EPoint, TPat);                            break;
		case IMAGE_PATTERN:       value = image_pattern      (EPoint, TPat);                            break;
		case SLOPE_PATTERN:       value = slope_pattern      (EPoint, TPat, Isection);                  break;
		case AOI_PATTERN:         value = aoi_pattern        (Isection, ray);                           break;
		case PAVEMENT_PATTERN:    value = pavement_pattern   (EPoint, TPat);                            break;
		case TILING_PATTERN:      value = tiling_pattern     (EPoint, TPat);                            break;
		case PIGMENT_PATTERN:     value = pigment_pattern    (EPoint, TPat, Isection, ray, Thread);     break;
		case OBJECT_PATTERN:      value = object_pattern     (EPoint, TPat, Thread);                    break;

		default: throw POV_EXCEPTION_STRING("Problem in Evaluate_TPat.");
	}

	if(TPat->Frequency != 0.0)
		value = fmod(value * TPat->Frequency + TPat->Phase, 1.00001); // TODO FIXME - magic number! Should be 1.0+SOME_EPSILON (or maybe actually 1.0?)

	/* allow negative Frequency */
	if(value < 0.0)
		value -= floor(value);

	switch(TPat->Wave_Type)
	{
		case RAMP_WAVE:
			break;
		case SINE_WAVE:
			value = (1.0 + cycloidal(value)) * 0.5;
			break;
		case TRIANGLE_WAVE:
			value = Triangle_Wave(value);
			break;
		case SCALLOP_WAVE:
			value = fabs(cycloidal(value * 0.5));
			break;
		case CUBIC_WAVE:
			value = Sqr(value) * ((-2.0 * value) + 3.0);
			break;
		case POLY_WAVE:
			value = pow(value, (DBL) TPat->Exponent);
			break;
		default:
			throw POV_EXCEPTION_STRING("Unknown Wave Type.");
	}

	return value;
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Init_TPat_Fields (TPATTERN *Tpat)
{
	Tpat->Type       = NO_PATTERN;
	Tpat->Wave_Type  = RAMP_WAVE;
	Tpat->Flags      = NO_FLAGS;
	Tpat->References = 1;
	Tpat->Exponent   = 1.0;
	Tpat->Frequency  = 1.0;
	Tpat->Phase      = 0.0;
	Tpat->Warps      = NULL;
	Tpat->Next       = NULL;
	Tpat->Blend_Map  = NULL;
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Copy_TPat_Fields (TPATTERN *New, const TPATTERN *Old)
{
	*New = *Old;

	/* Copy warp chain */
	New->Warps = Copy_Warps(Old->Warps);

	New->Blend_Map = Copy_Blend_Map(Old->Blend_Map);

	/* Note, cannot copy Old->Next because we don't know what kind of
	   thing this is.  It must be copied by Copy_Pigment, Copy_Tnormal etc.
	*/

	/* NK 1998 - added IMAGE_PATTERN */
	if ((Old->Type == BITMAP_PATTERN) || (Old->Type == IMAGE_PATTERN))
	{
		New->Vals.image = Copy_Image(Old->Vals.image);
	}

	if (Old->Type == DENSITY_FILE_PATTERN)
	{
		New->Vals.Density_File = Copy_Density_File(Old->Vals.Density_File);
	}

	if (Old->Type == PIGMENT_PATTERN )
	{
		New->Vals.Pigment = Copy_Pigment(Old->Vals.Pigment);
	}

	if (Old->Type == OBJECT_PATTERN)
	{
		if(Old->Vals.Object != NULL)
		{
			New->Vals.Object = reinterpret_cast<ObjectPtr>(Copy_Object(Old->Vals.Object));
		}
	}

	if (Old->Type == FUNCTION_PATTERN)
	{
		if (Old->Vals.Function.Fn != NULL)
		{
			New->Vals.Function.Fn = reinterpret_cast<void *>(Parser::Copy_Function( Old->Vals.Function.vm, (FUNCTION_PTR)(Old->Vals.Function.Fn) ));
		}
	}
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Destroy_TPat_Fields(TPATTERN *Tpat)
{
	Destroy_Warps(Tpat->Warps);
	Destroy_Blend_Map(Tpat->Blend_Map);
	/* Note, cannot destroy Tpat->Next nor pattern itself because we don't
	   know what kind of thing this is.  It must be destroied by Destroy_Pigment, etc.
	*/

	if ((Tpat->Type == BITMAP_PATTERN) || (Tpat->Type == IMAGE_PATTERN))
	{
		Destroy_Image(Tpat->Vals.image);
	}

	if (Tpat->Type == DENSITY_FILE_PATTERN)
	{
		Destroy_Density_File(Tpat->Vals.Density_File);
	}

	if (Tpat->Type == OBJECT_PATTERN)
	{
		if(Tpat->Vals.Object != NULL)
		{
			Destroy_Object(reinterpret_cast<ObjectPtr>(Tpat->Vals.Object));
		}
	}

	if (Tpat->Type == PIGMENT_PATTERN)
	{
		if (Tpat->Vals.Pigment != NULL)
		{
			Destroy_Pigment( Tpat->Vals.Pigment );
		}
	}

	if (Tpat->Type == FUNCTION_PATTERN)
	{
		if (Tpat->Vals.Function.Fn != NULL)
		{
			Parser::Destroy_Function(Tpat->Vals.Function.vm, (FUNCTION_PTR)(Tpat->Vals.Function.Fn));
		}
	}
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

TURB *Create_Turb()
{
	TURB *New;

	New = reinterpret_cast<TURB *>(POV_MALLOC(sizeof(TURB),"turbulence struct"));

	Make_Vector(New->Turbulence, 0.0, 0.0, 0.0);

	New->Octaves = 6;
	New->Omega = 0.5;
	New->Lambda = 2.0;

	return(New);
}


/*****************************************************************************
*
* FUNCTION
*
*   Translate_Tpattern
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Translate_Tpattern(TPATTERN *Tpattern, const VECTOR Vector)
{
	TRANSFORM Trans;

	if (Tpattern != NULL)
	{
		Compute_Translation_Transform (&Trans, Vector);

		Transform_Tpattern (Tpattern, &Trans);
	}
}


/*****************************************************************************
*
* FUNCTION
*
*   Rotate_Tpattern
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Rotate_Tpattern(TPATTERN *Tpattern, const VECTOR Vector)
{
	TRANSFORM Trans;

	if (Tpattern != NULL)
	{
		Compute_Rotation_Transform (&Trans, Vector);

		Transform_Tpattern (Tpattern, &Trans);
	}
}


/*****************************************************************************
*
* FUNCTION
*
*   Scale_Tpattern
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Scale_Tpattern(TPATTERN *Tpattern, const VECTOR Vector)
{
	TRANSFORM Trans;

	if (Tpattern != NULL)
	{
		Compute_Scaling_Transform (&Trans, Vector);

		Transform_Tpattern (Tpattern, &Trans);
	}
}


/*****************************************************************************
*
* FUNCTION
*
*   Transform_Tpattern
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Transform_Tpattern(TPATTERN *Tpattern, const TRANSFORM *Trans)
{
	WARP *Temp;

	if (Tpattern != NULL)
	{
		if (Tpattern->Warps == NULL)
		{
			Tpattern->Warps = Create_Warp(TRANSFORM_WARP);
		}
		else
		{
			if (Tpattern->Warps->Warp_Type != TRANSFORM_WARP)
			{
				Temp = Tpattern->Warps;

				Tpattern->Warps = Create_Warp(TRANSFORM_WARP);

				Tpattern->Warps->Next_Warp = Temp;
				if(Tpattern->Warps->Next_Warp != NULL)
					Tpattern->Warps->Next_Warp->Prev_Warp = Tpattern->Warps;
			}
		}

		Compose_Transforms (&( (reinterpret_cast<TRANS *>(Tpattern->Warps))->Trans), Trans);
	}
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

void Search_Blend_Map (DBL value, const BLEND_MAP *Blend_Map, const BLEND_MAP_ENTRY **Prev, const BLEND_MAP_ENTRY **Cur)
{
	BLEND_MAP_ENTRY *P, *C;
	int Max_Ent=Blend_Map->Number_Of_Entries-1;

	/* if greater than last, use last. */

	if (value >= Blend_Map->Blend_Map_Entries[Max_Ent].value)
	{
		P = C = &(Blend_Map->Blend_Map_Entries[Max_Ent]);
	}
	else
	{
		P = C = &(Blend_Map->Blend_Map_Entries[0]);

		while (value > C->value)
		{
			P = C++;
		}
	}

	if (value == C->value)
	{
		P = C;
	}

	*Prev = P;
	*Cur  = C;
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static const TURB *Search_For_Turb(const WARP *Warps)
{
	const WARP* Temp=Warps;

	if (Temp!=NULL)
	{
		while (Temp->Next_Warp != NULL)
		{
			Temp=Temp->Next_Warp;
		}

		if (Temp->Warp_Type != CLASSIC_TURB_WARP)
		{
			Temp=NULL;
		}
	}

	return (reinterpret_cast<const TURB *>(Temp));
}

/* Tiling & Pavement */


/*****************************************************************************
*
* FUNCTIONS
*
*   related to Tiling & Pavement
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   J. Grimbert
*
* DESCRIPTION
*
*   New colour function by J. Grimbert
*
*   Fill the X-Z plane with repeating pattern
*   When there is only one kind of form, 1 is the outside, 0 is most inside
*   When there is two kind of form, 0 & 1 are the most inside, 1/2 is
*    the outside
*
******************************************************************************/

static DBL tiling_square (const VECTOR EPoint)
{
	/* 
	 ** Classical square tiling
	 */
	DBL x,z;
	x = fabs(EPoint[X]);
	x -= floor(x);
	x = 2*fabs( x-0.5 );

	z = fabs(EPoint[Z]);
	z -= floor(z);
	z = 2*fabs( z-0.5 );

	return max(x,z);
}

static DBL tiling_hexagon (const VECTOR EPoint)
{
	/* 
	 ** Classical Hexagon tiling
	 */
	DBL x,z;
	DBL dist1,dist2;
	DBL answer;

	x=EPoint[X];
	z=EPoint[Z];
	x += 0.5;
	x -= 3.0*floor(x/3.0);
	z -= SQRT3*floor(z/(SQRT3));
	/* x,z is in { [0.0, 3.0 [, [0.0, SQRT3 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > SQRT3_2 )
	{
		z = SQRT3 - z;
	}
	/* 
	 ** Now only [0,3[,[0,SQRT3/2[
	 */
	if (x > 1.5)
	{
		x -= 1.5 ; /* translate */
		z = SQRT3_2 -z; /* mirror */
	}
	/*
	 ** And now, it is even simpler :  [0,1.5],[0,SQRT3/2]
	 ** on the bottom left corner, part of some other hexagon
	 ** on the top right corner, center of the hexagon
	 */
	if ((SQRT3*x+z)<SQRT3_2)
	{
		x = 0.5 - x;
		z = SQRT3_2 -z; /* mirror */
	}
	if (x > 1.0)
	{
		x = 2.0 -x; /* mirror */
	}
	/* Hexagon */
	dist1 = 1.0 - ( z / SQRT3_2 );
	dist2 = 1.0 - (((SQRT3 * x + z - SQRT3_2) ) / SQRT3 );
	answer = max(dist1,dist2);
	answer = min(1.0,answer);
	answer = max(0.0,answer);
	return answer;
}

static DBL tiling_triangle (const VECTOR EPoint)
{
	DBL x,z;
	DBL slop1;
	DBL dist1,dist2;
	int delta;
	x=EPoint[X];
	z=EPoint[Z];
	delta = 0;
	x -= floor(x);
	z -= SQRT3*floor(z/SQRT3);
	/* x,z is in { [0.0, 1.0 [, [0.0, SQRT3 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > SQRT3_2 )
	{
		z = SQRT3 - z; /* mirror */
		delta = 1 - delta;
	}
	if (x > 0.5)
	{
		x = 1.0 - x; /* mirror */
	}
	if (x != 0.0)
	{
		slop1 = z/x;
		if (slop1>SQRT3)
		{
			z = SQRT3_2 - z;
			x = 0.5 -x;
			delta = 1 - delta;
		}
	}
	else
	{
		z = SQRT3_2 -z;
		x = 0.5;
	}
	dist1 = 1.0 - (z * 2 * SQRT3 );
	dist2 = 1.0 - ((SQRT3 * x - z) * SQRT3 );
	return delta/2.0+(0.5)*max(dist1,dist2);
}

static DBL tiling_lozenge (const VECTOR EPoint)
{
	DBL x,z;
	DBL slop1;
	DBL dist1,dist2;

	x=EPoint[X];
	z=EPoint[Z];

	x -= floor(x);
	z -= SQRT3*floor(z/SQRT3);
	/* x,z is in { [0.0, 1.0 [, [0.0, SQRT3 [ } 
	 ** There is some mirror to reduce the problem
	 */
	if ( z > SQRT3_2 )
	{
		z -= SQRT3_2;
		x += 0.5;
	}
	if ( (2.0*z) > SQRT3_2 )
	{
		z = SQRT3_2 - z;
		x = 1.5 - x;
	}
	if (x > 0.75)
	{
		x -= 1.0;
	}
	if (x != 0.0)
	{
		slop1 = z/x;
		if (slop1>SQRT3)
		{
			z = SQRT3_2 - z;
			x = 0.5 -x;
		}
	}
	dist1 = 1.0 - (z * 4.0 * SQRT3 / 3.0 );
	dist2 = 1.0 - (fabs(SQRT3 * x - z) * SQRT3 *2.0 / 3.0);
	return max(dist1,dist2);
}

static DBL tiling_rhombus (const VECTOR EPoint)
{
	DBL x,z;
	DBL answer;
	DBL slop1;
	DBL dist1,dist2;
	int delta;
	x=EPoint[X];
	z=EPoint[Z];
	delta = 0;
	x += 0.5;
	x -= 3.0*floor(x/3.0);
	z -= SQRT3*floor(z/SQRT3);
	/* x,z is in { [0.0, 3.0 [, [0.0, SQRT3 [ } 
	 ** There is some mirror to reduce the problem
	 */
	if ( z > SQRT3_2 )
	{
		z = SQRT3 -z; /* mirror */
		delta = 2 - delta;
	}
	if (x > 1.5)
	{
		x -= 1.5 ; /* translate */
		z = SQRT3_2 -z; /* mirror */
		delta = 2 - delta;
	}
	/* Now in [0,1.5],[0,SQRT3/2] 
	 ** from left to right
	 ** part of a horizontal (z=0)
	 ** half a vertical 
	 ** part of a horizontal 
	 */
	if (x < 0.5)
	{
		/* mirrror */
		x = 1.0 - x;
		delta = 2 - delta;
	}
	/* 
	 ** Let shift the [0.5,1.5],[0,SQRT3/2] to [0,1]....
	 */
	x -= 0.5;
	if (x != 0.0)
	{
		slop1 = z/x;
		if (slop1>SQRT3)
		{ /* rotate the vertical to match the horizontal on the right */
			dist1 = ( x / 2.0 ) + ( z * SQRT3_2 );
			dist2 = ( z / 2.0 ) - ( x * SQRT3_2 );
			z = dist2;
			x = dist1;
			delta = 1;
		}
	}
	else
	{
		/* rotate the vertical to match the horizontal on the right */
		dist1 = ( x / 2.0 ) + ( z * SQRT3_2 );
		dist2 = ( z / 2.0 ) - ( x * SQRT3_2 );
		z = dist2;
		x = dist1;
		delta = 1;
	}
	/* It may be similar to lozenge (in fact, IT IS !), now */

	if ( (2.0*z) > SQRT3_2 )
	{
		z = SQRT3_2 - z;
		x = 1.5 - x;
	}
	if (x > 0.75)
	{
		x -= 1.0;
	}
	if (x != 0.0)
	{
		slop1 = z/x;
		if (slop1>SQRT3)
		{
			z = SQRT3_2 - z;
			x = 0.5 -x;
		}
	}
	dist1 = 1.0 - (z * 4.0 * SQRT3 / 3.0 );
	dist2 = 1.0 - (fabs(SQRT3 * x - z) * SQRT3 *2.0 / 3.0);
	answer = max(dist1,dist2);
	answer /= 3.0;
	answer += delta/3.0;
	answer = min(1.0,answer);
	answer = max(0.0,answer);
	return answer;
}

static DBL tiling_rectangle (const VECTOR EPoint)
{
	/*
	 ** Tiling with rectangles
	 ** resolve to square [0,4][0,4]
	 ** then 16 cases
	 **
	 **  +-----+--+  +
	 **  |     |  |  |
	 **  +--+--+  +--+
	 **     |  |  |
	 **  +--+  +--+--+
	 **  |  |  |     |
	 **  +  +--+--+--+
	 **  |  |     |  |
	 **  +--+-----+  +
	 */
	DBL x,z;
	DBL delta;
	DBL answer;
	int valueX,valueZ;
	x=EPoint[X];
	z=EPoint[Z];
	x -= 4.0*floor(x/4.0);
	z -= 4.0*floor(z/4.0);
	valueX = (int)x;
	valueZ = (int)z;
	delta = 1.0;
	switch((valueX+valueZ*4))
	{
		case 0:
		case 4:
			z -= 1.0;
			break;
		case 1:
		case 2:
			x -= 2.0;
			delta = 0.0;
			break;
		case 3:
			x -= 3.0;
			break;
		case 5:
		case 9:
			x -= 1.0;
			z -= 2.0;
			break;
		case 6:
		case 7:
			x -= 3.0;
			z -= 1.0;
			delta = 0.0;
			break;
		case 8:
			z -= 2.0;
			delta = 0.0;
			break;
		case 10:
		case 14:
			x -= 2.0;
			z -= 3.0;
			break;
		case 11:
			x -= 4.0;
			z -= 2.0;
			delta = 0.0;
			break;
		case 12:
		case 13:
			x -= 1.0;
			z -= 3.0;
			delta = 0.0;
			break;
		case 15:
			x -= 3.0;
			z -= 4.0;
			break;
	}
	if (delta == 1.0)
	{
		x = 2*fabs( x-0.5 );
		z = 2*( max(fabs( z),0.5) -0.5 );
	}
	else
	{
		x = 2*( max(fabs( x),0.5) -0.5 );
		z = 2*fabs( z -0.5 );
	}
	answer = fabs(max(x,z)/2.0 + delta/2);
	return answer;
}

static DBL tiling_octa_square (const VECTOR EPoint)
{
	/*
	 ** Tiling with a square and an octagon
	 */
	DBL answer;
	DBL x,z;
	DBL dist1,dist2;
	z=EPoint[Z];
	z -= (SQRT2+1.0)*floor(z/(SQRT2+1.0));
	z -= SQRT2_2+0.5;
	z = fabs(z);
	x=EPoint[X];
	x -= (SQRT2+1.0)*floor(x/(SQRT2+1.0));
	x -= SQRT2_2+0.5;
	x = fabs(x);
	if (z > x)
	{
		answer = x;
		x = z;
		z = answer;
	}
	if ( (x+z) < SQRT2_2)
	{
		/* Square tile */
		return ( (x+z)/SQRT2 );
	}
	dist1 = 1.0-z;
	dist2 = (SQRT2+SQRT2_2-(x+z))/SQRT2;

	return max(0.500000000001,max(dist1,dist2)); // TODO FIXME - magic number! Use nextafter() instead (or maybe actually 0.5)
}

static DBL tiling_square_triangle (const VECTOR EPoint)
{
	DBL x,z;
	DBL slop1;
	DBL dist1,dist2;
	int delta;
	x=EPoint[X];
	z=EPoint[Z];
	delta = 0;
	x -= floor(x);
	z -= (2.0+SQRT3)*floor(z/(SQRT3+2.0));
	/* x,z is in { [0.0, 1.0 [, [0.0, 2+SQRT3 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > SQRT3_2+1.0 )
	{
		z -= SQRT3_2+1.0;
		x += (x>0.5)?-0.5:0.5;
	}
	if (x > 0.5)
	{
		x = 1.0 - x; /* mirror */
	}
	z -= 1.0;
	if (z > 0.0)
	{ /* triangle */
		if (x != 0.0)
		{
			slop1 = z/x;
			if (slop1>SQRT3)
			{
				z = SQRT3_2 - z;
				x = 0.5 -x;
				delta = 1 - delta;
			}
		}
		else
		{
			z = SQRT3_2 -z;
			x = 0.5;
			delta = 1 - delta;
		}
		dist1 = 1.0 - (2 * z * SQRT3 );
		dist2 = 1.0 - ((SQRT3 * x - z) * SQRT3 );
		return (delta+max(dist1,dist2))/3.0;
	}
	else
	{ /* square */
		if (z < -0.5)
		{
			z = -1.0 -z;
		}
		if (x > 0.5)
		{
			x = 1.0 - x;
		}
		dist1 = 2 + 2 * SQRT3 * fabs( x ) ;
		dist2 = 2 + 2 * SQRT3 * fabs( z ) ;
		dist1 = min(dist1,3.0);
		dist2 = min(dist2,3.0);
		return (5.0000001-min(dist1,dist2))/3.0; // TODO FIXME - magic number! Should use nextafter()
	}
}

static DBL tiling_hexa_triangle (const VECTOR EPoint)
{
	/* 
	 ** Tiling with a hexagon and 2 triangles
	 */
	DBL x,z;
	DBL dist1,dist2;
	DBL answer=0;
	int delta;
	x=EPoint[X];
	z=EPoint[Z];
	delta = 0;
	x += 0.5;
	x -= 2.0*floor(x/2.0);
	z -= 2.0*SQRT3*floor(z/(SQRT3*2.0));
	/* x,z is in { [0.0, 2.0 [, [0.0, 2*SQRT3 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > SQRT3 )
	{
		z -= SQRT3;
		x += (x<1.0)?1.0:-1.0;
	}
	/* 
	 ** Now only [0,2[,[0,SQRT3[
	 */
	if (z > SQRT3_2 )
	{
		z = SQRT3 - z; /* mirror */
		delta = 1 - delta;
	}

	if (x > 1.0)
	{
		x = 2.0 - x; /* mirror */
	}
	/*
	 ** And now, it is even simpler :  [0,1],[0,SQRT3/2]
	 ** on the bottom left corner, part of the triangle
	 ** on the top right corner, center of the hexagon
	 */
	if ((SQRT3*x+z)<SQRT3_2)
	{
		/* Triangle */
		dist1 = 1.0 - (z * 2 * SQRT3 );
		dist2 = 1.0 + ((SQRT3 * x + z) - SQRT3_2 ) * SQRT3; /* really substracting */
		answer = (delta + max(dist1,dist2))/3.0;
	}
	else
	{
		/* Hexagon */
		dist1 = 2 + 2* (z * SQRT3 );
		dist2 = 2 + 2* ((SQRT3 * x + z - SQRT3_2) ) * SQRT3_2 ;
		answer = 5.0-min(dist1,dist2);
		answer = max(answer,2.0000001); // TODO FIXME - magic number! Should use nextafter()
		answer /= 3.0;
	}
	return answer;
}

static DBL tiling_square_offset (const VECTOR EPoint)
{
	/*
	 ** Tiling with a square, offset of half size
	 ** Reduce to rectangle [0,1][0,2]
	 ** move x,[1,2] to [0,1][0,1] with new x = x+1/2
	 */
	DBL x,z;
	z = EPoint[Z];
	z -= 2.0*floor(z/2.0);
	x = EPoint[X];
	if (z > 1.0)
	{
		x += 0.5;
		z -= 1.0;
	}
	x -= floor(x);
	x = 2*fabs( x-0.5 );
	z = 2*fabs( z-0.5 );

	return max(x,z);
}

static DBL tiling_square_rectangle (const VECTOR EPoint)
{
	/*
	 ** tiling with a central square and 4 rectangle (2x1)
	 ** orbiting around the square
	 ** Reduce to [0,3][0,3]
	 ** then 9 cases
	 **
	 **  +-----+--+
	 **  |     |  |
	 **  +--+--+  |
	 **  |  |  |  |
	 **  |  +--+--+
	 **  |  |     |
	 **  +--+-----+
	 */
	DBL x,z;
	DBL delta;
	int valueX,valueZ;
	x = EPoint[X];
	x -= 3.0*floor(x/3.0);
	z = EPoint[Z];
	z -= 3.0*floor(z/3.0);
	valueX = (int)x;
	valueZ = (int)z;
	delta = 2.0;
	switch((valueX+valueZ*3))
	{
		case 0:
		case 3:
			z -= 1.0;
			break;
		case 1:
		case 2:
			x -= 2.0;
			delta = 1.0;
			break;
		case 4:
			x -= 1.0;
			z -= 1.0;
			delta = 0.0;
			break;
		case 5:
		case 8:
			x -= 2.0;
			z -= 2.0;
			break;
		case 6:
		case 7:
			x -= 1.0;
			z -= 2.0;
			delta = 1.0;
			break;
	}
	if (delta == 1.0)
	{
		x = fabs( x);
		x = 2.0*( max( x,0.5) -0.5 );
		z = 2.0*fabs( z -0.5 );
	}
	if (delta == 2.0)
	{
		x = 2.0*fabs( x-0.5 );
		z = fabs(z);
		z = 2.0*( max(z,0.5) - 0.5 );
	}
	if (delta == 0.0)
	{
		x = 2.0*fabs( x-0.5 );
		z = 2.0*fabs( z-0.5 );
	}
	return ((max(x,z))+delta)/3.0 ;
}

static DBL tiling_rectangle_square (const VECTOR EPoint)
{
	/*
	 ** Tiling with a central square and 4 rectangles (2x1)
	 ** which turns around the square in both directions
	 ** Reduce to [0,6][0,6], fold in four and back
	 ** to 9 cases.
	 **
	 **  +-----+--+
	 **  |     |  |
	 **  +--+--+  |
	 **  |  |  |  |
	 **  |  +--+--+
	 **  |  |     |
	 **  +--+--+--+
	 */
	DBL x,z;
	DBL delta;
	int valueX,valueZ;
	x = EPoint[X];
	x -= 6.0*floor(x/6.0);
	x -= 3.0;
	x = fabs(x);
	z = EPoint[Z];
	z -= 6.0*floor(z/6.0);
	z -= 3.0;
	z = fabs(z);
	valueX = (int)x;
	valueZ = (int)z;
	delta = 2.0;
	switch((valueX+valueZ*3))
	{
		case 0:
		case 3:
			z -= 1.0;
			break;
		case 1:
		case 2:
			x -= 2.0;
			delta = 1.0;
			break;
		case 4:
			x -= 1.0;
			z -= 1.0;
			delta = 0.0;
			break;
		case 5:
		case 8:
			x -= 2.0;
			z -= 2.0;
			break;
		case 6:
		case 7:
			x -= 1.0;
			z -= 2.0;
			delta = 1.0;
			break;
	}
	if (delta == 1.0)
	{
		x = fabs( x);
		x = 2.0*( max(x,0.5) -0.5 );
		z = 2.0*fabs( z -0.5 );
	}
	if (delta == 2.0)
	{
		x = 2.0*fabs( x-0.5 );
		z = fabs(z);
		z = 2.0*( max(z,0.5) - 0.5 );
	}
	if (delta == 0.0)
	{
		x = 2.0*fabs( x-0.5 );
		z = 2.0*fabs( z-0.5 );
	}
	return ((max(x,z))+delta)/3.0 ;
}

static DBL tiling_square_internal (const VECTOR EPoint)
{
	DBL answer=0;
	DBL x,z;
	DBL dist1,dist2;
	int valueX,valueZ;
	x=EPoint[X];
	x *= SQRT2;
	x -= 4.0 * floor(x/4.0);
	x -= 2.0;
	x = fabs(x);
	z=EPoint[Z];
	z *= SQRT2;
	z -= 4.0 * floor(z/4.0);
	z -= 2.0;
	z = fabs(z);
	valueX=(int)x;
	valueZ=(int)z;
	switch((valueX+valueZ*2))
	{
		case 0:
			x -= 0.5;
			x = max(x,0.0);
			x *= 2.0;
			z -= 0.5;
			z = max(z,0.0);
			z *= 2.0;
			answer=max(x,z)/3.0;
			break;
		case 1:
			answer=(2.0+fabs(1.5-x)*2.0)/3.0;
			if (z>0.5)
			{
				dist2=(3.0-SQRT2*fabs(x-z))/3.0;
				answer = max(answer,dist2);
			}
			break;
		case 2:
			answer=(1.0+fabs(1.5-z)*2.0)/3.0;
			if (x>0.5)
			{
				dist2=(2.0-SQRT2*fabs(x-z))/3.0;
				answer = max(answer,dist2);
			}
			break;
		case 3:
			if (x > z)
			{
				dist1=(2.0+fabs(1.5-x)*2.0)/3.0;
				dist2=(3.0-SQRT2*fabs(z-x))/3.0;
				answer=max(dist1,dist2);
			}
			else
			{
				dist1=(1.0+fabs(1.5-z)*2.0)/3.0;
				dist2=(2.0-SQRT2*fabs(x-z))/3.0;
				answer=max(dist1,dist2);
			}
			break;
	}
	return answer;
}

static DBL tiling_square_internal_5 (const VECTOR EPoint)
{
	DBL answer=0;
	DBL x,z;
	DBL dist1,dist2;
	int mirX,mirZ;
	int valueX,valueZ;
	mirX=mirZ=0;
	x=EPoint[X];
	x *= SQRT2;
	x -= 4.0 * floor(x/4.0);
	x -= 2.0;
	mirX = (x < 0)?1:0;
	x = fabs(x);
	z=EPoint[Z];
	z *= SQRT2;
	z -= 4.0 * floor(z/4.0);
	z -= 2.0;
	mirZ = (z < 0)?2:3;
	z = fabs(z);
	valueX=(int)x;
	valueZ=(int)z;
	switch((valueX+valueZ*2))
	{
		case 0:
			x -= 0.5;
			x = max(x,0.0);
			x *= 2.0;
			z -= 0.5;
			z = max(z,0.0);
			z *= 2.0;
			answer=(4.000001 + max(x,z))/5.0; // TODO FIXME - magic number! Should use nextafter()
			break;
		case 1:
			answer=fabs(1.5-x)*2.0;
			if (z>0.5)
			{
				dist2=1.0-SQRT2*fabs(x-z);
				answer = max(answer,dist2);
			}
			answer += mirX;
			answer /= 5.0;
			break;
		case 2:
			answer=fabs(1.5-z)*2.0;
			if (x>0.5)
			{
				dist2=1.0-SQRT2*fabs(x-z);
				answer = max(answer,dist2);
			}
			answer += mirZ;
			answer /= 5.0;
			break;
		case 3:
			if (x > z)
			{
				dist1=fabs(1.5-x)*2.0;
				dist2=1.0-SQRT2*fabs(z-x);
				answer=max(dist1,dist2) + mirX;
			}
			else
			{
				dist1=fabs(1.5-z)*2.0;
				dist2=1.0-SQRT2*fabs(x-z);
				answer=max(dist1,dist2) + mirZ;
			}
			answer /= 5.0;
			break;
	}
	return answer;
}

static DBL tiling_square_double (const VECTOR EPoint)
{
	/*
	 ** Tiling with a square (1x1) and a square (2x2)
	 ** Reduce to [0,5][0,5] then 25 cases
	 **
	 **  +--+     +--+--+
	 **     |     |  |  
	 **     +--+--+--+   
	 **     |  |     |  
	 **  +--+--+     +--+
	 **  |     |     |  |
	 **  +     +--+--+--+
	 **  |     |  |     |
	 **  +--+--+--+     +
	 **  |  |     |     |
	 **  +--+     +--+--+
	 */
	DBL x,z;
	DBL delta;
	int valueX,valueZ;
	x = EPoint[X];
	x -= 5.0*floor(x/5.0);
	z = EPoint[Z];
	z -= 5.0*floor(z/5.0);
	valueX = (int)x;
	valueZ = (int)z;
	delta = 0.50000001; // TODO FIXME - magic number! Should use nextafter()
	switch((valueX+valueZ*5))
	{
		case 0:
			delta = 0.0;
			break;
		case 1:
		case 2:
			x -= 2.0;
			break;
		case 3:
		case 4:
		case 8:
		case 9:
			x -= 4.0;
			z -= 1.0;
			break;
		case 5 :
		case 6 :
		case 10:
		case 11:
			z -= 2.0;
			x -= 1.0;
			break;
		case 7:
			delta = 0.0;
			x -= 2.0;
			z -= 1.0;
			break;
		case 12:
		case 13:
		case 17:
		case 18:
			x -= 3.0;
			z -= 3.0;
			break;
		case 14:
			x -= 4.0;
			z -= 2.0;
			delta = 0.0;
			break;
		case 15:
		case 20:
			z -= 4.0;
			break;
		case 16:
			x -= 1.0;
			z -= 3.0;
			delta = 0.0;
			break;
		case 21:
		case 22:
			x -= 2.0;
			z -= 5.0;
			break;
		case 23:
			x -= 3.0;
			z -= 4.0;
			delta = 0.0;
			break;
		case 24:
		case 19:
			x -= 5.0;
			z -= 4.0;
			break;
	}
	if (delta)
	{
		x = fabs(x);
		x = 2*( max(x,0.5) -0.5 );
		z = fabs(z);
		z = 2*( max(z,0.5) -0.5 );
	}
	else
	{
		x = 2*fabs( x-0.5 );
		z = 2*fabs( z-0.5 );
	}
	return fabs((max(x,z))/2.0+delta);
}

static DBL tiling_hexa_square_triangle (const VECTOR EPoint)
{
	/* 
	 ** tiling with 1 hexagon, squares and triangles
	 */
	DBL x,z;
	DBL dist1,dist2;
	DBL answer=0;

	x=EPoint[X];
	z=EPoint[Z];
	x += 0.5;
	x -= (3.0+SQRT3)*floor(x/(3.0+SQRT3));
	z -= 2.0*(1.0+SQRT3)*floor(z/((SQRT3+1.0)*2.0));
	/* x,z is in { [0.0, 3.0+SQRT3 [, [0.0, 2*(1+SQRT3) [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (x > (SQRT3_2+1.5))
	{
		x -= (SQRT3_2+1.5);
		z += (z < SQRT3+1.0)?(SQRT3_2+0.5):(-1*(SQRT3_2+0.5));
	}
	if (z > (SQRT3+1.0) )
	{
		z -= SQRT3+1.0;
	}
	/* 
	 ** Now only [0, SQRT3/2+1.5 ], [0,SQRT3+1.0]
	 */
	if (z > (0.5+SQRT3_2 ) )
	{
		z = SQRT3+1.0 - z; /* mirror */
	}

	if (x > (1.0+SQRT3_2) )
	{
		x = SQRT3+2.0 - x; /* mirror */
	}
	/*
	 ** And now, it is even simpler :  [0,1+(SQRT3/2)],[0,1+(SQRT3/2)]
	 ** on the top right corner, center of the hexagon
	 ** on the bottom right, middle of square, a triangle on the left
	 ** on the top left corner, half a triangle
	 ** bottom
	 */
	if (((SQRT3*x+z)<(SQRT3_2+2.0))&&(x < 0.5+SQRT3_2))
	{
		/* Triangle or square */
		/* rotate in the lower part */
		z -= 0.5 + SQRT3_2 ;
		x -= 1.0 + SQRT3_2 ;
		dist1 = ( x / 2.0 ) - ( z * SQRT3_2 );
		dist2 = ( z / 2.0 ) + ( x * SQRT3_2 );
		z = dist2;
		x = dist1;
		z += 0.5 + SQRT3_2 ;
		x += 1.0 + SQRT3_2 ;
		if (z < 0)
		{
			z *= -1;
		}
		if (x > (1.0+SQRT3_2) )
		{
			x = SQRT3+2.0 - x; /* mirror */
		}
	}
	if ((!((SQRT3*x+z)<(SQRT3_2+2.0)))&&(!(z < 0.5)))
	{
		/* Hexagon */
		z -= 0.5;
		x -= SQRT3_2;
		z= fabs(z);
		x= fabs(x);
		dist1 = 2* z * SQRT3 ;
		dist2 = ((SQRT3 * x + z ) ) * SQRT3 - 1.5;
		answer = 3.0-min(dist1,dist2);
		answer = max(answer,2.0000001); // TODO FIXME - magic number! Should use nextafter()
		answer /= 3.0;
		return answer;
	}
	if (z < 0.5 )
	{
		if ( x > SQRT3_2+0.5)
		{
			/* square */
			x -= SQRT3_2+0.5;
			z -= 0.5;
			dist1 = 1.0 + z*2 * SQRT3;
			dist2 = 1.0 - x*2*SQRT3;
			dist1 = max(dist1,0.0);
			dist2 = max(dist2,0.0);
			answer = max(dist1,dist2)/3.0;
		}
		else
		{
			/* triangle */
			x -= SQRT3_2 + 0.5;
			x = fabs(x);
			dist2 = ((SQRT3 * z + x) ) * SQRT3 +0.5;
			dist1 = 2.0 - (x * 2.0 * SQRT3 );
			answer = (max(dist1,dist2))/3.0;
		}
	}
	return answer;
}

static DBL tiling_hexa_square_triangle_6 (const VECTOR EPoint)
{
	/* 
	 ** tiling with 1 hexagon, squares and triangles
	 ** all tiles get its own colour (according to its orientation)
	 ** 1 hexagon, 3 squares, 2 triangles
	 */
	DBL x,z;
	DBL dist1,dist2;
	DBL answer=0;
	int mirX=0;
	int mirZ=0;
	int rota=0;
	x=EPoint[X];
	z=EPoint[Z];
	x += 0.5;
	x -= (3.0+SQRT3)*floor(x/(3.0+SQRT3));
	z -= 2.0*(1.0+SQRT3)*floor(z/((SQRT3+1.0)*2.0));
	/* x,z is in { [0.0, 3.0+SQRT3 [, [0.0, 2*(1+SQRT3) [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (x > (SQRT3_2+1.5))
	{
		x -= (SQRT3_2+1.5);
		z += (z < SQRT3+1.0)?(SQRT3_2+0.5):(-1*(SQRT3_2+0.5));
	}
	if (z > (SQRT3+1.0) )
	{
		z -= SQRT3+1.0;
	}
	/* 
	 ** Now only [0, SQRT3/2+1.5 ], [0,SQRT3+1.0]
	 */
	if (z > (0.5+SQRT3_2 ) )
	{
		z = SQRT3+1.0 - z; /* mirror */
		mirZ = 1 - mirZ;
	}

	if (x > (1.0+SQRT3_2) )
	{
		x = SQRT3+2.0 - x; /* mirror */
		mirX = 1 - mirX;
	}
	/*
	 ** And now, it is even simpler :  [0,1+(SQRT3/2)],[0,1+(SQRT3/2)]
	 ** on the top right corner, center of the hexagon
	 ** on the bottom right, middle of square, a triangle on the left
	 ** on the top left corner, half a triangle
	 ** bottom
	 */
	if (((SQRT3*x+z)<(SQRT3_2+2.0))&&(x < 0.5+SQRT3_2))
	{
		/* Triangle or square */
		/* rotate in the lower part */
		z -= 0.5 + SQRT3_2 ;
		x -= 1.0 + SQRT3_2 ;
		dist1 = ( x / 2.0 ) - ( z * SQRT3_2 );
		dist2 = ( z / 2.0 ) + ( x * SQRT3_2 );
		z = dist2;
		x = dist1;
		z += 0.5 + SQRT3_2 ;
		x += 1.0 + SQRT3_2 ;
		rota = 1 - rota;
		if (z < 0)
		{
			z *= -1;
		}
		if (x > (1.0+SQRT3_2) )
		{
			x = SQRT3+2.0 - x; /* mirror */
			mirX = 1 - mirX;
			mirZ = 1 - mirZ;
		}
	}
	if ((!((SQRT3*x+z)<(SQRT3_2+2.0)))&&(!(z < 0.5)))
	{
		/* Hexagon */
		z -= 0.5;
		x -= SQRT3_2;
		z= fabs(z);
		x= fabs(x);
		dist1 = 2* z * SQRT3 ;
		dist2 = ((SQRT3 * x + z ) ) * SQRT3 - 1.5;
		answer = 6.0 - min(dist1,dist2);
		answer = max(answer,5.000001); // TODO FIXME - magic number! Should use nextafter()
		answer /= 6.0;
		return answer;
	}
	if (z < 0.5 )
	{
		if ( x > SQRT3_2+0.5)
		{
			/* square */
			x -= SQRT3_2+0.5;
			z -= 0.5;
			dist1 = 1.0 + z*2 * SQRT3;
			dist2 = 1.0 - x*2*SQRT3;
			dist1 = max(dist1,0.0);
			dist2 = max(dist2,0.0);
			answer = (max(dist1,dist2) +  rota * (1.000001 + ((mirX + mirZ) % 2)))/6.0; // TODO FIXME - magic number! Should use nextafter()
		}
		else
		{
			/* triangle */
			x -= SQRT3_2 + 0.5;
			x = fabs(x);
			dist2 = ((SQRT3 * z + x) ) * SQRT3 +0.5;
			dist1 = 2.0 - (x * 2.0 * SQRT3 );
			answer = max(dist1,dist2);
			if ((rota + mirX)%2)
			{
				answer = 2.0 + answer;
			}
			else
			{
				answer = 3.0 + answer;
			}
			answer /= 6.0;
		}
	}
	return answer;
}

static DBL tiling_rectangle_pair (const VECTOR EPoint)
{

	/*
	 ** Tiling with 2 rectangles (2x1)
	 ** Reduce to [0,4][0,4] then 16 cases
	 **
	 **  +-----+--+--+
	 **  |     |  |  |
	 **  +-----+  +  +
	 **  |     |  |  |
	 **  +-----+--+--+
	 **  |  |  |     |
	 **  |  |  +-----+
	 **  |  |  |     |
	 **  +--+--+-----+
	 */
	DBL x,z;
	DBL delta;
	DBL answer;
	int valueX,valueZ;
	x=EPoint[X];
	z=EPoint[Z];
	x -= 4.0*floor(x/4.0);
	z -= 4.0*floor(z/4.0);
	valueX = (int)x;
	valueZ = (int)z;
	delta = 1.0;
	switch((valueX+valueZ*4))
	{
		case 0:
		case 4:
			z -= 1.0;
			break;
		case 1:
		case 5:
			x -= 1.0;
			z -= 1.0;
			break;
		case 2:
		case 3:
			x -= 3.0;
			delta = 0.0;
			break;
		case 6:
		case 7:
			x -= 3.0;
			z -= 1.0;
			delta = 0.0;
			break;
		case 8:
		case 9:
			x -= 1.0;
			z -= 2.0;
			delta = 0.0;
			break;
		case 12:
		case 13:
			x -= 1.0;
			z -= 3.0;
			delta = 0.0;
			break;
		case 10:
		case 14:
			x -= 2.0;
			z -= 3.0;
			break;
		case 11:
		case 15:
			x -= 3.0;
			z -= 3.0;
			break;
	}
	if (delta == 1.0)
	{
		x = 2*fabs( x-0.5 );
		z = 2*( max(fabs( z),0.5) -0.5 );
	}
	else
	{
		x = 2*( max(fabs( x),0.5) -0.5 );
		z = 2*fabs( z -0.5 );
	}
	answer = fabs((max(x,z)+delta)/2.0);
	return answer;
}

static DBL tiling_hexa_tri_right (const VECTOR EPoint)
{
	DBL x,z;
	DBL answer;
	DBL slop1;
	DBL dist1,dist2;
	int zzof;
	int delta;
	x=EPoint[X];
	z=EPoint[Z];
	/* First, resume to a simple pattern */
	zzof = z / SQRT3_2;
	zzof /= 3;
	if (z < 0)
	{
		zzof -= 1;
	}
	x += zzof / 2.0; /* right handed */
	z -= 3*SQRT3_2*floor(z/(3*SQRT3_2));
	x += 7.0;
	x -= 7.0 *floor(x/7.0);
	if ((x > 4.5) && (z > SQRT3))
	{
		x -= 4.5;
		z -= SQRT3_2;
	}
	if ((x > 5.0) && (z < SQRT3_2))
	{
		x -= 5;
		z += SQRT3;
	}
	if ((x > 2.5) && (z < SQRT3))
	{
		x -= 2.5;
		z += SQRT3_2;
	}
	delta = 0;
	zzof = z /SQRT3_2;
	if ( zzof == 2)
	{
		zzof = 1;
		z = 2 * SQRT3 - z;
		delta = 1 - delta;
	}
	if ( (!zzof) || (x > 2.0) ||
	     (( z + SQRT3*x < SQRT3) || ( SQRT3*x -z > SQRT3)) )
	{
		/* triangle */
		x -= 1.0 *floor(x/1.0);
		z -= SQRT3*floor(z/SQRT3);
		/* x,z is in { [0.0, 1.0 [, [0.0, SQRT3 [ } 
		 ** but there is some symmetry to simplify the testing
		 */
		if (z > SQRT3_2 )
		{
			z = SQRT3 - z; /* mirror */
			delta = 1 - delta;
		}
		if (x > 0.5)
		{
			x = 1.0 - x; /* mirror */
		}
		if (x != 0.0)
		{
			slop1 = z/x;
			if (slop1>SQRT3)
			{
				z = SQRT3_2 - z;
				x = 0.5 -x;
				delta = 1 - delta;
			}
		}
		else
		{
			z = SQRT3_2 -z;
			x = 0.5;
			delta = 1 - delta;
		}
		dist1 = 1.0 - (z * 2 * SQRT3 );
		dist2 = 1.0 - ((SQRT3 * x - z) * SQRT3 );
		return (delta+max(dist1,dist2))/3.0;
	}
	else
	{ /* hexagon */
		z -= SQRT3_2;
		if (x > 1.0)
		{
			x = 2.0 - x; /* mirror */
		}
		/* Hexagon */
		dist1 = 2 + 2* (z * SQRT3 );
		dist2 = 2 + 2* ((SQRT3 * x + z - SQRT3_2) ) * SQRT3_2 ;
		answer = 5.0-min(dist1,dist2);
		answer = max(answer, 2.000001); // TODO FIXME - magic number! Should use nextafter()
		answer /= 3.0;
		return answer;
	}
}

static DBL tiling_hexa_tri_left (const VECTOR EPoint)
{
	DBL x,z;
	DBL slop1;
	DBL dist1,dist2;
	DBL answer;
	int zzof;
	int delta;
	x=EPoint[X];
	z=EPoint[Z];
	/* First, resume to a simple pattern */
	zzof = z / SQRT3_2;
	zzof /= 3;
	if (z < 0)
	{
		zzof -= 1;
	}
	x -= zzof / 2.0; /* left handed */
	z -= 3*SQRT3_2*floor(z/(3*SQRT3_2));
	x += 7.0;
	x -= 7.0 *floor(x/7.0);
	if ((x > 2.0) && (z < SQRT3_2))
	{
		x -= 2.0;
		z += SQRT3;
	}
	if ((x > 4.5) && (z < SQRT3))
	{
		x -= 4.5;
		z += SQRT3_2;
	}
	if ((x > 2.5) && (z > SQRT3))
	{
		x -= 2.5;
		z -= SQRT3_2;
	}
	delta = 0;
	zzof = z /SQRT3_2;
	if ( zzof == 2)
	{
		zzof = 1;
		z = 2 * SQRT3 - z;
		delta = 1 - delta;
	}
	if ( (!zzof) || (x > 2.0) ||
	     (( z + SQRT3*x < SQRT3) || ( SQRT3*x -z > SQRT3)) )
	{
		/* triangle */
		x -= 1.0 *floor(x/1.0);
		z -= SQRT3*floor(z/SQRT3);
		/* x,z is in { [0.0, 1.0 [, [0.0, SQRT3 [ } 
		 ** but there is some symmetry to simplify the testing
		 */
		if (z > SQRT3_2 )
		{
			z = SQRT3 - z; /* mirror */
			delta = 1 - delta;
		}
		if (x > 0.5)
		{
			x = 1.0 - x; /* mirror */
		}
		if (x != 0.0)
		{
			slop1 = z/x;
			if (slop1>SQRT3)
			{
				z = SQRT3_2 - z;
				x = 0.5 -x;
				delta = 1 - delta;
			}
		}
		else
		{
			z = SQRT3_2 -z;
			x = 0.5;
			delta = 1 - delta;
		}
		dist1 = 1.0 - (z * 2 * SQRT3 );
		dist2 = 1.0 - ((SQRT3 * x - z) * SQRT3 );
		return (delta+max(dist1,dist2))/3.0;
	}
	else
	{ /* hexagon */
		z -= SQRT3_2;
		if (x > 1.0)
		{
			x = 2.0 - x; /* mirror */
		}
		/* Hexagon */
		dist1 = 2 + 2* (z * SQRT3 );
		dist2 = 2 + 2* ((SQRT3 * x + z - SQRT3_2) ) * SQRT3_2 ;
		answer = 5.0-min(dist1,dist2);
		answer = max(answer, 2.000001); // TODO FIXME - magic number! Should use nextafter()
		answer /= 3.0;
		return answer;
	}
}

static DBL tiling_square_tri (const VECTOR EPoint)
{
	DBL x,z;
	DBL slop1;
	DBL dist1,dist2;
	int delta;
	int gamma,beta;
	int xflop,zflop;
	x=EPoint[X];
	z=EPoint[Z];
	delta = 0;
	gamma = 0;
	beta = 0;
	x -= (1.0+SQRT3)*floor(x/(1.0+SQRT3));
	z -= (1.0+SQRT3)*floor(z/(1.0+SQRT3));
	/* x,z is in { [0.0, SQRT3+1.0 [, [0.0, SQRT3+1.0 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > 0.5 + SQRT3_2 )
	{
		z = 1.0 + SQRT3 - z; /* mirror */
		delta = 1 - delta;
	}
	if (x > 0.5 + SQRT3_2)
	{
		x = 1.0 + SQRT3 - x; /* mirror */
		delta = 1 - delta;
		beta = 1;
	}
	/* x,z is in { [0.0, (SQRT3+1)/2 ], [0.0, (SQRT3+1)/2 ] }
	 ** but there is still a symmetry and a rotation 
	 */
	xflop = ( 2.0 * x + (SQRT3 -1.0)* 2.0 * z / ( SQRT3 + 1.0) > (SQRT3)) ? 1: 0;
	zflop = ( 2.0 * z * SQRT3 + (SQRT3 -3.0)* 2.0 * x / ( SQRT3 + 1.0) > (SQRT3)) ? 1: 0;
	switch (xflop + 2*zflop)
	{
		case 0: /* do nothing */
			gamma = 2 * beta;
			break;
		case 1: /* rotate clockwise */
			gamma = beta ? 1 + 2 * delta : 3 - 2 *delta;
			slop1 = x;
			x = z;
			z = SQRT3_2 + 0.5 - slop1;
			break;
		case 2: /* rotate normal */
			gamma = beta ? 3 - 2 * delta : 1 + 2 *delta;
			slop1 = z;
			z = x;
			x = SQRT3_2 +0.5 - slop1;
			break;
		case 3: /* symmetry */
			gamma = beta ? 0: 2 ;
			x = SQRT3_2+0.5 -x;
			z = SQRT3_2+0.5 -z;
			break;
	}

	if (x == 0.0)
	{
		z = 0.0;
		x = 0.0;
	}
	slop1 = (z * 2 * SQRT3) - SQRT3 + x * 2;
	if (slop1 < 0)
	{
		/* triangle */

		dist1 = -1.5 / SQRT3 * slop1;
		dist2 = 2.0 * x * SQRT3;
		slop1 = min(dist1,dist2);
		return (gamma+1.0-slop1)/6.0;
	}
	else
	{
		/* square */
		slop1 *= 1.5 / SQRT3;
		slop1 = min(slop1,1.0);
		if (delta)
		{
			slop1 *= -1;
			slop1 += 6.0;
			slop1 /= 6.0;
			slop1 = max(slop1,5.000001/6.0); // TODO FIXME - magic number! Should use nextafter()
			slop1 = min(slop1,1.0);
		}
		else
		{
			slop1 *= -1;
			slop1 += 5.0;
			slop1 /= 6.0;
			slop1 = min(slop1,5.0/6.0);
			slop1 = max(slop1,4.000001/6.0); // TODO FIXME - magic number! Should use nextafter()
		}
		return slop1;
	}
}

static DBL tiling_dodeca_tri (const VECTOR EPoint)
{
	DBL x,z;
	DBL dist1,dist2,dist3,dist4,ret_value;
	DBL tmpx,tmpz;
	int toggle=0; /* switched each time a triangle get toggled */
	x=EPoint[X];
	z=EPoint[Z];
	x -= (2.0+SQRT3)*floor(x/(2.0+SQRT3));
	z -= (3.0+2.0*SQRT3)*floor(z/(3.0+2.0*SQRT3));
	/* x,z is in { [0.0, SQRT3+2.0 [, [0.0, 2*SQRT3+3.0 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > SQRT3+1.5)
	{
		/* translate */
		z -= SQRT3+1.5;
		x += (x<(1.0+SQRT3_2) ? 1.0: -1.0)*(1.0+SQRT3_2);
	}
	if (x > 1.0+SQRT3_2)
	{
		x = 2.0 + SQRT3 -x;
	}
	if (z > 1.0+SQRT3_2)
	{
		z = 2.0 + SQRT3 -z;
		toggle = 1 - toggle;
	}
	dist2 = x - SQRT3_2 - 0.5 + z * SQRT3;
	if (dist2 < 0.0)
	{
		tmpx = x;
		tmpz = z;
		x = (1.0+SQRT3)/4.0 + 0.5 * tmpx - SQRT3_2 * tmpz ;
		z = (3.0+SQRT3)/4.0 - SQRT3_2 * tmpx - 0.5 * tmpz ;
		dist2 *= -1.0;
	}
	dist1 = (z * 3.0 ); /* from the bottom line */
	dist3 =  z - SQRT3_2 - 0.5 + x * SQRT3;
	dist3 *= SQRT3;
	dist2 *= SQRT3;
	dist4 = (x * 3.0 ); /* from the vertical line */
	if (dist3 < 0.0)
	{
		ret_value = max(dist3,-1.0);
		ret_value += 1+toggle;
		ret_value /= 3.0;
	}
	else
	{
		/* dodecagon */
		ret_value = min(dist1,dist2);
		ret_value = min(ret_value,dist3);
		ret_value = min(ret_value,dist4);
		ret_value = min(ret_value,1.0);
		ret_value = 3.0000001 - ret_value; // TODO FIXME - magic number! Should use nextafter()

		ret_value /= 3.0;
	}
	return ret_value;
}

static DBL tiling_dodeca_hex (const VECTOR EPoint)
{
	DBL x,z;
	DBL dist1,dist2,dist3,dist4,ret_value;
	DBL dist5,dist6,dist7;
	DBL tmpx,tmpz;
	x=EPoint[X];
	z=EPoint[Z];
	x -= (3.0+SQRT3)*floor(x/(3.0+SQRT3));
	z -= (3.0+3.0*SQRT3)*floor(z/(3.0+3.0*SQRT3));
	/* x,z is in { [0.0, SQRT3+3.0 [, [0.0, 3*SQRT3+3.0 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > 1.5*SQRT3+1.5)
	{
		/* translate */
		z -= 1.5*SQRT3+1.5;
		x += (x<(1.5+SQRT3_2) ? 1.0: -1.0)*(1.5+SQRT3_2);
	}
	if (x > 1.5+SQRT3_2)
	{
		x = 3.0 + SQRT3 -x;
	}
	if (z > 1.0+SQRT3)
	{
		z = 2.0 + 2.0*SQRT3 -z;
	}
	dist2 = x - SQRT3_2 - 1.5 + z * SQRT3;
	if (dist2 < 0.0)
	{
		tmpx = x;
		tmpz = z;
		x = (3.0+SQRT3)/4.0 + 0.5 * tmpx - SQRT3_2 * tmpz ;
		z = (3.0+3.0*SQRT3)/4.0 - SQRT3_2 * tmpx - 0.5 * tmpz ;
	}
	dist2 = x - SQRT3_2 - 2.5 + z * SQRT3;
	dist1 = (z * 2.0 ) - SQRT3; /* from the bottom line */
	dist3 = z - 1.5*SQRT3 - 0.5 + x * SQRT3;
	dist4 = ((x-0.5) * 2.0 ); /* from the vertical line */
	if ( (dist2 >= 0.0) &&
	     (dist1 >= 0.0) &&
	     (dist3 >= 0.0) &&
	     (dist4 >= 0.0) )
	{
		/* dodecagon */
		ret_value = min(dist1,dist2);
		ret_value = min(ret_value,dist3);
		ret_value = min(ret_value,dist4);
		ret_value = min(ret_value,1.0);
		ret_value = 3.000001 - ret_value; // TODO FIXME - magic number! Should use nextafter()
	}
	else
	{
		dist5 = 2*z - 2*SQRT3 - 1.0;
		if (dist5 >= 0)
		{
			dist4 *= -1.0;
			ret_value = min(dist5,dist4);
			ret_value = 2.0 - ret_value;
		}
		else
		{
			dist6 = SQRT3 * x - z -SQRT3_2 +0.5;
			dist7 = dist6 - 2.0;
			switch((dist6 >= 0?0:1)+(dist7 >= 0.0 ?2:0))
			{
				case 1: /* left hexagon */
					dist5 *= -1.0;
					dist6 *= -1.0;
					dist3 *= -1.0;
					ret_value = min(dist6,dist3);
					ret_value = min(ret_value,dist5);
					ret_value = min(ret_value,1.0);
					ret_value = 1.0 - ret_value;
					break;
				case 2: /* bottom hexagon */
					dist1 *= -1.0;
					ret_value = min(dist7,dist1);
					ret_value = min(ret_value,1.0);
					ret_value = 1.0 - ret_value;
					break;
				case 0:
				default: /* slanted square */
					dist2 *= -1.0;
					dist7 *= -1.0;
					ret_value = min(dist6,dist2);
					ret_value = min(ret_value,dist7);
					ret_value = 2.0 - ret_value;
					break;
			}
		}
	}
	ret_value /= 3.0;
	return ret_value;
}

static DBL tiling_dodeca_hex_5 (const VECTOR EPoint)
{
	DBL x,z;
	DBL dist1,dist2,dist3,dist4,ret_value;
	DBL dist5,dist6,dist7;
	DBL tmpx,tmpz;
	int mirX,mirZ,rota;
	mirX = mirZ = rota = 0;
	x=EPoint[X];
	z=EPoint[Z];
	x -= (3.0+SQRT3)*floor(x/(3.0+SQRT3));
	z -= (3.0+3.0*SQRT3)*floor(z/(3.0+3.0*SQRT3));
	/* x,z is in { [0.0, SQRT3+3.0 [, [0.0, 3*SQRT3+3.0 [ } 
	 ** but there is some symmetry to simplify the testing
	 */
	if (z > 1.5*SQRT3+1.5)
	{
		/* translate */
		z -= 1.5*SQRT3+1.5;
		x += (x<(1.5+SQRT3_2) ? 1.0: -1.0)*(1.5+SQRT3_2);
	}
	if (x > 1.5+SQRT3_2)
	{
		x = 3.0 + SQRT3 -x;
		mirX = 1 - mirX;
	}
	if (z > 1.0+SQRT3)
	{
		z = 2.0 + 2.0*SQRT3 -z;
		mirZ = 1 - mirZ;
	}
	dist2 = x - SQRT3_2 - 1.5 + z * SQRT3;
	if (dist2 < 0.0)
	{
		tmpx = x;
		tmpz = z;
		x = (3.0+SQRT3)/4.0 + 0.5 * tmpx - SQRT3_2 * tmpz ;
		z = (3.0+3.0*SQRT3)/4.0 - SQRT3_2 * tmpx - 0.5 * tmpz ;
	}
	dist2 = x - SQRT3_2 - 2.5 + z * SQRT3;
	dist1 = (z * 2.0 ) - SQRT3; /* from the bottom line */
	dist3 = z - 1.5*SQRT3 - 0.5 + x * SQRT3;
	dist4 = ((x-0.5) * 2.0 ); /* from the vertical line */
	if ( (dist2 >= 0.0) &&
	     (dist1 >= 0.0) &&
	     (dist3 >= 0.0) &&
	     (dist4 >= 0.0) )
	{
		/* dodecagon */
		ret_value = min(dist1,dist2);
		ret_value = min(ret_value,dist3);
		ret_value = min(ret_value,dist4);
		ret_value = min(ret_value,1.0);
		ret_value = 5.000001 - ret_value; // TODO FIXME - magic number! Should use nextafter()
	}
	else
	{
		dist5 = 2*z - 2*SQRT3 - 1.0;
		if (dist5 >= 0)
		{
			dist4 *= -1.0;
			ret_value = min(dist5,dist4);
			ret_value = 2.0 - ret_value;
		}
		else
		{
			dist6 = SQRT3 * x - z -SQRT3_2 +0.5;
			dist7 = dist6 - 2.0;
			switch((dist6 >= 0?0:1)+(dist7 >= 0.0 ?2:0))
			{
				case 1: /* left hexagon */
					dist5 *= -1.0;
					dist6 *= -1.0;
					dist3 *= -1.0;
					ret_value = min(dist6,dist3);
					ret_value = min(ret_value,dist5);
					ret_value = min(ret_value,1.0);
					ret_value = 1.0 - ret_value;
					break;
				case 2: /* bottom hexagon */
					dist1 *= -1.0;
					ret_value = min(dist7,dist1);
					ret_value = min(ret_value,1.0);
					ret_value = 1.0 - ret_value;
					break;
				case 0:
				default: /* slanted square */
					dist2 *= -1.0;
					dist7 *= -1.0;
					ret_value = min(dist6,dist2);
					ret_value = min(ret_value,dist7);
					ret_value = (mirZ + mirX) %2 + 3.0 - ret_value;
					break;
			}
		}
	}
	ret_value /= 5.0;
	return ret_value;
}

static DBL tiling_penrose_halfkite (DBL pX, DBL pZ, int depth, bool rhombs);
static DBL tiling_penrose_halfdart (DBL pX, DBL pZ, int depth, bool rhombs);

static DBL tiling_penrose_halfkite (DBL pX, DBL pZ, int depth, bool rhombs)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (z < (x - INVPHI) * TAN144)
		{
			return tiling_penrose_halfdart(x*PHI, z*PHI, depth-1, rhombs);
		}
		else
		{
			x -= COS36;
			z -= SIN36;
			DBL rotX = x*COS108 - z*SIN108;
			DBL rotZ = z*COS108 + x*SIN108;
			return tiling_penrose_halfkite(rotX*PHI, rotZ*PHI, depth-1, rhombs);
		}
	}
	else if (rhombs)
	{
		if (z < (x - INVPHI) * TAN72)
		{
			DBL dist1 = abs( SIN72  * (x-INVPHI) - COS72  * z ) * 5.55; // TODO FIXME - the factor is just an empiric value
			DBL dist2 = abs( SIN108 * (x-1)      - COS108 * z ) * 5.55;
			return max3(1.0-dist1/2,1.0-dist2/2,0.5+TILING_EPSILON);
		}
		else
		{
			DBL dist1 = abs( z )                              * 5.55; // TODO FIXME - the factor is just an empiric value
			DBL dist2 = abs( SIN72 * (x-INVPHI) - COS72 * z ) * 5.55;
			return min(max3(0.5-dist1/2,0.5-dist2/2,0.0),0.5-TILING_EPSILON);
		}
	}
	else
	{
		DBL dist1 = abs( SIN36  *  x    - COS36  * z ) * 4.46; // TODO FIXME - the factor is just an empiric value
		DBL dist2 = abs( SIN108 * (x-1) - COS108 * z ) * 4.46;
		return min(max3(0.5-dist1/2,0.5-dist2/2,0.0),0.5-TILING_EPSILON);
	}
}

static DBL tiling_penrose_halfdart (DBL pX, DBL pZ, int depth, bool rhombs)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (z < (x - INVPHI) * TAN108)
		{
			DBL rotX = x*COS36 + z*SIN36;
			DBL rotZ = z*COS36 - x*SIN36;
			return tiling_penrose_halfkite(rotX*PHI, rotZ*PHI, depth-1, rhombs);
		}
		else
		{
			x -= 1;
			DBL rotX = x*COS144 + z*SIN144;
			DBL rotZ = z*COS144 - x*SIN144;
			return tiling_penrose_halfdart(rotX*PHI, rotZ*PHI, depth-1, rhombs);
		}
	}
	else if (rhombs)
	{
		DBL dist1 = abs( SIN36  *  x    - COS36  * z ) * 5.55; // TODO FIXME - the factor is just an empiric value
		DBL dist2 = abs( SIN144 * (x-1) - COS144 * z ) * 5.55;
		return min(max3(0.5-dist1/2,0.5-dist2/2,0.0),0.5-TILING_EPSILON);
	}
	else
	{
		DBL dist1 = abs( z )                           * 4.46; // TODO FIXME - the factor is just an empiric value
		DBL dist2 = abs( SIN144 * (x-1) - COS144 * z ) * 4.46;
		return max3(1.0-dist1/2,1.0-dist2/2,0.5+TILING_EPSILON);
	}
}

static DBL tiling_penrose (const VECTOR EPoint, bool rhombs, bool centerFlag)
{
	// Penrose tiling
	// rhombs=false: P2 ("kite and dart") Penrose tiling
	//   centerFlag=false: 5 darts at center forming a star
	//   centerFlag=true:  5 kites at center forming a regular decagon ("sun")
	// rhombs=true:  P3 ("rhombus") Penrose tiling
	//   centerFlag=false: 5 wide rhombs at center, surrounded by 5 narrow rhombs to form a regular decagon
	//   centerFlag=true:  5 wide rhombs at center, surrounded by 10 narrow rhombs to form a pointed star

	DBL x,z;
	x = EPoint[X];
	z = EPoint[Z];

	DBL r = sqrt(x*x+z*z);
	if (r <= EPSILON)
		return 1.0;

	// exploit trivial mirror symmetry
	z = abs(z);

	// exploit rotational & mirror symmetry
	if (x < r * COS36)
	{
		DBL rotSin;
		DBL rotCos;
		if (x < r *COS108)
		{
			rotSin = SIN144;
			rotCos = COS144;
		}
		else
		{
			rotSin = SIN72;
			rotCos = COS72;
		}
		DBL rotX = x*rotCos + z*rotSin;
		DBL rotZ = z*rotCos - x*rotSin;

		x =     rotX;
		z = abs(rotZ);
	}

	if (rhombs)
	{
		x *= INVPHI;
		z *= INVPHI;
	}

	DBL dist = abs( SIN108 * x - COS108 * z ) / COS18;

	int depth = max(0, (int)ceil(log(dist)/log(SQRPHI)));

	x *= pow(INVSQRPHI,depth);
	z *= pow(INVSQRPHI,depth);

	if (depth % 2)
	{
		DBL rotX = x*COS36 + z*SIN36;
		DBL rotZ = z*COS36 - x*SIN36;
		x = rotX;
		z = abs(rotZ);
	}

	depth *= 2;

	if (centerFlag)
	{
		depth += 1;
		x *= INVPHI;
		z *= INVPHI;
	}

	return tiling_penrose_halfkite(x, z, depth, rhombs);
}

static DBL tiling_penrose1_pentagon1 (DBL pX, DBL pZ, int depth);
static DBL tiling_penrose1_pentagon2 (DBL pX, DBL pZ, int depth, bool insideQuad);
static DBL tiling_penrose1_pentagon3 (DBL pX, DBL pZ, int depth, bool insideWedge);
static DBL tiling_penrose1_star (DBL pX, DBL pZ, int depth);
static DBL tiling_penrose1_boat (DBL pX, DBL pZ, int depth, bool insideWedge);
static DBL tiling_penrose1_diamond (DBL pX, DBL pZ, int depth, bool sideA);

static void tiling_penrose1_pentagon_symmetry(DBL& x, DBL& z, DBL r)
{
	z = abs(z);

	if (x < r * COS36)
	{
		DBL rotSin;
		DBL rotCos;
		if (x < r *COS108)
		{
			rotSin = SIN144;
			rotCos = COS144;
		}
		else
		{
			rotSin = SIN72;
			rotCos = COS72;
		}
		DBL rotX = x*rotCos + z*rotSin;
		DBL rotZ = z*rotCos - x*rotSin;

		x =     rotX;
		z = abs(rotZ);
	}
}

static void tiling_penrose1_pentagon_symmetry(DBL& x, DBL& z)
{
	tiling_penrose1_pentagon_symmetry (x, z, sqrt(x*x+z*z));
}

static DBL tiling_penrose1_pentagon_dist (DBL pX, DBL pZ)
{
	return abs( pX - 0.5/TAN36 ) * 5.55 * INVPHI; // TODO FIXME - the factor is just an empiric value
}

static DBL tiling_penrose1_pentagon1 (DBL pX, DBL pZ, int depth)
{
	DBL x = pX;
	DBL z = abs(pZ);

	tiling_penrose1_pentagon_symmetry (x, z);

	if (depth > 0)
	{
		if (z < (x - 0.5/TAN36) * TAN54 + 0.5)
		{
			DBL rotX = x - 0.5/TAN36 - INVPHI*0.5*COS72/SIN36;
			DBL rotZ = z;
			return tiling_penrose1_pentagon2 (rotX*PHI, rotZ*PHI, depth-1, false);
		}
		else
		{
			DBL rotX = x*COS36 + z*SIN36;
			DBL rotZ = z*COS36 - x*SIN36;
			return tiling_penrose1_star (rotX*PHI, rotZ*PHI, depth-1);
		}
	}
	else
	{
		DBL dist = tiling_penrose1_pentagon_dist (x, z);
		return min(max(1.0/6-dist/6,0.0),1.0/6-TILING_EPSILON);
	}
}

static DBL tiling_penrose1_pentagon2 (DBL pX, DBL pZ, int depth, bool insideQuad)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (insideQuad)
		{
			if (z < (x - INVSQRPHI*0.5/SIN36) * TAN54)
			{
				DBL rotX = x - 0.5/SIN36;
				DBL rotZ = z;
				return tiling_penrose1_pentagon1 (rotX*PHI, rotZ*PHI, depth-1);
			}
			else if (z < (x - INVSQRPHI*0.5/SIN36) * TAN162)
			{
				DBL rotX = -x;
				DBL rotZ =  z;
				return tiling_penrose1_diamond (rotX*PHI, rotZ*PHI, depth-1, true);
			}
			else
			{
				DBL rotX = x*COS108 - z*SIN108 + INVPHI*0.5/SIN36;
				DBL rotZ = z*COS108 + x*SIN108;
				return tiling_penrose1_pentagon2 (rotX*PHI, rotZ*PHI, depth-1, true);
			}
		}
		else if (z < (x + 0.5/SIN36) * TAN18)
		{
			DBL rotX = x + 0.5/SIN36 - INVSQRPHI*0.5/SIN36;
			DBL rotZ = z;
			return tiling_penrose1_diamond (rotX*PHI, rotZ*PHI, depth-1, false);
		}
		else
		{
			DBL rotX = - (x*COS36 + z*SIN36) - COS72*0.5/SIN36;
			DBL rotZ =   (z*COS36 - x*SIN36) - SIN72*0.5/SIN36;
			return tiling_penrose1_pentagon3 (rotX*PHI, rotZ*PHI, depth-1, false);
		}
	}
	else
	{
		tiling_penrose1_pentagon_symmetry (x, z);
		DBL dist = tiling_penrose1_pentagon_dist (x, z);
		return min(max(2.0/6-dist/6,1.0/6+TILING_EPSILON),2.0/6-TILING_EPSILON);
	}
}

static DBL tiling_penrose1_pentagon3 (DBL pX, DBL pZ, int depth, bool insideWedge)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (insideWedge && (x > INVSQRPHI*0.5*COS72/SIN36))
		{
			DBL rotX = -(x - INVSQRPHI*0.5*COS72/SIN36 - INVPHI*0.5/TAN36);
			DBL rotZ =   z;
			return tiling_penrose1_pentagon2 (rotX*PHI, rotZ*PHI, depth-1, true);
			return 1.0/6 + TILING_EPSILON;
		}
		else if (!insideWedge && (x < 0.5*COS108/SIN36))
		{
			DBL rotX = x*COS144 + z*SIN144 - 0.5/SIN36;
			DBL rotZ = z*COS144 - x*SIN144;
			return tiling_penrose1_pentagon2 (rotX*PHI, rotZ*PHI, depth-1, false);
		}
		else if (!insideWedge && (z > (x - INVSQRPHI*0.5/SIN36)*TAN126))
		{
			DBL rotX = - (x*COS36 - z*SIN36) - COS72*0.5/SIN36;
			DBL rotZ =   (z*COS36 + x*SIN36) - SIN72*0.5/SIN36;
			return tiling_penrose1_pentagon3 (rotX*PHI, rotZ*PHI, depth-1, false);
		}
		else
		{
			return tiling_penrose1_boat (x*PHI, z*PHI, depth-1, insideWedge);
		}
	}
	else
	{
		tiling_penrose1_pentagon_symmetry (x, z);
		DBL dist = tiling_penrose1_pentagon_dist (x, z);
		return min(max(3.0/6-dist/6,2.0/6+TILING_EPSILON),3.0/6-TILING_EPSILON);
	}
}

static DBL tiling_penrose1_star (DBL pX, DBL pZ, int depth)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (x < INVPHI * 0.5/TAN36)
		{
			return tiling_penrose1_pentagon1(x*PHI, z*PHI, depth-1);
		}
		else
		{
			DBL rotX = -(x - INVPHI/TAN36);
			DBL rotZ =   z;
			return tiling_penrose1_pentagon3(rotX*PHI, rotZ*PHI, depth-1, true);
		}
	}
	else
	{
		DBL dist = abs( SIN162 * (x - PHI*0.5/SIN36) - COS162 * z) * 5.55 * INVPHI; // TODO FIXME - the factor is just an empiric value
		return min(max(4.0/6-dist/6,3.0/6+TILING_EPSILON),4.0/6-TILING_EPSILON);
	}
}

static DBL tiling_penrose1_boat (DBL pX, DBL pZ, int depth, bool insideWedge)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (insideWedge && (x > PHI*0.5*COS108/SIN36))
		{
			DBL rotX = -x;
			DBL rotZ =  z;
			return tiling_penrose1_pentagon1 (rotX*PHI, rotZ*PHI, depth-1);
		}
		else
		{
			DBL rotX, rotZ;
			if (insideWedge)
			{
				rotX = x;
				rotZ = z;
			}
			else
			{
				rotX = x*COS72 - z*SIN72;
				rotZ = z*COS72 + x*SIN72;
			}
			rotX += 0.5/SIN36;
			return tiling_penrose1_pentagon3 (rotX*PHI, rotZ*PHI, depth-1, true);
		}
	}
	else
	{
		DBL dist1 = abs( x - INVPHI*0.5*COS72/SIN36) * 5.55 * INVPHI; // TODO FIXME - the factor is just an empiric value
		x = -x;
		tiling_penrose1_pentagon_symmetry (x, z);
		DBL dist2 = abs( SIN162 * (x - PHI*0.5/SIN36) - COS162 * z) * 5.55 * INVPHI; // TODO FIXME - the factor is just an empiric value
		return min(max3(5.0/6-dist1/6,5.0/6-dist2/6,4.0/6+TILING_EPSILON),5.0/6-TILING_EPSILON);
	}
}

static DBL tiling_penrose1_diamond (DBL pX, DBL pZ, int depth, bool sideA)
{
	DBL x = pX;
	DBL z = abs(pZ);

	if (depth > 0)
	{
		if (sideA)
		{
			return tiling_penrose1_pentagon1(x*PHI, z*PHI, depth-1);
		}
		else
		{
			return tiling_penrose1_pentagon3(x*PHI, z*PHI, depth-1, true);
		}
	}
	else
	{
		DBL dist = abs( SIN18 * (x + INVPHI*0.5/SIN36) - COS18 * z) * 5.55 * INVPHI; // TODO FIXME - the factor is just an empiric value
		return min(max(6.0/6-dist/6,5.0/6+TILING_EPSILON),6.0/6-TILING_EPSILON);
	}
}

static DBL tiling_penrose1 (const VECTOR EPoint, bool centerFlag)
{
	// Penrose P1 ("pentagon, star, boat and diamond") tiling
	//   centerFlag=false: pentagon at center
	//   centerFlag=true:  pentagram (star) at center

	DBL x,z;
	x = EPoint[X];
	z = EPoint[Z];

	DBL r = sqrt(x*x+z*z);
	if (r <= EPSILON)
		return 1.0;

	tiling_penrose1_pentagon_symmetry (x, z, r);

	DBL dist = x * 2 * TAN36;

	int depth = max(0, (int)ceil(log(dist)/log(SQRPHI*SQRPHI)));

	x *= pow(INVSQRPHI*INVSQRPHI,depth);
	z *= pow(INVSQRPHI*INVSQRPHI,depth);

	depth *= 4;

	/*
	if (centerFlag)
	{
		depth += 1;
		x *= INVPHI;
		z *= INVPHI;
	}
	*/

	return tiling_penrose1_pentagon1(x, z, depth);
}

static unsigned char digon[]={ 0x0E, 0x0B };
static unsigned char trigon[][6]=
{
	{ 0x0D, 0x05, 0x07 , 0x0D, 5,7 },
	{ 0x0E, 0x0D, 0x16, 0x49, 7, 0x0B }
};

static unsigned char tetragon[][16]=
{
	{ 0x0D, 5, 5, 7, 0x0D, 5, 5, 7, 0x0D, 5, 5, 7, 0x0D, 5, 5, 7 },
	{ 0x0E, 0x0D, 5, 0x16, 0x49, 5, 7, 0x0B, 0x0E, 0x0D, 5, 0x16, 0x49, 5, 7, 0x0B },
	{ 0x0D, 0x34, 0x7, 0x0B, 0x07, 0x0B, 0x0D, 0x34, 0x0D, 0x34, 0x7, 0x0B, 0x07, 0x0B, 0x0D, 0x34},
	{ 0x0C, 0x06, 0x0C, 0x06, 0x09, 0x03, 0x09, 0x03, 0x0C, 0x06, 0x0C, 0x06, 0x09, 0x03, 0x09, 0x03 },
	{ 7, 0x2C, 7, 0x2C, 0x0D, 0x83, 0x0D, 0x83, 0x2C, 7, 0x2C,7, 0x83, 0x0D, 0x83, 0x0D  }
};

static unsigned char pentagon[][10]=
{
	{ 0x0D, 5,5,5,7 , 0x0D, 5,5,5,7},
	{ 0x0E, 0x0D,5,5,0x16, 0x49, 5,5,7,0x0B},
	{ 7, 0x0C, 6, 0x0C, 0x24, 0x0D, 0x81, 3, 9, 3 },
	{ 0x0D, 5, 0x34, 7, 0x0B, 15,15,15,15,15 },
	{ 0x0D, 0xF0, 7, 0x0B, 0x0E , 15,15,15,15,15 },
	{ 0x0E, 0x0A, 0x49, 5, 7, 0x0D, 5, 0x16, 0x0A, 0x0B },
	{ 0x0B, 0x0E, 0x49, 0x16, 0x49, 7, 0x0D, 0x16, 0x49, 0x16 },
	{ 0x2C, 5, 0x16, 0x0B, 0x0E, 0x0B, 0x0E, 0x49, 5, 0x83 },
	{ 0x0E, 0x49, 5, 0x16, 0x0B, 0x0E, 0x49, 5, 0x16, 0x0B },
	{ 0x0E, 0x0D, 0xC1, 0x16, 0x0B, 0x49, 0x34, 7, 0x0B, 0x0E },
	{ 7, 0x2C, 5, 7, 0x2C, 0x0D, 0x83, 0x0D, 5, 0x83 },
	{ 0x0D, 0x34, 7, 0x0D, 0xC1, 7, 0x0A, 0x0B, 0x0E, 0x0A}
};

static unsigned char hexagon[][12]=
{
	{ 0x0D, 5, 5, 5, 5, 7, 0x0D, 5, 5, 5, 5, 7},
	{ 0x2C, 5, 5, 5, 7, 0x0E, 0x0B, 0x0D, 5, 5, 5, 0x83 },
	{ 0x0D, 0x34, 5, 5, 7, 0x0E, 7, 0x0B, 0x0D, 5, 5, 0xC1 },
	{ 0x0D, 5, 0x34, 5, 7, 0x0E, 5, 7, 0x0B, 0x0D, 5, 0xC1 },
	{ 0x0D, 0x83, 0x2C, 5, 5, 7 , 15,15,15,15,15,15},

	{ 0x0C, 0x24, 5, 7, 0x0C, 6, 9, 3, 0x0D, 5, 0x81, 3 },
	{ 7, 0x0C, 6, 0x0D, 0x14, 0x24, 0x0D, 0x81, 0x41, 7, 9, 3},
	{ 0x0D, 5, 0x83, 0x2C, 5, 7, 15,15,15,15,15,15},
	{ 7, 0x0C, 0x24, 7, 0x0C, 0x24, 0x0D, 0x81, 3, 0x0D, 0x81, 3},
	{ 0x0C, 4, 6, 0x0C, 4, 6, 9, 1, 3, 9, 1, 3 },

	{ 0x0E, 0x0D, 0x14, 6, 0x48, 6, 9, 0x12, 9, 0x41, 7, 0x0B},
	{ 0x0D, 5, 0x16, 0x49, 0x16, 0x0B, 15,15,15,15,15,15},
	{ 0x0D, 0x34, 5, 0x16, 0x0E, 0x0B, 0x0E, 0x0B, 0x49, 5, 0xC1, 7},
	{ 0x0D, 0x34, 7, 0x16, 0x0A, 0x0D, 0x0A, 0x49, 7, 0xC1, 7, 0x0D},
	{ 0x0D, 0x14, 0x82, 0x0E, 9, 3, 15,15,15,15,15,15},

	{ 0x0E, 0x0D, 0xD0, 6, 9, 3, 15,15,15,15,15,15},
	{ 0x0D, 0xF0, 5, 7, 0x0E, 0x0B, 0x0E, 0x0B, 0x0D, 5, 0xF0, 7},
	{ 0x0E, 0x0B, 0x0D, 5, 5, 0x92, 0x68, 5, 5, 7, 0x0E, 0x0B},
	{ 0x0E, 0x49, 5, 0x16, 0x0A, 0x0B, 15,15,15,15,15,15},
	{ 0x0B, 0x2C, 5, 5, 0x83, 0x0E, 15,15,15,15,15,15},

	{ 0x0B, 0x0A, 0x2C, 5, 5, 7, 15,15,15,15,15,15},
	{ 0x2C, 5, 5, 0x16, 0x0B, 0x0E, 0x0E, 0x0B, 5, 0x83, 0x49, 5},
	{ 0x0A, 0x0D, 0xC1, 0x16, 0x49, 0x34, 7, 0x0A, 0x0E, 0x0B, 0x0E, 0x0B},
	{ 0x0D, 0x16, 0x0D, 5, 0x92, 0x0B, 0x0E, 0x68, 5, 7, 0x49, 7},
	{ 0x0D, 0x16, 0x0D, 0x16, 0x16, 0x49, 0x16, 0x49, 0x49, 7, 0x49, 7},

	{ 0x0C, 6, 0x0C, 6, 9, 0x12, 9, 0x12, 7, 0x49, 7, 0x49 },
	{ 0x2C, 7, 0x2C, 7, 0x92, 0x0D, 0x92, 0x0D, 0x49, 7, 0x49, 7},
	{ 0x0E, 0x49, 5, 0x16, 0x49, 7, 15,15,15,15,15,15},
	{ 0x0D, 0x16, 0x0B, 0x0E, 0x49, 7, 0x16, 0x49, 5, 0x83, 0x2C, 5},
	{ 7, 0x0E, 0x49, 0x34, 7, 0x49, 0x0D, 0xC1, 0x16, 0x0B, 0x0D, 0x16},

	{ 0x2C, 5, 0xC1, 7, 0x0E, 0x0B, 0x0B, 0x0D, 0x34, 5, 0x83, 0x0E },
	{ 0x0E, 0x0B, 0x0D, 0x34, 0xC1, 7, 15,15,15,15,15,15},
	{ 0x0D, 5, 0x16, 0x2C, 7, 0x0A, 0x0A, 0x0D, 0x83, 0x49, 5, 7 },
	{ 0x2C, 0xC1, 5, 7, 0x0E, 0x0B, 0x0E, 0x0B, 0x0D, 5, 0x34, 0x83 },
	{ 0x0A, 0x0D, 5, 0x92, 0x68, 5, 7, 0x0A, 0x0B, 0x0E, 0x0E, 0x0B}
};

static DBL tetragonal (const VECTOR EPoint, const TPATTERN *TPat)
{
	unsigned char how;
	long xv,zv;
	DBL return_value=0;
	DBL value;
	DBL value1;
	DBL value2;
	DBL x,y;
	int lng=0;
	zv = floor(y=EPoint[Z]);
	xv = floor(x=EPoint[X]);
	switch(TPat->Vals.Pavement.Tile)
	{
		case 6:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
				case 1:
				case 2:
				case 3:
				case 5:
				case 6:
				case 8:
				case 9:
					xv %= 6; if (xv < 0) { xv += 6 ;}
					zv &= 0x01;
					lng = 6;
					break;
				case 4:
				case 7:
				case 19:
				case 20:
					lng = 0;
					zv %= 6; if (zv <0) { zv += 6;}
					xv += 5*zv;
					xv %= 6; if (xv <0) { xv += 6; }
					break;
				case 11:
				case 18:
				case 27:
					lng = 0;
					zv %= 6; if (zv <0) { zv += 6;}
					xv += zv;
					xv %= 6; if (xv <0) { xv += 6; }
					break;
				case 10:
				case 12:
				case 21:
				case 22:
				case 24:
				case 25:
				case 26:
					lng = 4;
					xv &= 0x03;
					zv %= 3; if (zv<0) { zv += 3;}
					break;
				case 13:
				case 32:
					lng = 3;
					zv &= 0x03;
					xv %= 3; if (xv < 0) { xv += 3; }
					break;
				case 14:
					lng = 3;
					zv %= 6; if (zv < 0) { zv += 6; }
					xv += 2 * (zv/2);
					zv &= 0x01;
					xv %= 3; if (xv < 0) { xv += 3; }
					break;
				case 15:
					lng = 2;
					xv %= 6; if (xv < 0) { xv+= 6; }
					zv += (xv/2);
					xv &= 0x01;
					zv %= 3; if (zv<0) { zv += 3;}
					break;
				case 16:
				case 17:
					lng = 6;
					zv %= 12; if (zv <0) { zv+=12; }
					xv += zv/2;
					zv &= 0x01;
					xv %= 6; if (xv < 0) { xv+= 6; }
					break;
				case 23:
				case 28:
					lng = 6;
					zv %= 12; if (zv <0) { zv+=12; }
					xv += 4* (zv/2);
					zv &= 0x01;
					xv %= 6; if (xv < 0) { xv+= 6; }
					break;
				case 29:
				case 30:
					lng = 6;
					zv &= 0x03;
					xv += 3* (zv/2);
					zv &= 0x01;
					xv %= 6; if (xv < 0) { xv+= 6; }
					break;
				case 31:
					lng = 0;
					zv %= 3; if (zv <0) { zv+=3; }
					xv += 4* zv;
					xv %= 6; if (xv < 0) { xv+= 6; }
					break;
				case 33:
					lng = 0;
					zv %= 12; if (zv < 0) { zv+= 12; }
					xv += 7*zv;
					xv %= 12; if (xv < 0) { xv+= 12; }
					break;
				case 34:
					lng = 4;
					zv %= 6; if (zv<0) { zv+=6;}
					xv += 2 * (zv/3);
					xv &= 0x03;
					zv %= 3; if (zv<0) { zv += 3;}
					break;
			}
			how = hexagon[TPat->Vals.Pavement.Number-1][xv+zv*lng];
			break;
		case 5:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
				case 1:
					xv %= 5; if (xv <0) { xv += 5 ; }
					zv &= 0x01;
					break;
				case 2:
				case 9:
					zv %= 10; if (zv <0) { zv += 10;}
					xv += 3 * (zv/2);
					xv %= 5; if (xv <0) { xv += 5;  }
					zv &= 0x01;
					break;
				case 10:
					zv %= 10; if (zv <0) { zv += 10;}
					xv += 4*(zv/2);
					xv %= 5; if (xv <0) { xv += 5 ;  }
					zv &= 0x01;
					break;
				case 3:
					zv %= 5; if (zv <0) { zv += 5;}
					xv += 2*zv;
					xv %= 5; if (xv <0) { xv += 5 ;  }
					zv = 0x0;
					break;
				case 4:
					zv %= 5; if (zv <0) { zv += 5;}
					xv += 2 * zv;
					xv %= 5; if (xv <0) { xv += 5;  }
					zv = 0x00;
					break;
				case 5:
				case 6:
				case 8:
					zv %= 10; if (zv <0) { zv += 10;}
					xv += zv;
					xv %= 10; if (xv <0) { xv += 10;  }
					zv = 0x00;
					break;
				case 11:
					zv %= 10; if (zv <0) { zv += 10;}
					xv += 8* zv;
					xv %= 10; if (xv <0) { xv += 10;  }
					zv = 0x00;
					break;
				case 7:
					zv %= 10; if (zv <0) { zv += 10;}
					xv += 3*zv;
					xv %= 10; if (xv <0) { xv += 10;  }
					zv = 0x00;
					break;
			}
			how = pentagon[TPat->Vals.Pavement.Number-1][xv+zv*5];
			break;
		case 4:
			xv &= 0x03;
			zv &= 0x03;
			how = tetragon[TPat->Vals.Pavement.Number-1][xv+zv*4];
			break;
		case 3:
			xv %= 3; if (xv < 0) { xv += 3; }
			zv &= 0x01;
			how = trigon[TPat->Vals.Pavement.Number-1][xv+zv*3];
			break;
		case 2:
			zv &= 0x01;
			how = digon[zv];
			break;
		case 1:
		default:
			how = 0x0F;
			break;
	}
	/* 
	 **   5---1---6
	 **   |       |
	 **   4       2
	 **   |       |
	 **   8---3---7
	 */
	x -= floor(x);
	y -= floor(y);
	switch(TPat->Vals.Pavement.Form)
	{
		case 2:
			if ((how & 0x16) == 0x16)
			{
				value1 = 2*x;
				value2 = 2 - 2*y;
				value = fabs(sqrt(value1*value1+value2*value2) - 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if ((how & 0x2C) == 0x2C)
			{
				value1 = 2 - 2*x;
				value2 = 2 - 2*y;
				value = fabs(sqrt(value1*value1+value2*value2) - 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if ((how & 0x49) == 0x49)
			{
				value1 = 2 - 2*x;
				value2 = 2*y;
				value = fabs(sqrt(value1*value1+value2*value2) - 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if ((how & 0x83) == 0x83)
			{
				value1 = 2*x;
				value2 = 2*y;
				value = fabs(sqrt(value1*value1+value2*value2) - 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			break;
		case 1:
			if ((how & 0x16) == 0x16)
			{
				value1 = 2*x;
				value2 = 2*y;
				value = fabs(value1 - value2 + 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if ((how & 0x2C) == 0x2C)
			{
				value1 = 2*x;
				value2 = 2 - 2*y;
				value = fabs(value2 - value1 + 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if ((how & 0x49) == 0x49)
			{
				value1 = 2*x;
				value2 = 2*y;
				value = fabs(value2 - value1 + 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if ((how & 0x83) == 0x83)
			{
				value1 = 2*x;
				value2 = 2 - 2*y;
				value = fabs(value1 - value2 + 1.0);
				return_value = min(value,1.0);
				return return_value;
			}
			break;
		case 0:
			if ((how & 0x16) == 0x16)
			{
				value1 = 2*x -1;
				value2 = 1 - 2*y;
				return_value = max(value1,value2);
				value1 = 1 - 2*x;
				value2 = 2*y - 1;
				value = min(value1,value2);
				return_value = max(return_value,value);
				return return_value;
			}
			if ((how & 0x2C) == 0x2C)
			{
				value1 = 1 - 2*x;
				value2 = 1 - 2*y;
				return_value = max(value1,value2);
				value1 = 2*x - 1;
				value2 = 2*y - 1;
				value = min(value1,value2);
				return_value = max(return_value,value);
				return return_value;
			}
			if ((how & 0x49) == 0x49)
			{
				value1 = 1 - 2*x;
				value2 = 2*y - 1;
				return_value = max(value1,value2);
				value1 = 2*x - 1;
				value2 = 1 - 2*y;
				value = min(value1,value2);
				return_value = max(return_value,value);
				return return_value;
			}
			if ((how & 0x83) == 0x83)
			{
				value1 = 2*x -1;
				value2 = 2*y -1;
				return_value = max(value1,value2);
				value1 = 1 - 2*x;
				value2 = 1 - 2*y;
				value = min(value1,value2);
				return_value = max(return_value,value);
				return return_value;
			}
			break;
		default:
		case 3:
			break;
	}
	if (how & 0x01)
	{
		value = 2*y - 1;
		return_value = max(return_value,value);
	}
	if (how & 0x02)
	{
		value = 2*x -1;
		return_value = max(return_value,value);
	}
	if (how & 0x04)
	{
		value = 1 - 2*y;
		return_value = max(return_value,value);
	}
	if (how & 0x08)
	{
		value = 1 - 2*x;
		return_value = max(return_value,value);
	}
	switch(TPat->Vals.Pavement.Interior)
	{
		case 2:
			if (how & 0x40)
			{
				value1 = 2 - 2*x;
				value2 = 2*y;
				value = 1.0- sqrt(value1*value1+value2*value2);
				return_value = max(return_value,value);
			}
			if (how & 0x80)
			{
				value1 = 2*x;
				value2 = 2*y;
				value = 1.0- sqrt(value1*value1+value2*value2);
				return_value = max(return_value,value);
			}
			if (how & 0x10)
			{
				value1 = 2*x;
				value2 = 2 - 2*y;
				value = 1.0- sqrt(value1*value1+value2*value2);
				return_value = max(return_value,value);
			}
			if (how & 0x20)
			{
				value1 = 2 - 2*x;
				value2 = 2 - 2*y;
				value = 1.0- sqrt(value1*value1+value2*value2);
				return_value = max(return_value,value);
			}
			break;
		case 1:
			if (how & 0x40)
			{
				value1 = 2 - 2*x;
				value2 = 2*y;
				value = 1.0- (value1+value2);
				return_value = max(return_value,value);
			}
			if (how & 0x80)
			{
				value1 = 2*x;
				value2 = 2*y;
				value = 1.0- (value1+value2);
				return_value = max(return_value,value);
			}
			if (how & 0x10)
			{
				value1 = 2*x;
				value2 = 2 - 2*y;
				value = 1.0- (value1+value2);
				return_value = max(return_value,value);
			}
			if (how & 0x20)
			{
				value1 = 2 - 2*x;
				value2 = 2 - 2*y;
				value = 1.0- (value1+value2);
				return_value = max(return_value,value);
			}
			break;
		default:
		case 0:
			if (how & 0x10)
			{
				value1 = 1 - 2*x;
				value2 = 2*y - 1;
				value = min(value1,value2);
				return_value = max(return_value,value);
			}
			if (how & 0x20)
			{
				value1 = 2*x - 1;
				value2 = 2*y - 1;
				value = min(value1,value2);
				return_value = max(return_value,value);
			}
			if (how & 0x40)
			{
				value1 = 2*x - 1;
				value2 = 1 - 2*y;
				value = min(value1,value2);
				return_value = max(return_value,value);
			}
			if (how & 0x80)
			{
				value1 = 1 - 2*x;
				value2 = 1 - 2*y;
				value = min(value1,value2);
				return_value = max(return_value,value);
			}
			break;
	}
	switch(TPat->Vals.Pavement.Exterior)
	{
		case 2:
			value1 = 2*x - 1;
			value2 = 2*y - 1;
			if ( (((how & 0x06) == 0x06)&&(value1>=0.0)&&(value2<=0.0)) ||
			     (((how & 0x0C) == 0x0C)&&(value1<=0.0)&&(value2<=0.0)) ||
			     (((how & 0x09) == 0x09)&&(value1<=0.0)&&(value2>=0.0)) ||
			     (((how & 0x03) == 0x03)&&(value1>=0.0)&&(value2>=0.0)) )
			{
				value = sqrt(value1*value1+value2*value2);
				value = min(value,1.0);
				return_value = max(return_value,value);
			}
			break;
		case 1:
			if ((how & 0x06) == 0x06)
			{
				value1 = 2 - 2*x;
				value2 = 2*y;
				value = 2.0- (value1+value2);
				value = min(value,1.0);
				return_value = max(return_value,value);
			}
			if ((how & 0x0C) == 0x0C)
			{
				value1 = 2*x;
				value2 = 2*y;
				value = 2.0- (value1+value2);
				value = min(value,1.0);
				return_value = max(return_value,value);
			}
			if ((how & 0x09) == 0x09)
			{
				value1 = 2*x;
				value2 = 2 - 2*y;
				value = 2.0- (value1+value2);
				value = min(value,1.0);
				return_value = max(return_value,value);
			}
			if ((how & 0x03) == 0x03)
			{
				value1 = 2 - 2*x;
				value2 = 2 - 2*y;
				value = 2.0- (value1+value2);
				value = min(value,1.0);
				return_value = max(return_value,value);
			}
			break;
		default:
		case 0:
			break;
	}
	return return_value;
}

static unsigned short tritrigon[][6]=
{
	{0x215,0x344,0x126,   0x126,0x344,0x215}
};

static unsigned short tritetragon[][8]=
{
	{0x126,0x126,0x144,0x144, 0x126,0x126,0x144,0x144},
	{0x611,0x344,0x126,0x423, 0x611,0x344,0x126,0x423},
	{5,0,6,3, 0,5,3,6}
};

static unsigned short tripentagon[][10]=
{
	{0x215,0x244,4,0x144,0x126, 0x126,0x144,4,0x244,0x215},
	{0x215,0x244,0x244,0x611,0x611, 0x244,0x244,0x215,0x423,0x423},
	{0x146,0x126,0x344,0x611,0x611, 0x344, 0x126,0x146,0x522,0x522},
	{5,0,0x244,0x215,3, 3,0x215,0x244,0,5}
};

static unsigned short trihexagon[][12]=
{
	{0x215,0x244,4,4,0x244,0x215, 0x215,0x244,4,4,0x244,0x215},
	{0x413,0x522,0x144,4,0x244,0x215, 0x413,0x522,0x144,4,0x244,0x215},
	{3,0x126,0x144,0,0x244,0x215, 3,0x126,0x144,0,0x244,0x215},
	{0x215,0x244,4,0,6,3, 4,0x244,0x215,3,6,0},
	{5,0,0x244,0x215,0x245,0x211, 5,0x211,0x245,0x215,0x244,0},
	{0x245,0x211,0x215,0x244,0x244,0x211, 0x245,0x211,0x244,0x244,0x215,0x211},
	{0x215,0x244,0x244,0x611, 0x522,0x146,0x146,0x522, 0x611,0x244,0x244,0x215},
	{5,0,0x244,0x215, 0x146,0x122,0x122,0x146, 0x215,0x244,0,5},
	{5,0,6,0x126,0x344,0x211, 5,0x211,0x344,0x126,6,0},
	{0x215,0x344,0x122,0x122,0x344,0x215, 0x215,0x344,0x122,0x122,0x344,0x215},
	{5,0,6,6,0,5, 6,0,5,5,0,6},
	{0x691,0x3C4,0x5A2,0x5A2,0x3C4,0x691, 0x5A2,0x3C4,0x691,0x691,0x3C4,0x5A2},
};

static DBL trigonal (const VECTOR EPoint, const TPATTERN *TPat)
{
	unsigned short how;
	long xv,zv;
	DBL return_value=0;
	DBL value;
	DBL value1;
	DBL value2;
	DBL dist1;
	DBL dist2;
	DBL dist3;
	DBL x,z;
	int lng=0;

	x=EPoint[X];
	z=EPoint[Z];

	xv = floor(x);
	zv = floor(z/SQRT3);
	x -= xv;
	z -= SQRT3*zv;
	/* x,z is in { [0.0, 1.0 [, [0.0, SQRT3 [ } 
	 ** There is some mirror to reduce the problem
	 */
	zv *= 2;
	xv *= 2;
	if ( z > SQRT3_2 )
	{
		z -= SQRT3_2;
		if (x>0.5)
		{
			x -= 0.5;
			xv++;
		}
		else
		{
			x += 0.5;
			xv--;
		}
		zv++;
	}
	if ((x == 0.0)||(z/x>SQRT3))
	{
		z = SQRT3_2 - z;
		x = 0.5 -x;
		xv--;
	}
	if ((x == 1.0)||(z/(1.0-x)>SQRT3))
	{
		z = SQRT3_2 - z;
		x = 1.5 -x;
		xv++;
	}
	switch(TPat->Vals.Pavement.Tile)
	{
		case 6:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
				case 1:
				case 9:
					xv += 5*zv;
					zv = 0;
					xv %= 6; if (xv <0) { xv += 6;}
					lng = 0;
					break;
				case 2:
				case 10:
				case 11:
					zv &= 0x01;
					xv += 3*zv;
					xv %= 6; if (xv <0) { xv += 6;}
					lng = 0;
					break;
				case 3:
					xv += 14*((zv%6+((zv%6)<0?6:0))/2);
					xv %= 6; if (xv <0) { xv += 6;}
					lng = 6;
					zv &= 0x01;
					break;
				case 4:
				case 8:
					xv += 8*((zv%6+((zv%6)<0?6:0))/2);
					xv %= 6; if (xv <0) { xv += 6;}
					lng = 6;
					zv &= 0x01;
					break;
				case 5:
					xv %= 6; if (xv <0) { xv += 6;}
					lng = 6;
					zv &= 0x01;
					break;
				case 6:
				case 7:
					xv -= ((zv%12+((zv%12)<0?12:0))/3);
					xv &= 3;
					lng = 4;
					zv %= 3; if (zv <0) { zv +=3;}
					break;
			}
			how = trihexagon[TPat->Vals.Pavement.Number-1][xv+zv*lng];
			break;
		case 5:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
				case 1:
				case 2:
					zv &= 0x01;
					xv += 5*zv;
					xv %= 10; if (xv <0) { xv += 10 ;  }
					zv = 0x00;
					break;
				case 3:
					zv %= 10; if (zv <0) { zv += 10; }
					xv += 3*zv;
					xv %= 10; if (xv <0) { xv += 10 ;  }
					zv = 0x00;
					break;
			}
			how = tripentagon[TPat->Vals.Pavement.Number-1][xv];
			break;
		case 4:
			zv &= 0x03;
			xv += zv;
			xv &= 0x03;
			zv &= 0x01;
			how = tritetragon[TPat->Vals.Pavement.Number-1][xv+zv*4];
			break;
		case 3:
			zv &= 0x01;
			xv += 3*zv;
			xv %= 6; if (xv < 0) { xv += 6; }
			zv = 0x00;
			how = tritrigon[TPat->Vals.Pavement.Number-1][xv];
			break;
		case 2:
			how = 0x166;
			break;
		case 1:
		default:
			how = 0x07;
			break;
	}

	/* 
	 **      / \
	 **     1   \
	 **    /     2
	 **   /       \
	 **  -----3-----
	 ** *3/2
	 */
	if (how & 0x01)
	{
		dist1 = 1.0 - (fabs(SQRT3 * x - z) * SQRT3 );
		return_value = max(return_value,dist1);
	}
	if (how & 0x02)
	{
		dist2 = 1.0 - (fabs(SQRT3 * (1.0-x) - z) * SQRT3  );
		return_value = max(return_value,dist2);
	}
	if (how & 0x04)
	{
		dist3 = 1.0 - (z * 2.0 * SQRT3 );
		return_value = max(return_value,dist3);
	}
	switch(TPat->Vals.Pavement.Interior)
	{
		case 1:
			dist1 = (1.0 - (fabs(SQRT3 * z + x) ));
			dist2 = (1.0 - (fabs(SQRT3 * z - x + 1.0) ));
			dist3 = (1.0 - (x * 2.0 ));
			if (((how & 0x83) == 0x00)&&(dist1<0)&&(dist2<0))
			{
				value1 = (3.0 / 2.0 *(fabs(SQRT3 * z + x) ) - 2.0);
				value2 = (3.0 / 2.0 *(fabs(SQRT3 * z - x + 1.0) ) - 2.0);
				value =  min(value1,value2);
				return_value = max(return_value,value);
			}
			if (((how & 0x85) == 0x00)&&(dist1>0)&&(dist3>0))
			{
				value1 = (1.0 - 3.0 / 2.0 * (fabs(SQRT3 * z + x) ));
				value2 = (1.0 - (x * 3.0 ));
				value =  min(value1,value2);
				return_value = max(return_value,value);
			}
			if (((how & 0x86) == 0x00)&&(dist3<0)&&(dist2>0))
			{
				value1 = (1.0 - 3.0 / 2.0 *(fabs(SQRT3 * z - x + 1.0) ));
				value2 = ((x * 3.0 ) - 2.0);
				value =  min(value1,value2);
				return_value = max(return_value,value);
			}
			break;
		case 2:
			if ((how & 0x83) == 0x00)
			{
				dist1 = x - 0.5;
				dist2 = z - SQRT3_2;
				dist3 = 1.0 - (sqrt((dist1*dist1+dist2*dist2))*3.0 );
				return_value = max(return_value,dist3);
			}
			if ((how & 0x85) == 0x00)
			{
				dist1 = x;
				dist2 = z;
				dist3 = 1.0 - (sqrt((dist1*dist1+dist2*dist2)) *3.0);
				return_value = max(return_value,dist3);
			}
			if ((how & 0x86) == 0x00)
			{
				dist1 = x - 1.0;
				dist2 = z ;
				dist3 = 1.0 - (sqrt((dist1*dist1+dist2*dist2)) *3.0);
				return_value = max(return_value,dist3);
			}
			break;
		case 0:
			if ((how & 0x83) == 0x00)
			{
				dist3 = 1.0 - ((SQRT3_2 - z) * 2.0 * SQRT3  );
				return_value = max(return_value,dist3);
			}
			if ((how & 0x85) == 0x00)
			{
				dist2 = 1.0 - (fabs(SQRT3 * x + z) * SQRT3 );
				return_value = max(return_value,dist2);
			}
			if ((how & 0x86) == 0x00)
			{
				dist1 = 1.0 - (fabs(SQRT3 * (x -1.0) - z) * SQRT3 );
				return_value = max(return_value,dist1);
			}
			break;
	}
	switch(TPat->Vals.Pavement.Exterior)
	{
		case 2:
			dist1 = (1.0 - (fabs(SQRT3 * z + x) ));
			dist2 = (1.0 - (fabs(SQRT3 * z - x + 1.0) ));
			dist3 = (1.0 - (x * 2.0 ));
			if ( (((how & 0x03) == 0x03)&&(dist1<=0.0)&&(dist2<=0.0)) ||
			     (((how & 0x06) == 0x06)&&(dist2>=0.0)&&(dist3<=0.0)) ||
			     (((how & 0x05) == 0x05)&&(dist1>=0.0)&&(dist3>=0.0)) )
			{
				value1 = x - 0.5;
				value2 = z - SQRT3_2/3.0;
				value = 2 * SQRT3 *sqrt(value1*value1+value2*value2);
				return_value = min(1.0,value);
			}
			break;
		case 1:
			/* dist1 = (1.0 - (fabs(SQRT3 * z + x) )); */
			dist1 = (1.0 - (fabs(SQRT3 * x - z)*SQRT3 ));
			/* dist2 = (1.0 - (fabs(SQRT3 * z - x + 1.0) )); */
			dist2 = (1.0 - (fabs(SQRT3 * (1.0 -x ) -z ) *SQRT3));
			/* dist3 = (1.0 - (x * 2.0 )); */
			dist3 = (1.0 - (z * 2.0 * SQRT3));
			value1 = (x - 0.5);
			value2 = (z - SQRT3_2/3.0);
			if  (((how & 0x03) == 0x03)&&(dist1>=0.0)&&(dist2>=0.0))
			{
				value = fabs(value2 * 2.0 * SQRT3 );
				return_value = min(1.0,value);
			}
			if  (((how & 0x06) == 0x06)&&(dist2>=0.0)&&(dist3>=0.0))
			{
				value = fabs(SQRT3 * value1 - value2) * SQRT3 ;
				return_value = min(1.0,value);
			}
			if  (((how & 0x05) == 0x05)&&(dist1>=0.0)&&(dist3>=0.0))
			{
				value = fabs(SQRT3 * value1 + value2) * SQRT3  ;
				return_value = min(1.0,value);
			}
			break;
		case 0:
		default:
			break;
	}
	dist1 = (1.0 - (fabs(SQRT3 * z + x) ));
	dist2 = (1.0 - (fabs(SQRT3 * z - x + 1.0) ));
	dist3 = (1.0 - (x * 2.0 ));
	switch(TPat->Vals.Pavement.Form)
	{
		case 2:
			if (((how & 0x120) == 0x120)&&(dist1<0)&&(dist2<0))
			{
				value1 = x;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x140) == 0x140)&&(dist1>0)&&(dist3>0))
			{
				value1 = x - 0.5;
				value2 = z- SQRT3_2;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x210) == 0x210)&&(dist1<0)&&(dist2<0))
			{
				value1 = x - 1.0;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x240) == 0x240)&&(dist3<0)&&(dist2>0))
			{
				value1 = x - 0.5;
				value2 = z - SQRT3_2;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x410) == 0x410)&&(dist1>0)&&(dist3>0))
			{
				value1 = x - 1.0;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x420) == 0x420)&&(dist3<0)&&(dist2>0))
			{
				value1 = x;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			break;
		case 1:
			if (((how & 0x120) == 0x120)&&(dist1<0)&&(dist2<0))
			{
				value = -dist1 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x140) == 0x140)&&(dist1>0)&&(dist3>0))
			{
				value = dist1 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x210) == 0x210)&&(dist1<0)&&(dist2<0))
			{
				value = -dist2 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x240) == 0x240)&&(dist3<0)&&(dist2>0))
			{
				value = dist2 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x410) == 0x410)&&(dist1>0)&&(dist3>0))
			{
				value = dist3 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x420) == 0x420)&&(dist2>0)&&(dist3<0))
			{
				value = -dist3 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x120) == 0x120)&&(dist1<0)&&(dist2<0))
			{
				value = -dist1 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			break;
		default:
		case 0:
			break;
	}
	return return_value;
}

/*
** open face (3 bits)
** special (1 bit)
** nexus vertex/open face (3 bits)
** unused (1 bits)
** close face (3 bits)
*/
static unsigned short hexmonogon[][6]=
{
	{0x691,0x3C4,0x5A2,0x5A2,0x3C4,0x691}
};

static unsigned short hexdigon[][12]=
{
	{0x691,0x3C4,0x1A2,0x1A2,0x3C4,0x691,0x5A2,0x1C4,0x6080,0x6080,0x1C4,0x5A2 }
};

static unsigned short hextrigon[][36]=
{
	{
		0x691,0x3C4,0x5A2,  0x5A2,0x3C4, 0x691,
		0x5A2,0x3C4,0x691,  0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x691,0x3C4,0x5A2
	},

	{
		0x691,0x3C4,0x1A2, 0x1A2,0x3C4,0x691,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x691,0x2C4,0x4080, 0x1080,0x1080,0x491,
		0x5A2,0x3C4,0x291, 0x291,0x3C4,0x5A2,
		0x491,0x1080,0x1080, 0x4080,0x2C4,0x691,
		0x4A2,0x2080,0x2080, 0x4080,0x1C4,0x5A2
	},

	{
		0x691,0x3C4,0x1A2, 0x1A2,0x3C4,0x291, 0x291,0x3C4,0x5A2,
		0x5A2,0x1C4,0x6080,0x6080,0x0C4,0x5080,0x5080,0x2C4,0x691,
		0x5A2,0x1C4,0x6080,0x6080,0x0C4,0x5080,0x5080,0x2C4,0x691,
		0x691,0x3C4,0x1A2, 0x1A2,0x3C4,0x291, 0x291,0x3C4,0x5A2
	}
};

static unsigned short hextetragon[][48]=
{
	{
		0x691,0x3C4,0x5A2,  0x5A2,0x3C4, 0x691,
		0x5A2,0x3C4,0x691,  0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x691,0x3C4,0x5A2
	},

	{
		0x691,0x2C4,0x4080, 0x1080,0x80,0x2080,
		0x4080,0x2C4,0x691, 0x291,0x3C4,0x1A2,
		0x4080,0x1C4,0x5A2, 0x1A2,0x3C4,0x291,
		0x5A2,0x1C4,0x4080, 0x2080,0x80,0x1080,
		0x691,0x2C4,0x4080, 0x1080,0x80,0x2080,
		0x4080,0x2C4,0x691, 0x291,0x3C4,0x1A2,
		0x4080,0x1C4,0x5A2, 0x1A2,0x3C4,0x291,
		0x5A2,0x1C4,0x4080, 0x2080,0x80,0x1080
	},

	{
		0x691,0x2C4,0x5080,  0x5080,0x0C4,0x6080,
		0x6080,0x0C4,0x5080, 0x5080,0x2C4,0x691,
		0x5A2,0x3C4,0x291, 0x291,0x3C4,0x1A2,
		0x1A2,0x3C4,0x291, 0x291,0x3C4,0x5A2,
		0x691,0x2C4,0x5080,  0x5080,0x0C4,0x6080,
		0x6080,0x0C4,0x5080, 0x5080,0x2C4,0x691,
		0x5A2,0x3C4,0x291, 0x291,0x3C4,0x1A2,
		0x1A2,0x3C4,0x291, 0x291,0x3C4,0x5A2,
	},

	{
		0x691,0x3C4,0x5A2,  0x4A2,0x3080,0x491,
		0x1A2,0x3C4,0x691,  0x691,0x3C4,0x5A2,
		0x6080,0x1C4,0x5A2, 0x1A2,0x3C4,0x691,
		0x5A2,0x1C4,0x6080, 0x6080,0x1C4,0x5A2,
		0x491,0x3080,0x0A2, 0x5A2,0x1C4,0x6080,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x0A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2
	},

	{
		0x691,0x3C4,0x1A2,  0x5A2,0x1C4,0x6080,
		0x5080,0x2C4,0x691, 0x491,0x3080,0x0A2,
		0x291,0x3C4,0x5A2,  0x4A2,0x3080,0x091,
		0x5A2,0x3C4,0x291,  0x691,0x2C4,0x5080,
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x1A2,0x3C4,0x691,  0x091,0x3080,0x4A2,
		0x6080,0x1C4,0x5A2, 0x0A2,0x3080,0x491,
		0x5A2,0x1C4,0x6080, 0x6080,0x1C4,0x5A2
	},

	{
		0x691,0x3C4,0x5A2,  0x0A2,0x3080,0x491,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x691,0x2C4,0x4080, 0x1080,0x1080,0x491,
		0x5A2,0x3C4,0x291,  0x291,0x3C4,0x5A2,
		0x491,0x1080,0x1080,0x4080,0x2C4,0x691,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2,
		0x491,0x3080,0x0A2, 0x5A2,0x3C4,0x691,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2
	},

	{
		0x691,0x3C4,0x5A2,  0x1A2,0x3C4,0x691, 0x091,0x3080,0x0A2, 0x5A2,0x3C4,0x291,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2,0x4A2,0x3080,0x491, 0x691,0x2C4,0x5080,
		0x091,0x3080,0x0A2, 0x5A2,0x3C4,0x291, 0x691,0x3C4,0x5A2,  0x1A2,0x3C4,0x691,
		0x4A2,0x3080,0x491, 0x691,0x2C4,0x5080,0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2
	}
};

static unsigned short hexpentagon[][60]=
{ /* 0 */
	{
		0x691,0x3C4,0x5A2,  0x5A2,0x3C4, 0x691,
		0x5A2,0x3C4,0x691,  0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x691,0x3C4,0x5A2
	},
	/* 1 */
	{
		0x6080,0x1C4,0x5A2, 0x1A2,0x3C4,0x691,
		0x5A2,0x1C4,0x6080, 0x6080,0x1C4,0x5A2 ,
		0x491,0x3080,0x0A2, 0x5A2,0x1C4,0x6080,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x0A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x4A2,0x3080,0x491,
		0x1A2,0x3C4,0x691,  0x691,0x3C4,0x5A2
	},
	/* 2 */
	{
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4, 0x691,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x691,0x2C4,0x4080, 0x1080,0x1080,0x491,
		0x5A2,0x3C4,0x691,  0x091,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x091, 0x691,0x3C4,0x5A2,
		0x491,0x1080,0x1080,0x4080,0x2C4,0x691,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2
	},
	/* 3 */
	{
		0x691,0x3C4,0x5A2,  0x0A2,0x3080,0x491,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x691,0x2C4,0x4080, 0x1080,0x1080,0x491,
		0x5A2,0x3C4,0x691,  0x091,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x091, 0x691,0x3C4,0x5A2,
		0x491,0x1080,0x1080,0x4080,0x2C4,0x691,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2,
		0x491,0x3080,0x0A2, 0x5A2,0x3C4, 0x691,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2
	},
	/* 4 */
	{
		0x6080,0x1C4,0x5A2, 0x0A2,0x3080,0x491,
		0x5A2,0x1C4,0x6080, 0x6080,0x1C4,0x5A2,
		0x491,0x3080,0x0A2, 0x5A2,0x1C4,0x6080,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x0A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x4A2,0x3080,0x491,
		0x5A2,0x3C4,0x691,  0x691,0x3C4,0x5A2,
		0x491,0x3080,0x4A2, 0x5A2,0x3C4,0x691,
		0x0A2,0x3080,0x491, 0x491,0x3080,0x4A2,
	},
	/* 5 */
	{
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4, 0x691,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x491,0x1080,0x080, 0x080,0x1080,0x491,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2,
		0x491,0x3080,0x0A2, 0x5A2,0x3C4,0x691,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x0A2,0x3080,0x491,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x491,0x1080,0x080, 0x080,0x1080,0x491,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2
	},
	/* 6 */
	{
		0x691,0x2C4,0x4080, 0x1080,0x1080, 0x491,
		0x5A2,0x3C4,0x291,  0x291,0x3C4,0x5A2,
		0x491,0x1080,0x1080,0x4080,0x2C4,0x691,
		0x4A2,0x2080,0x080, 0x080,0x2080,0x4A2,
		0x491,0x1080,0x080, 0x080,0x1080,0x491,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2,
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4,0x691,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x491,0x1080,0x080, 0x080,0x1080,0x491,
		0x4A2,0x2080,0x080, 0x080,0x2080,0x4A2
	},
	/* 7 */
	{
		0x6080,0x1C4,0x5A2,
		0x5A2,0x1C4,0x4080,
		0x691,0x2C4,0x4080,
		0x4080,0x2C4,0x691,
		0x4080,0x1C4,0x5A2,
		0x5A2,0x1C4,0x6080,
		0x691,0x3C4,0x1A2,

		0x5A2,0x1C4,0x6080,
		0x491,0x3080,0x0A2,

		0x4A2,0x3080,0x091,
		0x491,0x1080,0x1080,
		0x4A2,0x2080,0x2080,
		0x691,0x3C4,0x1A2,
		0x1A2,0x3C4,0x691,
		0x2080,0x2080,0x4A2,
		0x1080,0x1080,0x491,
		0x091,0x3080,0x4A2,
		0x0A2,0x3080,0x491,
		0x6080,0x1C4,0x5A2,
		0x1A2,0x3C4,0x691
	},
	/* 8 */
	{
		0x6080,0x0C4,0x5080, 0x5080,0x2C4, 0x291,
		0x5A2,0x3C4,0x291,  0x291,0x2C4,0x5080,
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x5080,0x2C4,0x691, 0x091,0x3080,0x4A2,
		0x091,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2, 0x0A2,0x3080,0x491,
		0x5A2,0x1C4,0x6080, 0x6080,0x1C4,0x5A2,

		0x691,0x3C4,0x1A2, 0x5A2,0x1C4,0x6080,
		0x1A2,0x3C4,0x291, 0x691,0x3C4,0x1A2
	},
	/* 9 */
	{
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4,0x691,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2,
		0x291,0x3C4,0x1A2,  0x1A2,0x3C4,0x291,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x5080,
		0x491,0x3080,0x0A2, 0x5A2,0x3C4,0x691,
		0x6A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x0A2,0x3080,0x491,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2,
		0x291,0x3C4,0x1A2, 0x1A2,0x3C4,0x291,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x5080,
	},
	/* 10 */
	{
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4,0x691,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x491,0x1080,0x80, 0x80,0x1080,0x091,
		0x4A2,0x2080,0x2080, 0x4080,0x0C4,0x5080,
		0x691,0x3C4,0x1A2, 0x5A2,0x3C4,0x291,
		0x5080,0x2C4,0x691, 0x691,0x2C4,0x5080,
		0x291,0x3C4,0x5A2,  0x1A2,0x3C4,0x691,
		0x5080,0x0C4,0x4080, 0x2080,0x2080,0x4A2,
		0x091,0x1080,0x80, 0x80,0x1080,0x491,
		0x4A2,0x2080,0x2080, 0x4080,0x1C4,0x5A2
	},
	/* 11 */
	{
		0x291,0x3C4,0x5A2,  0x1A2,0x3C4,0x691,
		0x4080,0x0C4,0x4080, 0x2080,0x2080,0x4A2,
		0x4080,0x0C4,0x4080, 0x1080,0x1080,0x491,
		0x5A2,0x3C4,0x291,  0x291,0x3C4,0x5A2,
		0x491,0x1080,0x1080,0x4080,0x0C4,0x4080,
		0x4A2,0x2080,0x2080,0x4080,0x0C4,0x4080,
		0x691,0x3C4,0x1A2, 0x5A2,0x3C4,0x291,
		0x1A2,0x3C4,0x691, 0x491,0x1080,0x1080,
		0x2080,0x2080,0x4A2, 0x4A2,0x2080,0x2080,
		0x1080,0x1080,0x491, 0x691,0x3C4,0x1A2
	},
	/* 12 */
	{
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x5080,0x2C4,0x691, 0x091,0x3080,0x4A2,
		0x291,0x3C4,0x5A2,  0x0A2,0x3080,0x491,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2,
		0x291,0x3C4,0x1A2,  0x1A2,0x3C4,0x291,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x5080,
		0x491,0x3080,0x0A2, 0x5A2,0x3C4,0x291,
		0x4A2,0x3080,0x091, 0x691,0x2C4,0x5080,
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x5A2,0x3C4,0x291,  0x291,0x3C4,0x5A2
	},
	/* 13 */
	{
		0x691,0x3C4,0x5A2,  0x5A2,0x3C4,0x691,
		0x5A2,0x3C4,0x691,  0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x091,
		0x4A2,0x3080,0x091, 0x691,0x2C4,0x5080,
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x291,
		0x5080,0x2C4,0x291, 0x291,0x2C4,0x5080,
		0x291,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x5080,0x2C4,0x691, 0x091,0x3080,0x4A2,
		0x091,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x691,0x3C4,0x5A2
	},
	/* 14 */
	{
		0x291,0x3C4,0x5A2, 0x5A2,0x3C4,0x691,
		0x5A2,0x3C4,0x691, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2,0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491,0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2,0x4A2,0x3080,0x091,
		0x4A2,0x3080,0x091,0x691,0x2C4,0x5080,
		0x691,0x2C4,0x5080,0x5080,0x2C4,0x291,
		0x5080,0x2C4,0x291,0x291,0x2C4,0x5080,
		0x291,0x2C4,0x5080,0x5080,0x2C4,0x691,
		0x5080,0x2C4,0x691,0x291,0x3C4,0x5A2
	},
	/* 15 */
	{
		0x691,0x2C4,0x5080, 0x5080,0x0C4,0x4080,
		0x1A2,0x3C4,0x691,  0x291,0x2C4,0x4080,
		0x2080,0x2080,0x4A2,0x5A2,0x3C4,0x691,
		0x1080,0x1080,0x491,0x491,0x3080,0x4A2,
		0x091,0x3080,0x4A2, 0x4A2,0x3080,0x091,
		0x4A2,0x3080,0x491, 0x491,0x1080,0x1080,
		0x691,0x3C4,0x5A2,  0x4A2,0x2080,0x2080,
		0x4080,0x2C4,0x291, 0x691,0x3C4,0x1A2,
		0x4080,0x0C4,0x5080,0x5080,0x2C4,0x691,
		0x5A2,0x3C4,0x291,  0x291,0x3C4,0x5A2
	},
	/* 16 */
	{
		0x4080,0x1C4,0x1A2, 0x1A2,0x1C4,0x4080,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x4080,
		0x691,0x3C4,0x1A2,  0x5A2,0x3C4,0x691,
		0x1A2,0x3C4,0x691,  0x491,0x3080,0x4A2,
		0x2080,0x2080,0x4A2,0x4A2,0x3080,0x091,
		0x1080,0x1080,0x491,0x491,0x1080,0x1080,
		0x091,0x3080,0x4A2, 0x4A2,0x2080,0x2080,
		0x4A2,0x3080,0x491, 0x691,0x3C4,0x1A2,
		0x691,0x3C4,0x5A2,  0x1A2,0x3C4,0x691,
		0x4080,0x0C4,0x6080,0x6080,0x1C4,0x5A2
	},
	/* 17 */
	{
		0x291,0x3C4,0x1A2,  0x1A2,0x3C4,0x291,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x5080,
		0x691,0x3C4,0x1A2,  0x5A2,0x3C4,0x691,
		0x5080,0x2C4,0x691, 0x491,0x3080,0x4A2,
		0x091,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x091,
		0x4A2,0x3080,0x491, 0x691,0x2C4,0x5080,
		0x691,0x3C4,0x5A2,  0x1A2,0x3C4,0x691,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2
	},
	/* 18 */
	{
		0x091,0x3080,0x4A2, 0x5A2,0x3C4,0x691,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x0A2,0x3080,0x091,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x5080,
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4,0x691,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2,
		0x091,0x3080,0x0A2, 0x5A2,0x3C4,0x691,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x4A2,0x3080,0x091,
		0x5080,0x2C4,0x691, 0x691,0x2C4,0x5080
	},
	/* 19 */
	{
		0x691,0x3C4,0x1A2,  0x1A2,0x3C4,0x691,
		0x5A2,0x1C4,0x4080, 0x2080,0x2080,0x4A2,
		0x691,0x2C4,0x4080, 0x1080,0x1080,0x091,
		0x5080,0x2C4,0x691, 0x291,0x2C4,0x5080,
		0x091,0x3080,0x4A2, 0x5A2,0x3C4,0x691,

		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x4A2,0x3080,0x091,
		0x5080,0x2C4,0x291, 0x691,0x2C4,0x5080,
		0x091,0x1080,0x1080,0x4080,0x2C4,0x691,
		0x4A2,0x2080,0x2080,0x4080,0x1C4,0x5A2
	},
	/* 20 */
	{
		0x291,0x3C4,0x5A2,  0x5A2,0x3C4,0x691,
		0x5A2,0x3C4,0x691,  0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x091,
		0x4A2,0x3080,0x091, 0x691,0x2C4,0x5080,
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x5080,0x2C4,0x691, 0x091,0x3080,0x4A2,
		0x091,0x3080,0x4A2, 0x4A2,0x3080,0x091,
		0x4A2,0x3080,0x091, 0x691,0x2C4,0x5080,
		0x691,0x2C4,0x5080, 0x5080,0x2C4,0x691,
		0x5080,0x2C4,0x691, 0x291,0x3C4,0x5A2
	},
	/* 21 */
	{
		0x691,0x3C4,0x1A2,  0x5A2,0x3C4,0x291,
		0x5080,0x2C4,0x691, 0x691,0x2C4,0x5080,
		0x291,0x3C4,0x5A2,  0x1A2,0x3C4,0x691,
		0x5080,0x0C4,0x6080,0x6080,0x1C4,0x5A2,
		0x091,0x3080,0x0A2, 0x5A2,0x3C4,0x691,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x491,0x3080,0x4A2, 0x4A2,0x3080,0x491,
		0x4A2,0x3080,0x491, 0x491,0x3080,0x4A2,
		0x691,0x3C4,0x5A2,  0x0A2,0x3080,0x091,
		0x5A2,0x1C4,0x6080, 0x6080,0x0C4,0x5080
	}
};

static DBL hexagonal (const VECTOR EPoint, const TPATTERN *TPat)
{
	unsigned short how;
	long xv,zv;
	DBL return_value=0;
	DBL value;
	DBL value1;
	DBL value2;
	DBL dist1;
	DBL dist2;
	DBL dist3;
	DBL x,z;
	int lng;

	x=EPoint[X];
	z=EPoint[Z];

	xv = floor(x);
	zv = floor(z/SQRT3);
	x -= xv;
	z -= SQRT3*zv;
	/* x,z is in { [0.0, 1.0 [, [0.0, SQRT3 [ } 
	 ** There is some mirror to reduce the problem
	 */
	zv *= 2;
	xv *= 2;
	if ( z > SQRT3_2 )
	{
		z -= SQRT3_2;
		if (x>0.5)
		{
			x -= 0.5;
			xv++;
		}
		else
		{
			x += 0.5;
			xv--;
		}
		zv++;
	}
	if ((x == 0.0)||(z/x>SQRT3))
	{
		z = SQRT3_2 - z;
		x = 0.5 -x;
		xv--;
	}
	if ((x == 1.0)||(z/(1.0-x)>SQRT3))
	{
		z = SQRT3_2 - z;
		x = 1.5 -x;
		xv++;
	}
	switch(TPat->Vals.Pavement.Tile)
	{
		case 5:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
				case 2:
				case 3:
				case 5:
				case 6:
				case 19:
					zv %= 10; if (zv < 0) { zv += 10; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 60 */
					zv = 0;
					break;
				case 1:
				case 4:
				case 9:
					zv -= 2*(((xv%30+(xv%30<0?30:0))/6));
					zv %= 10; if (zv < 0) { zv += 10; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 60 */
					zv = 0;
					break;
				case 7:
					zv -= 7*(((xv%60+(xv%60<0?60:0))/3));
					zv %= 20; if (zv < 0) { zv += 20; }
					xv %= 3; if (xv < 0) { xv += 3; }
					xv += 3*zv; /* 60 */
					zv = 0;
					break;
				case 8:
				case 10:
				case 13:
				case 14:
				case 15:
				case 17:
				case 20:
				case 21:
					zv += 2*(((xv%30+(xv%30<0?30:0))/6));
					zv %= 10; if (zv < 0) { zv += 10; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 60 */
					zv = 0;
					break;
				case 11:
				case 16:
					zv -= 6*(((xv%30+(xv%30<0?30:0))/6));
					zv %= 10; if (zv < 0) { zv += 10; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 60 */
					zv = 0;
					break;
				case 12:
				case 18:
					zv += 6*(((xv%30+(xv%30<0?30:0))/6));
					zv %= 10; if (zv < 0) { zv += 10; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 60 */
					zv = 0;
					break;
			}
			how = hexpentagon[TPat->Vals.Pavement.Number-1][xv];
			break;
		case 4:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
					zv &= 0x07;
					xv %= 6; if(xv <0) { xv += 6; }
					xv += 6*zv; /* 48 */
					zv = 0;
					break;
				case 3:
					zv -= 2*(((xv%24+(xv%24<0?24:0))/6));
					zv %= 8; if (zv < 0) { zv += 8; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 48 */
					zv = 0;
					break;
				case 2:
					zv &= 0x01;
					xv %= 12; if (xv < 0) { xv += 12; }
					xv += 12*zv; /* 24 */
					zv = 0;
					break;
				case 5:
				case 4:
					zv -= 2*(((xv%24+(xv%24<0?24:0))/6));
					zv %= 8; if (zv < 0) { zv += 8; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 48 */
					zv = 0;
					break;
				case 1:
					zv += 2*(((xv%12+(xv%12<0?12:0))/6));
					zv %= 8; if (zv < 0) { zv += 8; }
					xv %= 6; if (xv < 0) { xv += 6; }
					xv += 6*zv; /* 48 */
					zv = 0;
					break;
				case 6:
					zv %= 4; if (zv < 0) { zv += 4; }
					xv %= 12; if (xv < 0) { xv += 12; }
					xv += 12*zv; /* 48 */
					zv = 0;
					break;
			}
			how = hextetragon[TPat->Vals.Pavement.Number-1][xv];
			break;
		case 3:
			switch(TPat->Vals.Pavement.Number-1)
			{
				case 0:
					zv %= 6; if(zv <0) { zv += 6; }
					xv %= 6; if(xv <0) { xv += 6; }
					xv += 6*zv;
					zv = 0;
					break;
				case 1:
					zv += 2*(((xv%18+(xv%18<0?18:0))/6));
					zv %= 6; if(zv <0) { zv += 6; }
					xv %= 6; if(xv <0) { xv += 6; }
					xv += 6*zv;
					zv = 0;
					break;
				case 2:
					zv &= 0x01;
					xv %= 18; if (xv < 0) { xv += 18; }
					xv += 18*zv;
					zv = 0x00;
					break;
			}
			how = hextrigon[TPat->Vals.Pavement.Number-1][xv];
			break;
		case 2:
			zv &= 0x01;
			xv %= 6; if (xv < 0) { xv += 6; }
			how = hexdigon[TPat->Vals.Pavement.Number-1][xv+6*zv];
			break;
		case 1:
		default:
			zv &= 0x01;
			xv += 3*zv;
			xv %= 6; if (xv <0) { xv += 6;}
			lng = 0;
			how = hexmonogon[TPat->Vals.Pavement.Number-1][xv];
			break;
	}

	/* 
	 **      / \
	 **     1   \
	 **    /     2
	 **   /       \
	 **  -----3-----
	 ** height = sqrt(3)/2
	 */
	if (how & 0x01)
	{
		dist1 = 1.0 - (fabs(SQRT3 * x - z) * SQRT3 );
		return_value = max(return_value,dist1);
	}
	if (how & 0x02)
	{
		dist2 = 1.0 - (fabs(SQRT3 * (1.0-x) - z) * SQRT3  );
		return_value = max(return_value,dist2);
	}
	if (how & 0x04)
	{
		dist3 = 1.0 - (z * 2.0 * SQRT3 );
		return_value = max(return_value,dist3);
	}
	switch(TPat->Vals.Pavement.Interior)
	{
		case 1:
			dist1 = (1.0 - (fabs(SQRT3 * z + x) ));
			dist2 = (1.0 - (fabs(SQRT3 * z - x + 1.0) ));
			dist3 = (1.0 - (x * 2.0 ));
			if ( (((how & 0x83) == 0x00)||((how & 0x4000) == 0x4000)) &&
			     (dist1<0)&&(dist2<0) )
			{
				value1 = (3.0 / 2.0 *(fabs(SQRT3 * z + x) ) - 2.0);
				value2 = (3.0 / 2.0 *(fabs(SQRT3 * z - x + 1.0) ) - 2.0);
				value =  min(value1,value2);
				return_value = max(return_value,value);
			}
			if ((((how & 0x85) == 0x00)||((how & 0x2000) == 0x2000))
					&&(dist1>0)&&(dist3>0))
			{
				value1 = (1.0 - 3.0 / 2.0 * (fabs(SQRT3 * z + x) ));
				value2 = (1.0 - (x * 3.0 ));
				value =  min(value1,value2);
				return_value = max(return_value,value);
			}
			if ((((how & 0x86) == 0x00)||((how & 0x1000) == 0x1000))
					&&(dist3<0)&&(dist2>0))
			{
				value1 = (1.0 - 3.0 / 2.0 *(fabs(SQRT3 * z - x + 1.0) ));
				value2 = ((x * 3.0 ) - 2.0);
				value =  min(value1,value2);
				return_value = max(return_value,value);
			}
			break;
		case 2:
			if (((how & 0x83) == 0x00)||((how & 0x4000) == 0x4000))
			{
				dist1 = x - 0.5;
				dist2 = z - SQRT3_2;
				dist3 = 1.0 - (sqrt((dist1*dist1+dist2*dist2))*3.0 );
				return_value = max(return_value,dist3);
			}
			if (((how & 0x85) == 0x00)||((how & 0x2000) == 0x2000))
			{
				dist1 = x;
				dist2 = z;
				dist3 = 1.0 - (sqrt((dist1*dist1+dist2*dist2)) *3.0);
				return_value = max(return_value,dist3);
			}
			if (((how & 0x86) == 0x00)||((how & 0x1000) == 0x1000))
			{
				dist1 = x - 1.0;
				dist2 = z ;
				dist3 = 1.0 - (sqrt((dist1*dist1+dist2*dist2)) *3.0);
				return_value = max(return_value,dist3);
			}
			break;
		case 0:
			if (((how & 0x83) == 0x00)||((how & 0x4000) == 0x4000))
			{
				dist3 = 1.0 - ((SQRT3_2 - z) * 2.0 * SQRT3  );
				return_value = max(return_value,dist3);
			}
			if (((how & 0x85) == 0x00)||((how & 0x2000) == 0x2000))
			{
				dist2 = 1.0 - (fabs(SQRT3 * x + z) * SQRT3 );
				return_value = max(return_value,dist2);
			}
			if (((how & 0x86) == 0x00)||((how & 0x1000) == 0x1000))
			{
				dist1 = 1.0 - (fabs(SQRT3 * (x -1.0) - z) * SQRT3 );
				return_value = max(return_value,dist1);
			}
			break;
	}
	dist1 = (1.0 - (fabs(SQRT3 * z + x) ));
	dist2 = (1.0 - (fabs(SQRT3 * z - x + 1.0) ));
	dist3 = (1.0 - (x * 2.0 ));
	switch(TPat->Vals.Pavement.Form)
	{
		case 2:
			if (((how & 0x120) == 0x120)&&(dist1<0)&&(dist2<0))
			{
				value1 = x;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x140) == 0x140)&&(dist1>0)&&(dist3>0))
			{
				value1 = x - 0.5;
				value2 = z- SQRT3_2;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x210) == 0x210)&&(dist1<0)&&(dist2<0))
			{
				value1 = x - 1.0;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x240) == 0x240)&&(dist3<0)&&(dist2>0))
			{
				value1 = x - 0.5;
				value2 = z - SQRT3_2;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x410) == 0x410)&&(dist1>0)&&(dist3>0))
			{
				value1 = x - 1.0;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x420) == 0x420)&&(dist3<0)&&(dist2>0))
			{
				value1 = x;
				value2 = z;
				value = 2.0*SQRT3*(sqrt(value1*value1+value2*value2 ) - SQRT3/3.0 );
				value = max(value,0.0);
				return_value = min(value,1.0);
				return return_value;
			}
			break;
		case 1:
			if (((how & 0x120) == 0x120)&&(dist1<0)&&(dist2<0))
			{
				value = -dist1 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x140) == 0x140)&&(dist1>0)&&(dist3>0))
			{
				value = dist1 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x210) == 0x210)&&(dist1<0)&&(dist2<0))
			{
				value = -dist2 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x240) == 0x240)&&(dist3<0)&&(dist2>0))
			{
				value = dist2 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x410) == 0x410)&&(dist1>0)&&(dist3>0))
			{
				value = dist3 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x420) == 0x420)&&(dist2>0)&&(dist3<0))
			{
				value = -dist3 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			if (((how & 0x120) == 0x120)&&(dist1<0)&&(dist2<0))
			{
				value = -dist1 * 2;
				return_value = min(value,1.0);
				return return_value;
			}
			break;
		default:
		case 0:
			break;
	}
	return return_value;
}

/*****************************************************************************
*
* FUNCTION
*
*   tiling_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   J. Grimbert
*
* DESCRIPTION
*
*   Classical tiling patterns
*
* CHANGES
*
*
******************************************************************************/
static DBL tiling_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	switch(TPat->Vals.Tiling.Pattern)
	{
		case 27: return tiling_penrose(EPoint, true, false);
		case 26: return tiling_penrose(EPoint, false, false);
		case 25: return tiling_penrose1(EPoint, false);
		case 24: return tiling_dodeca_hex_5(EPoint);
		case 23: return tiling_dodeca_hex(EPoint);
		case 22: return tiling_dodeca_tri(EPoint);
		case 21: return tiling_square_tri(EPoint);
		case 20: return tiling_hexa_tri_left(EPoint);
		case 19: return tiling_hexa_tri_right(EPoint);
		case 18: return tiling_rectangle_pair(EPoint);
		case 17: return tiling_hexa_square_triangle_6(EPoint);
		case 16: return tiling_hexa_square_triangle(EPoint);
		case 15: return tiling_square_double(EPoint);
		case 14: return tiling_square_internal_5(EPoint);
		case 13: return tiling_square_internal(EPoint);
		case 12: return tiling_rectangle_square(EPoint);
		case 11: return tiling_square_rectangle(EPoint);
		case 10: return tiling_square_offset(EPoint);
		case 9: return tiling_hexa_triangle(EPoint);
		case 8: return tiling_square_triangle(EPoint);
		case 7: return tiling_octa_square(EPoint);
		case 6: return tiling_rectangle(EPoint);
		case 5: return tiling_rhombus(EPoint);
		case 4: return tiling_lozenge(EPoint);
		case 3: return tiling_triangle(EPoint);
		case 2: return tiling_hexagon(EPoint);
		case 1:
		default:
			return tiling_square(EPoint);
	}
}

/*****************************************************************************
*
* FUNCTION
*
*   tiling_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   J. Grimbert
*
* DESCRIPTION
*
*   Classical tiling patterns
*
* CHANGES
*
*
******************************************************************************/
static DBL pavement_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	switch(TPat->Vals.Pavement.Side)
	{
		case 6:
			return hexagonal(EPoint,TPat);

		case 4:
			return tetragonal(EPoint,TPat);

		case 3:
		default:
			return trigonal(EPoint,TPat);
	}
}

/*****************************************************************************
*
* FUNCTION
*
*   agate_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
*   Oct 1994    : adapted from agate pigment by [CY]
*
******************************************************************************/

static DBL agate_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator)
{
	register DBL noise, turb_val;
	const TURB* Turb;

	Turb=Search_For_Turb(TPat->Warps);

	turb_val = TPat->Vals.Agate_Turb_Scale * Turbulence(EPoint,Turb,noise_generator);

	noise = 0.5 * (cycloidal(1.3 * turb_val + 1.1 * EPoint[Z]) + 1.0);

	if (noise < 0.0)
	{
		noise = 0.0;
	}
	else
	{
		noise = min(1.0, noise);
		noise = pow(noise, 0.77);
	}

	return(noise);
}


/*****************************************************************************
*
* FUNCTION
*
*   boxed_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   -
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL boxed_pattern (const VECTOR EPoint)
{
	register DBL value;

	value = max(fabs(EPoint[X]), max(fabs(EPoint[Y]), fabs(EPoint[Z])));
	CLIP_DENSITY(value);

	return(value);
}


/*****************************************************************************
*
* FUNCTION
*
*   brick_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*   
* OUTPUT
*   
* RETURNS
*
*   DBL value exactly 0.0 or 1.0
*   
* AUTHOR
*
*   Dan Farmer
*   
* DESCRIPTION
*
* CHANGES
*
*   Oct 1994    : adapted from pigment by [CY]
*
******************************************************************************/

static DBL brick_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int ibrickx, ibricky, ibrickz;
	DBL brickheight, brickwidth, brickdepth;
	DBL brickmortar, mortarheight, mortarwidth, mortardepth;
	DBL brickx, bricky, brickz;
	DBL x, y, z, fudgit;

	fudgit=EPSILON+TPat->Vals.Brick.Mortar;

	x =  EPoint[X]+fudgit;
	y =  EPoint[Y]+fudgit;
	z =  EPoint[Z]+fudgit;

	brickwidth  = TPat->Vals.Brick.Size[X];
	brickheight = TPat->Vals.Brick.Size[Y];
	brickdepth  = TPat->Vals.Brick.Size[Z];
	brickmortar = (DBL)TPat->Vals.Brick.Mortar;

	mortarwidth  = brickmortar / brickwidth;
	mortarheight = brickmortar / brickheight;
	mortardepth  = brickmortar / brickdepth;

	/* 1) Check mortar layers in the X-Z plane (ie: top view) */

	bricky = y / brickheight;
	ibricky = (int) bricky;
	bricky -= (DBL) ibricky;

	if (bricky < 0.0)
	{
		bricky += 1.0;
	}

	if (bricky <= mortarheight)
	{
		return(0.0);
	}

	bricky = (y / brickheight) * 0.5;
	ibricky = (int) bricky;
	bricky -= (DBL) ibricky;

	if (bricky < 0.0)
	{
		bricky += 1.0;
	}


	/* 2) Check ODD mortar layers in the Y-Z plane (ends) */

	brickx = (x / brickwidth);
	ibrickx = (int) brickx;
	brickx -= (DBL) ibrickx;

	if (brickx < 0.0)
	{
		brickx += 1.0;
	}

	if ((brickx <= mortarwidth) && (bricky <= 0.5))
	{
		return(0.0);
	}

	/* 3) Check EVEN mortar layers in the Y-Z plane (ends) */

	brickx = (x / brickwidth) + 0.5;
	ibrickx = (int) brickx;
	brickx -= (DBL) ibrickx;

	if (brickx < 0.0)
	{
		brickx += 1.0;
	}

	if ((brickx <= mortarwidth) && (bricky > 0.5))
	{
		return(0.0);
	}

	/* 4) Check ODD mortar layers in the Y-X plane (facing) */

	brickz = (z / brickdepth);
	ibrickz = (int) brickz;
	brickz -= (DBL) ibrickz;

	if (brickz < 0.0)
	{
		brickz += 1.0;
	}

	if ((brickz <= mortardepth) && (bricky > 0.5))
	{
		return(0.0);
	}

	/* 5) Check EVEN mortar layers in the X-Y plane (facing) */

	brickz = (z / brickdepth) + 0.5;
	ibrickz = (int) brickz;
	brickz -= (DBL) ibrickz;

	if (brickz < 0.0)
	{
		brickz += 1.0;
	}

	if ((brickz <= mortardepth) && (bricky <= 0.5))
	{
		return(0.0);
	}

	/* If we've gotten this far, color me brick. */

	return(1.0);
}


/*****************************************************************************
*
* FUNCTION
*
*   cells_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   John VanSickle
*
* DESCRIPTION
*
*   "cells":
*
*   New colour function by John VanSickle,
*     vansickl@erols.com
*
*   Assigns a pseudorandom value to each unit cube.  The value for the cube in
*   which the evaluted point lies is returned.
*
*   All "cells" specific source code and examples are in the public domain.
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL cells_pattern (const VECTOR EPoint)
{
	/* select a random value based on the cube from which this came. */

	/* floor the values, instead of just truncating - this eliminates duplicated cells
	around the axes */

	return min(PatternRands(Hash3d((int)floor(EPoint[X]+EPSILON), (int)floor(EPoint[Y]+EPSILON), (int)floor(EPoint[Z]+EPSILON))), 1.0);
}


/*****************************************************************************
*
* FUNCTION
*
*   checker_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value exactly 0.0 or 1.0
*
* AUTHOR
*
*   POV-Team
*
* DESCRIPTION
*
* CHANGES
*   Oct 1994    : adapted from pigment by [CY]
*
******************************************************************************/

static DBL checker_pattern (const VECTOR EPoint)
{
	int value;

	value = (int)(floor(EPoint[X]+EPSILON) +
	              floor(EPoint[Y]+EPSILON) +
	              floor(EPoint[Z]+EPSILON));

	if (value & 1)
	{
		return (1.0);
	}
	else
	{
		return (0.0);
	}
}


/*****************************************************************************
*
* FUNCTION
*
*   crackle_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Jim McElhiney
*
* DESCRIPTION
*
*   "crackle":
*
*   New colour function by Jim McElhiney,
*     CompuServe 71201,1326, aka mcelhiney@acm.org
*
*   Large scale, without turbulence, makes a pretty good stone wall.
*   Small scale, without turbulence, makes a pretty good crackle ceramic glaze.
*   Highly turbulent (with moderate displacement) makes a good marble, solving
*   the problem of apparent parallel layers in Perlin's method.
*   2 octaves of full-displacement turbulence make a great "drizzled paint"
*   pattern, like a 1950's counter top.
*   Rule of thumb:  put a single colour transition near 0 in your colour map.
*
*   Mathematically, the set crackle(p)=0 is a 3D Voronoi diagram of a field of
*   semirandom points, and crackle(p)>0 is distance from set along shortest path.
*   (A Voronoi diagram is the locus of points equidistant from their 2 nearest
*   neighbours from a set of disjoint points, like the membranes in suds are
*   to the centres of the bubbles).
*
*   All "crackle" specific source code and examples are in the public domain.
*
* CHANGES
*   Oct 1994    : adapted from pigment by [CY]
*   Other changes: enhanced by Ron Parker, Integer math by Nathan Kopp
*
******************************************************************************/
static int IntPickInCube(int tvx, int tvy, int tvz, VECTOR  p1);

static DBL crackle_pattern (const VECTOR EPoint, const TPATTERN *TPat, TraceThreadData *Thread)
{
	DBL sum, minsum, minsum2, minsum3, tf;
	int minVecIdx = 0;
	VECTOR dv;

	int flox, floy, floz;

	DBL Metric = TPat->Vals.Crackle.Metric;
	DBL Offset = TPat->Vals.Crackle.Offset;

	bool UseSquare = ( Metric == 2);
	bool UseUnity  = ( Metric == 1);

	/*
	 * This uses floor() not FLOOR, so it will not be a mirror
	 * image about zero in the range -1.0 to 1.0. The viewer
	 * won't see an artefact around the origin.
	 */

	flox = (int)floor(EPoint[X] - EPSILON);
	floy = (int)floor(EPoint[Y] - EPSILON);
	floz = (int)floor(EPoint[Z] - EPSILON);

	/*
	 * Check to see if the input point is in the same unit cube as the last
	 * call to this function, to use cache of cubelets for speed.
	 */

	Crackle_Cell_Coord ccoord(flox, floy, floz);
	Thread->Stats()[CrackleCache_Tests]++;

	Crackle_Cache_Entry dummy_entry;
	Crackle_Cache_Entry* entry = &dummy_entry;

	// search for this hash value in the cache
	Crackle_Cache_Type::iterator iter = Thread->Crackle_Cache.find(ccoord);
	if (iter == Thread->Crackle_Cache.end())
	{
		/*
		 * No, not same unit cube.  Calculate the random points for this new
		 * cube and its 80 neighbours which differ in any axis by 1 or 2.
		 * Why distance of 2?  If there is 1 point in each cube, located
		 * randomly, it is possible for the closest random point to be in the
		 * cube 2 over, or the one two over and one up.  It is NOT possible
		 * for it to be two over and two up.  Picture a 3x3x3 cube with 9 more
		 * cubes glued onto each face.
		 */

		// generate a new cache entry, but only if the size of the cache is reasonable.
		// having to re-calculate entries that would have been cache hits had we not
		// skipped on adding an entry is less expensive than chewing up immense amounts
		// of RAM and finally hitting the swapfile. unfortunately there's no good way
		// to tell how much memory is 'too much' for the cache, so we just use a hard-
		// coded number for now (ideally we should allow the user to configure this).
		// keep in mind that the cache memory usage is per-thread, so the more threads,
		// the more RAM. if we don't do the insert, entry will point at a local variable.
		if (Thread->Crackle_Cache.size() * sizeof(Crackle_Cache_Type::value_type) < 30 * 1024 * 1024)
		{
			iter = Thread->Crackle_Cache.insert(Thread->Crackle_Cache.end(), Crackle_Cache_Type::value_type(ccoord, Crackle_Cache_Entry()));
			entry = &iter->second;
			entry->last_used = Thread->ProgressIndex();
		}

		// see InitializeCrackleCubes() below.
		int *pc = CrackleCubeTable;
		for (int i = 0; i < 81; i++, pc += 3)
			IntPickInCube(flox + pc[X], floy + pc[Y], floz + pc[Z], entry->data[i]);
	}
	else
	{
		Thread->Stats()[CrackleCache_Tests_Succeeded]++;
		entry = &iter->second;
	}

	// Find the 3 points with the 3 shortest distances from the input point.
	// Set up the loop so the invariant is true:  minsum <= minsum2 <= minsum3
	VSub(dv, entry->data[0], EPoint);

	if(UseSquare)
	{
		minsum = VSumSqr(dv);

		VSub(dv, entry->data[1], EPoint);
		minsum2 = VSumSqr(dv);

		VSub(dv, entry->data[2], EPoint);
		minsum3  = VSumSqr(dv);
	}
	else if(UseUnity)
	{
		minsum = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);

		VSub(dv, entry->data[1], EPoint);
		minsum2 = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);

		VSub(dv, entry->data[2], EPoint);
		minsum3 = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);
	}
	else
	{
		minsum = pow(fabs(dv[X]), Metric) +
		         pow(fabs(dv[Y]), Metric) +
		         pow(fabs(dv[Z]), Metric);

		VSub(dv, entry->data[1], EPoint);
		minsum2 = pow(fabs(dv[X]), Metric) +
		          pow(fabs(dv[Y]), Metric) +
		          pow(fabs(dv[Z]), Metric);

		VSub(dv, entry->data[2], EPoint);
		minsum3 = pow(fabs(dv[X]), Metric) +
		          pow(fabs(dv[Y]), Metric) +
		          pow(fabs(dv[Z]), Metric);
	}

	// sort the 3 computed sums
	if(minsum2 < minsum)
	{
		tf = minsum; minsum = minsum2; minsum2 = tf;
		minVecIdx = 1;
	}

	if(minsum3 < minsum)
	{
		tf = minsum; minsum = minsum3; minsum3 = tf;
		minVecIdx = 2;
	}

	if(minsum3 < minsum2)
	{
		tf = minsum2; minsum2 = minsum3; minsum3 = tf;
	}

	// Loop for the 81 cubelets to find closest and 2nd closest.
	for(int i = 3; i < 81; i++)
	{
		VSub(dv, entry->data[i], EPoint);

		if(UseSquare)
			sum  = VSumSqr(dv);
		else if(UseUnity)
			sum = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);
		else
			sum = pow(fabs(dv[X]), Metric) +
			      pow(fabs(dv[Y]), Metric) +
			      pow(fabs(dv[Z]), Metric);

		if(sum < minsum)
		{
			minsum3 = minsum2;
			minsum2 = minsum;
			minsum = sum;
			minVecIdx = i;
		}
		else if(sum < minsum2)
		{
			minsum3 = minsum2;
			minsum2 = sum;
		}
		else if( sum < minsum3 )
		{
			minsum3 = sum;
		}
	}

	if (Offset)
	{
		if(UseSquare)
		{
			minsum += Offset*Offset;
			minsum2 += Offset*Offset;
			minsum3 += Offset*Offset;
		}
		else if (UseUnity)
		{
			minsum += Offset;
			minsum2 += Offset;
			minsum3 += Offset;
		}
		else
		{
			minsum += pow( Offset, Metric );
			minsum2 += pow( Offset, Metric );
			minsum3 += pow( Offset, Metric );
		}
	}

	if(TPat->Vals.Crackle.IsSolid)
	{
		tf = Noise( entry->data[minVecIdx], GetNoiseGen(TPat, Thread) );
	}
	else if(UseSquare)
	{
		tf = TPat->Vals.Crackle.Form[X]*sqrt(minsum) +
		     TPat->Vals.Crackle.Form[Y]*sqrt(minsum2) +
		     TPat->Vals.Crackle.Form[Z]*sqrt(minsum3);
	}
	else if(UseUnity)
	{
		tf = TPat->Vals.Crackle.Form[X]*minsum +
		     TPat->Vals.Crackle.Form[Y]*minsum2 +
		     TPat->Vals.Crackle.Form[Z]*minsum3;
	}
	else
	{
		tf = TPat->Vals.Crackle.Form[X]*pow(minsum, 1.0/Metric) +
		     TPat->Vals.Crackle.Form[Y]*pow(minsum2, 1.0/Metric) +
		     TPat->Vals.Crackle.Form[Z]*pow(minsum3, 1.0/Metric);
	}

	return max(min(tf, 1.), 0.);
}


/*****************************************************************************
*
* FUNCTION
*
*   cylindrical_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   -
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL cylindrical_pattern (const VECTOR EPoint)
{
	register DBL value;

	value = sqrt(Sqr(EPoint[X]) + Sqr(EPoint[Z]));
	CLIP_DENSITY(value);

	return(value);
}


/*****************************************************************************
*
* FUNCTION
*
*   density_pattern
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Dieter Bayer
*
* DESCRIPTION
*
* CHANGES
*
*   Dec 1996 : Creation.
*
******************************************************************************/

inline float intp3(float t, float fa, float fb, float fc, float fd)
{
	float b,d,e,f;

	b = (fc - fa) * 0.5;
	d = (fd - fb) * 0.5;
	e = 2.0 * (fb - fc) + b + d;
	f = -3.0 * (fb - fc) - 2.0 * b - d;

	return ((e * t + f) * t + b) * t + fb;
}

inline float intp3_2(float t, float fa, float fb, float fc, float fd)
{
	float b,e,f;

	e = fd - fc - fa + fb;
	f = fa - fb - e;
	b = fc - fa;

	return ((e * t + f) * t + b) * t + fb;
}

#define zmax(i,imax) (((i)<0)?(imax-1):((i) % (imax)))

static DBL density_pattern(const VECTOR EPoint, const TPATTERN *TPat)
{
	size_t x, y, z;
	size_t x1, y1, z1;
	size_t x2, y2, z2;
	DBL Ex, Ey, Ez;
	DBL xx, yy, zz;
	DBL xi, yi;
	DBL f111, f112, f121, f122, f211, f212, f221, f222;
	float intpd2[4][4];
	DBL density = 0.0;
	DENSITY_FILE_DATA *Data;
	size_t k0, k1, k2, k3, i,j,ii,jj;

	Ex=EPoint[X];
	Ey=EPoint[Y];
	Ez=EPoint[Z];

	if((TPat->Vals.Density_File != NULL) && ((Data = TPat->Vals.Density_File->Data) != NULL) &&
	   (Data->Sx) && (Data->Sy) && (Data->Sz))
	{
/*		if(Data->Cyclic == true) 
		{
			Ex -= floor(Ex);
			Ey -= floor(Ey);
			Ez -= floor(Ez);
		}
*/
		if((Ex >= 0.0) && (Ex < 1.0) && (Ey >= 0.0) && (Ey < 1.0) && (Ez >= 0.0) && (Ez < 1.0))
		{
			switch (TPat->Vals.Density_File->Interpolation % 10)
			{
				case NO_INTERPOLATION:
					x = (size_t)(Ex * (DBL)Data->Sx);
					y = (size_t)(Ey * (DBL)Data->Sy);
					z = (size_t)(Ez * (DBL)Data->Sz);

					if ((x < 0) || (x >= Data->Sx) || (y < 0) || (y >= Data->Sy) || (z < 0) || (z >= Data->Sz))
						density = 0.0;
					else
					{
						if(Data->Type == 4)
							density = (DBL)Data->Density32[z * Data->Sy * Data->Sx + y * Data->Sx + x] / (DBL)UINT_MAX;
						else if(Data->Type==2)
							density = (DBL)Data->Density16[z * Data->Sy * Data->Sx + y * Data->Sx + x] / (DBL)USHRT_MAX;
						else if(Data->Type == 1)
							density = (DBL)Data->Density8[z * Data->Sy * Data->Sx + y * Data->Sx + x] / (DBL)UCHAR_MAX;
					}
					break;
				case TRILINEAR_INTERPOLATION:
					xx = Ex * (DBL)(Data->Sx );
					yy = Ey * (DBL)(Data->Sy );
					zz = Ez * (DBL)(Data->Sz );

					x1 = (size_t)xx;
					y1 = (size_t)yy;
					z1 = (size_t)zz;

					x2 = (x1 + 1) % Data->Sx;
					y2 = (y1 + 1) % Data->Sy;
					z2 = (z1 + 1) % Data->Sz;

					xx -= floor(xx);
					yy -= floor(yy);
					zz -= floor(zz);

					xi = 1.0 - xx;
					yi = 1.0 - yy;

					if(Data->Type == 4)
					{
						f111 = (DBL)Data->Density32[z1 * Data->Sy * Data->Sx + y1 * Data->Sx + x1] / (DBL)UINT_MAX;
						f112 = (DBL)Data->Density32[z1 * Data->Sy * Data->Sx + y1 * Data->Sx + x2] / (DBL)UINT_MAX;
						f121 = (DBL)Data->Density32[z1 * Data->Sy * Data->Sx + y2 * Data->Sx + x1] / (DBL)UINT_MAX;
						f122 = (DBL)Data->Density32[z1 * Data->Sy * Data->Sx + y2 * Data->Sx + x2] / (DBL)UINT_MAX;
						f211 = (DBL)Data->Density32[z2 * Data->Sy * Data->Sx + y1 * Data->Sx + x1] / (DBL)UINT_MAX;
						f212 = (DBL)Data->Density32[z2 * Data->Sy * Data->Sx + y1 * Data->Sx + x2] / (DBL)UINT_MAX;
						f221 = (DBL)Data->Density32[z2 * Data->Sy * Data->Sx + y2 * Data->Sx + x1] / (DBL)UINT_MAX;
						f222 = (DBL)Data->Density32[z2 * Data->Sy * Data->Sx + y2 * Data->Sx + x2] / (DBL)UINT_MAX;
					}
					else if(Data->Type == 2)
					{
						f111 = (DBL)Data->Density16[z1 * Data->Sy * Data->Sx + y1 * Data->Sx + x1] / (DBL)USHRT_MAX;
						f112 = (DBL)Data->Density16[z1 * Data->Sy * Data->Sx + y1 * Data->Sx + x2] / (DBL)USHRT_MAX;
						f121 = (DBL)Data->Density16[z1 * Data->Sy * Data->Sx + y2 * Data->Sx + x1] / (DBL)USHRT_MAX;
						f122 = (DBL)Data->Density16[z1 * Data->Sy * Data->Sx + y2 * Data->Sx + x2] / (DBL)USHRT_MAX;
						f211 = (DBL)Data->Density16[z2 * Data->Sy * Data->Sx + y1 * Data->Sx + x1] / (DBL)USHRT_MAX;
						f212 = (DBL)Data->Density16[z2 * Data->Sy * Data->Sx + y1 * Data->Sx + x2] / (DBL)USHRT_MAX;
						f221 = (DBL)Data->Density16[z2 * Data->Sy * Data->Sx + y2 * Data->Sx + x1] / (DBL)USHRT_MAX;
						f222 = (DBL)Data->Density16[z2 * Data->Sy * Data->Sx + y2 * Data->Sx + x2] / (DBL)USHRT_MAX;
					}
					else if(Data->Type == 1)
					{
						f111 = (DBL)Data->Density8[z1 * Data->Sy * Data->Sx + y1 * Data->Sx + x1] / (DBL)UCHAR_MAX;
						f112 = (DBL)Data->Density8[z1 * Data->Sy * Data->Sx + y1 * Data->Sx + x2] / (DBL)UCHAR_MAX;
						f121 = (DBL)Data->Density8[z1 * Data->Sy * Data->Sx + y2 * Data->Sx + x1] / (DBL)UCHAR_MAX;
						f122 = (DBL)Data->Density8[z1 * Data->Sy * Data->Sx + y2 * Data->Sx + x2] / (DBL)UCHAR_MAX;
						f211 = (DBL)Data->Density8[z2 * Data->Sy * Data->Sx + y1 * Data->Sx + x1] / (DBL)UCHAR_MAX;
						f212 = (DBL)Data->Density8[z2 * Data->Sy * Data->Sx + y1 * Data->Sx + x2] / (DBL)UCHAR_MAX;
						f221 = (DBL)Data->Density8[z2 * Data->Sy * Data->Sx + y2 * Data->Sx + x1] / (DBL)UCHAR_MAX;
						f222 = (DBL)Data->Density8[z2 * Data->Sy * Data->Sx + y2 * Data->Sx + x2] / (DBL)UCHAR_MAX;
					}

					density = ((f111 * xi + f112 * xx) * yi + (f121 * xi + f122 * xx) * yy) * (1.0 - zz) +
					          ((f211 * xi + f212 * xx) * yi + (f221 * xi + f222 * xx) * yy) * zz;
					break;
				case TRICUBIC_INTERPOLATION:
				default:
					xx = Ex * (DBL)(Data->Sx);
					yy = Ey * (DBL)(Data->Sy);
					zz = Ez * (DBL)(Data->Sz);

					x1 = (size_t)xx;
					y1 = (size_t)yy;
					z1 = (size_t)zz;

					xx -= floor(xx);
					yy -= floor(yy);
					zz -= floor(zz);

					k0 = zmax(-1+z1, Data->Sz );
					k1 = zmax(   z1, Data->Sz );
					k2 = zmax( 1+z1, Data->Sz );
					k3 = zmax( 2+z1, Data->Sz );

					if(Data->Type == 4)
					{
						for(i = 0; i < 4; i++)
						{
							ii = zmax(i + x1 - 1, Data->Sx);
							for(j = 0; j < 4; j++)
							{
								jj = zmax(j + y1 - 1, Data->Sy);
								intpd2[i][j] = intp3(zz,
								                     Data->Density32[k0 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UINT_MAX,
								                     Data->Density32[k1 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UINT_MAX,
								                     Data->Density32[k2 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UINT_MAX,
								                     Data->Density32[k3 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UINT_MAX);
							}
						}
					}
					else if(Data->Type == 2)
					{
						for(i = 0; i < 4; i++)
						{
							ii = zmax(i + x1 - 1, Data->Sx);
							for(j = 0; j < 4; j++)
							{
								jj = zmax(j + y1 - 1, Data->Sy);
								intpd2[i][j] = intp3(zz,
								                     Data->Density16[k0 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)USHRT_MAX,
								                     Data->Density16[k1 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)USHRT_MAX,
								                     Data->Density16[k2 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)USHRT_MAX,
								                     Data->Density16[k3 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)USHRT_MAX);
							}
						}
					}
					else if(Data->Type == 1)
					{
						for(i = 0; i < 4; i++)
						{
							ii = zmax(i + x1 - 1, Data->Sx);
							for(j = 0; j < 4; j++)
							{
								jj = zmax(j + y1 - 1, Data->Sy);
								intpd2[i][j] = intp3(zz,
								                     Data->Density8[k0 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UCHAR_MAX,
								                     Data->Density8[k1 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UCHAR_MAX,
								                     Data->Density8[k2 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UCHAR_MAX,
								                     Data->Density8[k3 * Data->Sy * Data->Sx + jj * Data->Sx + ii] / (DBL)UCHAR_MAX);
							}
						}
					}

					for(i = 0; i < 4; i++)
						intpd2[0][i] = intp3(yy, intpd2[i][0], intpd2[i][1],  intpd2[i][2], intpd2[i][3]);

					density = intp3(xx, intpd2[0][0], intpd2[0][1], intpd2[0][2], intpd2[0][3]);
					break;
			}
		}
		else
			density = 0.0;
	}

	if (density < 0.0)
		density = 0.0;
	return density;
}


/*****************************************************************************
*
* FUNCTION
*
*   dents_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*   
* AUTHOR
*
*   POV-Ray Team
*   
* DESCRIPTION   : Note this pattern is only used for pigments and textures.
*                 Normals have a specialized pattern for this.
*
* CHANGES
*   Nov 1994 : adapted from normal by [CY]
*
******************************************************************************/

static DBL dents_pattern (const VECTOR EPoint, int noise_generator)
{
	DBL noise;

	noise = Noise (EPoint, noise_generator);

	return(noise * noise * noise);
}


/*****************************************************************************
*
* FUNCTION
*
*   function_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   
* OUTPUT
*   
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*   
* AUTHOR
*
*   POV-Ray Team
*   
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static DBL function_pattern (const VECTOR EPoint, const TPATTERN *TPat, TraceThreadData *Thread)
{
	DBL value;

	if(Thread->functionPatternContext[TPat->Vals.Function.Data] == NULL)
		Thread->functionPatternContext[TPat->Vals.Function.Data] = Thread->functionContext->functionvm->NewContext(const_cast<TraceThreadData *>(Thread));

	FPUContext *ctx = Thread->functionPatternContext[TPat->Vals.Function.Data];

	ctx->SetLocal(X, EPoint[X]);
	ctx->SetLocal(Y, EPoint[Y]);
	ctx->SetLocal(Z, EPoint[Z]);

	value = POVFPU_Run(ctx, *(reinterpret_cast<const FUNCTION*>(TPat->Vals.Function.Fn)));

	return ((value > 1.0) ? fmod(value, 1.0) : value);
}


/*****************************************************************************
*
* FUNCTION
*
*   gradient_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   
* OUTPUT
*   
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*   
* AUTHOR
*
*   POV-Ray Team
*   
* DESCRIPTION
*
*   Gradient Pattern - gradient based on the fractional values of
*   x, y or z, based on whether or not the given directional vector is
*   a 1.0 or a 0.0.
*   The basic concept of this is from DBW Render, but Dave Wecker's
*   only supports simple Y axis gradients.
*
* CHANGES
*
*   Oct 1994    : adapted from pigment by [CY]
*
******************************************************************************/

static DBL gradient_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	DBL Result;
	VDot( Result, EPoint, TPat->Vals.Gradient );

	/* Mod to keep within [0.0,1.0] range */
	return ((Result > 1.0) ? fmod(Result, 1.0) : Result);
}


/*****************************************************************************
*
* FUNCTION
*
*   granite_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   
* OUTPUT
*   
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*   
* AUTHOR
*
*   POV-Ray Team
*   
* DESCRIPTION
*
*   Granite - kind of a union of the "spotted" and the "dented" textures,
*   using a 1/f fractal noise function for color values. Typically used
*   with small scaling values. Should work with colour maps for pink granite.
*
* CHANGES
*
*   Oct 1994    : adapted from pigment by [CY]
*
******************************************************************************/

static DBL granite_pattern (const VECTOR EPoint, int noise_generator)
{
	register int i;
	register DBL temp, noise = 0.0, freq = 1.0;
	VECTOR tv1,tv2;

	VScale(tv1,EPoint,4.0);

	for (i = 0; i < 6 ; freq *= 2.0, i++)
	{
		VScale(tv2,tv1,freq);
		if(noise_generator==1)
		{
			temp = 0.5 - Noise (tv2, noise_generator);
			temp = fabs(temp);
		}
		else
		{
			temp = 1.0 - 2.0 * Noise (tv2, noise_generator);
			temp = fabs(temp);
			if (temp>0.5) temp=0.5;
		}

		noise += temp / freq;
	}

	return(noise);
}


/*****************************************************************************
*
* FUNCTION
*
*   hexagon_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value exactly 0.0, 1.0 or 2.0
*
* AUTHOR
*
*   Ernest MacDougal Campbell III
*   
* DESCRIPTION
*
*   TriHex pattern -- Ernest MacDougal Campbell III (EMC3) 11/23/92
*
*   Creates a hexagon pattern in the XZ plane.
*
*   This algorithm is hard to explain.  First it scales the point to make
*   a few of the later calculations easier, then maps some points to be
*   closer to the Origin.  A small area in the first quadrant is subdivided
*   into a 6 x 6 grid.  The position of the point mapped into that grid
*   determines its color.  For some points, just the grid location is enough,
*   but for others, we have to calculate which half of the block it's in
*   (this is where the atan2() function comes in handy).
*
* CHANGES
*
*   Nov 1992 : Creation.
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

const DBL xfactor = 0.5;         /* each triangle is split in half for the grid */
const DBL zfactor = 0.866025404; /* sqrt(3)/2 -- Height of an equilateral triangle */

static DBL hexagon_pattern (const VECTOR EPoint)
{
	int xm, zm;
	int brkindx;
	DBL xs, zs, xl, zl, value = 0.0;
	DBL x=EPoint[X];
	DBL z=EPoint[Z];


	/* Keep all numbers positive.  Also, if z is negative, map it in such a
	 * way as to avoid mirroring across the x-axis.  The value 5.196152424
	 * is (sqrt(3)/2) * 6 (because the grid is 6 blocks high)
	 */

	x = fabs(x);

	/* Avoid mirroring across x-axis. */

	z = z < 0.0 ? 5.196152424 - fabs(z) : z;

	/* Scale point to make calcs easier. */

	xs = x/xfactor;
	zs = z/zfactor;

	/* Map points into the 6 x 6 grid where the basic formula works. */

	xs -= floor(xs/6.0) * 6.0;
	zs -= floor(zs/6.0) * 6.0;

	/* Get a block in the 6 x 6 grid. */

	xm = (int) FLOOR(xs) % 6;
	zm = (int) FLOOR(zs) % 6;

	switch (xm)
	{
		/* These are easy cases: Color depends only on xm and zm. */

		case 0:
		case 5:

			switch (zm)
			{
				case 0:
				case 5: value = 0; break;

				case 1:
				case 2: value = 1; break;

				case 3:
				case 4: value = 2; break;
			}

			break;

		case 2:
		case 3:

			switch (zm)
			{
				case 0:
				case 1: value = 2; break;

				case 2:
				case 3: value = 0; break;

				case 4:
				case 5: value = 1; break;
			}

			break;

		/* These cases are harder.  These blocks are divided diagonally
		 * by the angled edges of the hexagons.  Some slope positive, and
		 * others negative.  We flip the x value of the negatively sloped
		 * pieces.  Then we check to see if the point in question falls
		 * in the upper or lower half of the block.  That info, plus the
		 * z status of the block determines the color.
		 */

		case 1:
		case 4:

			/* Map the point into the block at the origin. */

			xl = xs-xm;
			zl = zs-zm;

			/* These blocks have negative slopes so we flip it horizontally. */

			if (((xm + zm) % 2) == 1)
			{
				xl = 1.0 - xl;
			}

			/* Avoid a divide-by-zero error. */

			if (xl == 0.0)
			{
				xl = 0.0001; // TODO FIXME - magic number! Should be SOME_EPSILON I guess, or use nextafter()
			}

			/* Is the angle less-than or greater-than 45 degrees? */

			brkindx = (zl / xl) < 1.0;

			/* was...
			 * brkindx = (atan2(zl,xl) < (45 * M_PI_180));
			 * ...but because of the mapping, it's easier and cheaper,
			 * CPU-wise, to just use a good ol' slope.
			 */

			switch (brkindx)
			{
				case true:

					switch (zm)
					{
						case 0:
						case 3: value = 0; break;

						case 2:
						case 5: value = 1; break;

						case 1:
						case 4: value = 2; break;
					}

					break;

				case false:

					switch (zm)
					{
						case 0:
						case 3: value = 2; break;

						case 2:
						case 5: value = 0; break;

						case 1:
						case 4: value = 1; break;
					}

					break;
			}
	}

	value = fmod(value, 3.0);

	return(value);
}


/*****************************************************************************
*
* FUNCTION
*
*   cubic_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value exactly 0.0, 1.0, 2.0, 3.0, 4.0 or 5.0
*
* AUTHOR
*
*   Nieminen Juha
*   
* DESCRIPTION
*
*   Creates a cubic pattern. The six texture elements are mapped to the six
*   pyramids centered on the six axes.
*
* CHANGES
*
*   Nov 2007 : Creation.
*
******************************************************************************/

static DBL cubic_pattern (const VECTOR EPoint)
{
	const DBL x = EPoint[X], y = EPoint[Y], z = EPoint[Z];
	const DBL ax = fabs(x), ay = fabs(y), az = fabs(z);

	if(x >= 0 && x >= ay && x >= az) return 0.0;
	if(y >= 0 && y >= ax && y >= az) return 1.0;
	if(z >= 0 && z >= ax && z >= ay) return 2.0;
	if(x < 0 && x <= -ay && x <= -az) return 3.0;
	if(y < 0 && y <= -ax && y <= -az) return 4.0;
	return 5.0;
}

/*****************************************************************************
*
* FUNCTION
*
*   square_pattern 
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value exactly 0.0, 1.0, 2.0 or 3.0
*
* AUTHOR
*
*   J. Grimbert
*
* DESCRIPTION
*   Paving the XZ plan with 4 'colours', in square
*
* CHANGES
*
******************************************************************************/

static DBL square_pattern (const VECTOR EPoint)
{
	int valueX,valueZ;

	valueX = (int)(floor(EPoint[X]));
	valueZ = (int)(floor(EPoint[Z]));

	if (valueX & 1)
	{
		if (valueZ & 1)
		{
			return (2.0);
		}
		else
		{
			return (3.0);
		}
	}
	else
	{
		if (valueZ & 1)
		{
			return (1.0);
		}
		else
		{
			return (0.0);
		}
	}
}

/*****************************************************************************
*
* FUNCTION
*
*   triangular_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value exactly 0.0, 1.0, 2.0, 3.0, 4.0 or 5.0
*
* AUTHOR
*
*   J. Grimbert
*
* DESCRIPTION
*   Paving the XZ plan with 6 'colours', in triangle around the origin
*
* CHANGES
*
******************************************************************************/

static DBL triangular_pattern (const VECTOR EPoint)
{
	DBL answer;
	DBL x,z;
	DBL xs,zs;
	int a,b;
	DBL k,slop1,slop2;
	int mask;

	x=EPoint[X];
	z=EPoint[Z];
  /* Fold the space to a basic rectangle */
	xs = x-3.0*floor(x/3.0);
	zs = z-SQRT3*floor(z/SQRT3);

	/* xs,zs is in { [0.0, 3.0 [, [0.0, SQRT3 [ } 
	 ** but there is some symmetry to simplify the testing
	 */

	a = (int)floor(xs);
	xs -= a;
	b = (zs <SQRT3_2 ? 0: 1);
	if (b)
	{
		zs = SQRT3 - zs; /* mirror */
	}

	k = 1.0 - xs;
	if ((xs != 0.0)&&( k != 0.0 )) /* second condition should never occurs */
	{
		slop1 = zs/xs;
		slop2 = zs/k; /* just in case */
		switch( (slop1<SQRT3?1:0)+(slop2<SQRT3?2:0))
		{
			case 3:
				answer = 0.0;
				break;
			case 2:
				answer = 1.0;
				break;
			case 1:
				answer = 3.0;
				break;
		}
	}
	else
	{
		answer = 1.0;
	}
	mask = (int) answer;
	answer = (mask & 1) ? fmod(answer+2.0*a,6.0): fmod(6.0+answer-2.0*a,6.0);
	if (b)
	{
		answer = 5.0 - answer;
	}

	return answer;
}




/*****************************************************************************
*
* FUNCTION
*
*   julia_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL julia_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, dist2, mindist2,
	    cr = TPat->Vals.Fractal.Coord[U], ci = TPat->Vals.Fractal.Coord[V];

	a = EPoint[X]; a2 = Sqr(a);
	b = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		b  = 2.0 * a * b + ci;
		a  = a2 - b2 + cr;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   julia3_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL julia3_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, dist2, mindist2,
	    cr = TPat->Vals.Fractal.Coord[U], ci = TPat->Vals.Fractal.Coord[V];

	a = EPoint[X]; a2 = Sqr(a);
	b = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		b = 3.0*a2*b - b2*b + ci;
		a = a2*a - 3.0*a*b2 + cr;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   julia4_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL julia4_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, dist2, mindist2,
	    cr = TPat->Vals.Fractal.Coord[U], ci = TPat->Vals.Fractal.Coord[V];

	a = EPoint[X]; a2 = Sqr(a);
	b = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		b = 4.0 * (a2*a*b - a*b2*b) + ci;
		a = a2*a2 - 6.0*a2*b2 + b2*b2 + cr;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   juliax_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL juliax_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col, exponent;
	DBL a, b, cf=0, x, y, dist2, mindist2,
	    cr = TPat->Vals.Fractal.Coord[U], ci = TPat->Vals.Fractal.Coord[V];
	int* binomial_coeff;

	a = x = EPoint[X];
	b = y = EPoint[Y];
	mindist2 = a*a+b*b;

	it_max = TPat->Vals.Fractal.Iterations;
	exponent = TPat->Vals.Fractal.Exponent;

	binomial_coeff = &BinomialCoefficients[(exponent+1)*exponent/2];

	for (col = 0; col < it_max; col++)
	{
		// Calculate (a+bi)^exponent
		DBL new_a = pow(a, exponent);
		for(int k=2; k<=exponent; k+=2)
		{
			new_a += binomial_coeff[k]*pow(a, exponent-k)*pow(b, k);
		}
		DBL new_b = 0;
		for(int l=1; l<=exponent; l+=2)
		{
			new_b += binomial_coeff[l]*pow(a, exponent-l)*pow(b, l);
		}

		a = new_a + cr;
		b = new_b + ci;

		dist2 = a*a+b*b;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   leopard_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Scott Taylor
*
* DESCRIPTION
*
* CHANGES
*
*   Jul 1991 : Creation.
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL leopard_pattern (const VECTOR EPoint)
{
	register DBL value, temp1, temp2, temp3;

	/* This form didn't work with Zortech 386 compiler */
	/* value = Sqr((sin(x)+sin(y)+sin(z))/3); */
	/* So we break it down. */

	temp1 = sin(EPoint[X]);
	temp2 = sin(EPoint[Y]);
	temp3 = sin(EPoint[Z]);

	value = Sqr((temp1 + temp2 + temp3) / 3.0);

	return(value);
}


/*****************************************************************************
*
* FUNCTION
*
*   magnet1m_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL magnet1m_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, x, y, tmp, tmp1r, tmp1i, tmp2r, tmp2i, dist2, mindist2;

	x = EPoint[X];
	y = EPoint[Y];
	a = a2 = 0;
	b = b2 = 0;
	mindist2 = 10000;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		tmp1r = a2-b2 + x-1;
		tmp1i = 2*a*b + y;
		tmp2r = 2*a + x-2;
		tmp2i = 2*b + y;
		tmp = tmp2r*tmp2r + tmp2i*tmp2i;
		a = (tmp1r*tmp2r + tmp1i*tmp2i) / tmp;
		b = (tmp1i*tmp2r - tmp1r*tmp2i) / tmp;
		b2 = b*b;
		b = 2*a*b;
		a = a*a-b2;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		tmp1r = a-1;
		if(dist2 > 10000.0 || tmp1r*tmp1r+b2 < 1/10000.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   magnet1j_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL magnet1j_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, tmp, tmp1r, tmp1i, tmp2r, tmp2i, dist2, mindist2,
	    cr = TPat->Vals.Fractal.Coord[U], ci = TPat->Vals.Fractal.Coord[V];

	a = EPoint[X]; a2 = Sqr(a);
	b = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		tmp1r = a2-b2 + cr-1;
		tmp1i = 2*a*b + ci;
		tmp2r = 2*a + cr-2;
		tmp2i = 2*b + ci;
		tmp = tmp2r*tmp2r + tmp2i*tmp2i;
		a = (tmp1r*tmp2r + tmp1i*tmp2i) / tmp;
		b = (tmp1i*tmp2r - tmp1r*tmp2i) / tmp;
		b2 = b*b;
		b = 2*a*b;
		a = a*a-b2;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		tmp1r = a-1;
		if(dist2 > 10000.0 || tmp1r*tmp1r+b2 < 1/10000.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   magnet2m_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL magnet2m_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, x, y, tmp, tmp1r, tmp1i, tmp2r, tmp2i,
	    c1r, c2r, c1c2r, c1c2i, dist2, mindist2;

	x = EPoint[X];
	y = EPoint[Y];
	a = a2 = 0;
	b = b2 = 0;
	mindist2 = 10000;

	c1r = x-1; c2r = x-2;
	c1c2r = c1r*c2r-y*y;
	c1c2i = (c1r+c2r)*y;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		tmp1r = a2*a-3*a*b2 + 3*(a*c1r-b*y) + c1c2r;
		tmp1i = 3*a2*b-b2*b + 3*(a*y+b*c1r) + c1c2i;
		tmp2r = 3*(a2-b2) + 3*(a*c2r-b*y) + c1c2r + 1;
		tmp2i = 6*a*b + 3*(a*y+b*c2r) + c1c2i;
		tmp = tmp2r*tmp2r + tmp2i*tmp2i;
		a = (tmp1r*tmp2r + tmp1i*tmp2i) / tmp;
		b = (tmp1i*tmp2r - tmp1r*tmp2i) / tmp;
		b2 = b*b;
		b = 2*a*b;
		a = a*a-b2;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		tmp1r = a-1;
		if(dist2 > 10000.0 || tmp1r*tmp1r+b2 < 1/10000.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   magnet2j_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL magnet2j_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, tmp, tmp1r, tmp1i, tmp2r, tmp2i, c1r,c2r,c1c2r,c1c2i,
	    cr = TPat->Vals.Fractal.Coord[U], ci = TPat->Vals.Fractal.Coord[V],
	    dist2, mindist2;

	a = EPoint[X]; a2 = Sqr(a);
	b = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	c1r = cr-1, c2r = cr-2;
	c1c2r = c1r*c2r-ci*ci;
	c1c2i = (c1r+c2r)*ci;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		tmp1r = a2*a-3*a*b2 + 3*(a*c1r-b*ci) + c1c2r;
		tmp1i = 3*a2*b-b2*b + 3*(a*ci+b*c1r) + c1c2i;
		tmp2r = 3*(a2-b2) + 3*(a*c2r-b*ci) + c1c2r + 1;
		tmp2i = 6*a*b + 3*(a*ci+b*c2r) + c1c2i;
		tmp = tmp2r*tmp2r + tmp2i*tmp2i;
		a = (tmp1r*tmp2r + tmp1i*tmp2i) / tmp;
		b = (tmp1i*tmp2r - tmp1r*tmp2i) / tmp;
		b2 = b*b;
		b = 2*a*b;
		a = a*a-b2;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		tmp1r = a-1;
		if(dist2 > 10000.0 || tmp1r*tmp1r+b2 < 1/10000.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   mandel_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   submitted by user, name lost (sorry)
*
* DESCRIPTION
*
*   The mandel pattern computes the standard Mandelbrot fractal pattern and
*   projects it onto the X-Y plane.  It uses the X and Y coordinates to compute
*   the Mandelbrot set.
*
* CHANGES
*
*   Oct 1994 : adapted from pigment by [CY]
*   May 2001 : updated with code from Warp [trf]
*
******************************************************************************/

static DBL mandel_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, x, y, dist2, mindist2;

	a = x = EPoint[X]; a2 = Sqr(a);
	b = y = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		b  = 2.0 * a * b + y;
		a  = a2 - b2 + x;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   mandel3_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL mandel3_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, x, y, dist2, mindist2;

	a = x = EPoint[X]; a2 = Sqr(a);
	b = y = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		b = 3.0*a2*b - b2*b + y;
		a = a2*a - 3.0*a*b2 + x;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   mandel4_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL mandel4_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col;
	DBL a, b, cf, a2, b2, x, y, dist2, mindist2;

	a = x = EPoint[X]; a2 = Sqr(a);
	b = y = EPoint[Y]; b2 = Sqr(b);
	mindist2 = a2+b2;

	it_max = TPat->Vals.Fractal.Iterations;

	for (col = 0; col < it_max; col++)
	{
		b = 4.0 * (a2*a*b - a*b2*b) + y;
		a = a2*a2 - 6.0*a2*b2 + b2*b2 + x;

		a2 = Sqr(a);
		b2 = Sqr(b);
		dist2 = a2+b2;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   mandelx_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Nieminen Juha
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL mandelx_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	int it_max, col, exponent;
	DBL a, b, cf=0, x, y, dist2, mindist2;
	int* binomial_coeff;

	a = x = EPoint[X];
	b = y = EPoint[Y];
	mindist2 = a*a+b*b;

	it_max = TPat->Vals.Fractal.Iterations;
	exponent = TPat->Vals.Fractal.Exponent;

	binomial_coeff = &BinomialCoefficients[(exponent+1)*exponent/2];

	for (col = 0; col < it_max; col++)
	{
		// Calculate (a+bi)^exponent
		DBL new_a = pow(a, exponent);
		for(int k=2; k<=exponent; k+=2)
		{
			new_a += binomial_coeff[k]*pow(a, exponent-k)*pow(b, k);
		}
		DBL new_b = 0;
		for(int l=1; l<=exponent; l+=2)
		{
			new_b += binomial_coeff[l]*pow(a, exponent-l)*pow(b, l);
		}

		a = new_a + x;
		b = new_b + y;

		dist2 = a*a+b*b;

		if(dist2 < mindist2) mindist2 = dist2;
		if(dist2 > 4.0)
		{
			cf = fractal_exterior_color(TPat, col, a, b);
			break;
		}
	}

	if(col == it_max)
		cf = fractal_interior_color(TPat, col, a, b, mindist2);

	return(cf);
}


/*****************************************************************************
*
* FUNCTION
*
*   marble_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL marble_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator)
{
	register DBL turb_val;
	const TURB *Turb;

	if ((Turb=Search_For_Turb(TPat->Warps)) != NULL)
	{
		turb_val = Turb->Turbulence[X] * Turbulence(EPoint,Turb,noise_generator);
	}
	else
	{
		turb_val = 0.0;
	}

	return(EPoint[X] + turb_val);
}


/*****************************************************************************
*
* FUNCTION
*
*   object_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static DBL object_pattern (const VECTOR EPoint, const TPATTERN *TPat, TraceThreadData *Thread)
{
	if(TPat->Vals.Object != NULL)
	{
		if(Inside_Object(EPoint, TPat->Vals.Object, Thread))
			return 1.0;
		else
			return 0.0;
	}

	return 0.0;
}

/*****************************************************************************
*
* FUNCTION
*
*   onion_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Scott Taylor
*
* DESCRIPTION
*
* CHANGES
*
*   Jul 1991 : Creation.
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL onion_pattern (const VECTOR EPoint)
{
	/* The variable noise is not used as noise in this function */

	register DBL noise;

/*
	 This ramp goes 0-1,1-0,0-1,1-0...

	 noise = (fmod(sqrt(Sqr(x)+Sqr(y)+Sqr(z)),2.0)-1.0);

	 if (noise<0.0) {noise = 0.0-noise;}
*/

	/* This ramp goes 0-1, 0-1, 0-1, 0-1 ... */

	noise = (fmod(sqrt(Sqr(EPoint[X])+Sqr(EPoint[Y])+Sqr(EPoint[Z])), 1.0));

	return(noise);
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR 
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static DBL pigment_pattern (const VECTOR EPoint, const TPATTERN *TPat, const Intersection *isect, const Ray *ray, TraceThreadData *Thread)
{
	DBL value;
	Colour Col;
	int colour_found=false;

	if (TPat->Vals.Pigment)
		// TODO ALPHA - we're discarding transparency information, so maybe we want to pre-multiply if there's alpha in there?
		colour_found = Compute_Pigment(Col, TPat->Vals.Pigment, EPoint, isect, ray, Thread);

	if(!colour_found)
		value = 0.0;
	else
		value = Col.greyscale();

	return value ;
}


/*****************************************************************************
*
* FUNCTION
*
*   planar_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   -
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL planar_pattern (const VECTOR EPoint)
{
	register DBL value = fabs(EPoint[Y]);

	CLIP_DENSITY(value);

	return value;
}


/*****************************************************************************
*
* FUNCTION
*
*   quilted_pattern
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*   
* AUTHOR
*
*   Dan Farmer & Chris Young
*   
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static DBL quilted_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	VECTOR value;
	DBL t;

	value[X] = EPoint[X]-FLOOR(EPoint[X])-0.5;
	value[Y] = EPoint[Y]-FLOOR(EPoint[Y])-0.5;
	value[Z] = EPoint[Z]-FLOOR(EPoint[Z])-0.5;

	t = sqrt(value[X]*value[X]+value[Y]*value[Y]+value[Z]*value[Z]);

	t = quilt_cubic(t, TPat->Vals.Quilted.Control0, TPat->Vals.Quilted.Control1);

	value[X] *= t;
	value[Y] *= t;
	value[Z] *= t;

	return((fabs(value[X])+fabs(value[Y])+fabs(value[Z]))/3.0);
}


/*****************************************************************************
*
* FUNCTION
*
*   radial_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Chris Young -- new in vers 2.0
*
* DESCRIPTION
*
* CHANGES
*
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL radial_pattern (const VECTOR EPoint)
{
	register DBL value;

	if ((fabs(EPoint[X])<0.001) && (fabs(EPoint[Z])<0.001))
	{
		value = 0.25;
	}
	else
	{
		value = 0.25 + (atan2(EPoint[X],EPoint[Z]) + M_PI) / TWO_M_PI;
	}

	return(value);
}


/*****************************************************************************
*
* FUNCTION
*
*   ripples_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*   
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*   
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION   : Note this pattern is only used for pigments and textures.
*                 Normals have a specialized pattern for this.
*
* CHANGES
*
*   Nov 1994 : adapted from normal by [CY]
*
******************************************************************************/

static DBL ripples_pattern (const VECTOR EPoint, const TPATTERN *TPat, const TraceThreadData *Thread)
{
	register unsigned int i;
	register DBL length, index;
	DBL scalar =0.0;
	VECTOR point;

	for (i = 0 ; i < Thread->numberOfWaves ; i++)
	{
		VSub (point, EPoint, *Thread->waveSources[i]);
		VLength (length, point);

		if (length == 0.0)
			length = 1.0;

		index = length * TPat->Frequency + TPat->Phase;

		scalar += cycloidal(index);
	}

	scalar = 0.5*(1.0+(scalar / (DBL)Thread->numberOfWaves));

	return(scalar);
}


/*****************************************************************************
*
* FUNCTION
*
*   slope_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*             is evaluated.
*   TPat   -- Texture pattern struct
*   Intersection - intersection struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0, 0.0 if normal is NULL
*
* AUTHOR
*
*   -hdf-
*
* DESCRIPTION   :
*
*   calculates the surface slope from surface normal vector
*
* CHANGES
*
*   Apr 1998 : written by H.-D. Fink
*   May 1998 : modified by M.C. Andrews - now combines slope and 'gradient'.
*
******************************************************************************/

static DBL slope_pattern (const VECTOR EPoint, const TPATTERN *TPat, const Intersection *Isection)
{
	DBL value, value1, value2;

	if (Isection == NULL) return 0.0; /* just in case ... */

	if (TPat->Vals.Slope.Point_At)
	{
		VECTOR vect;
		VSub(vect,TPat->Vals.Slope.Slope_Vector,Isection->IPoint);
		VNormalizeEq(vect);
		VDot(value1, Isection->PNormal, vect);
	}
	else
	{
		if (TPat->Vals.Slope.Slope_Base > 0)
			/* short case 1: slope vector in x, y or z direction */
			value1 = Isection->PNormal[TPat->Vals.Slope.Slope_Base - 1];
		else if (TPat->Vals.Slope.Slope_Base < 0)
			/* short case 2: slope vector in negative x, y or z direction */
			value1 = -Isection->PNormal[-TPat->Vals.Slope.Slope_Base - 1];
		else
			/* projection slope onto normal vector */
			VDot(value1, Isection->PNormal, TPat->Vals.Slope.Slope_Vector);
	}

	/* Clamp to 1.0. */
	/* should never be necessary since both vectors are normalized */
	if      (value1 >  1.0) value1 =  1.0;
	else if (value1 < -1.0) value1 = -1.0;

	value1 = asin(value1) / M_PI * 2;
	value1 = (value1 + 1.0) * 0.5;        /* normalize to [0..1] interval */

	/* If set, use offset and scalings for slope and altitude. */
	if (0.0 != TPat->Vals.Slope.Slope_Mod[V])
	{
		value1 = (value1 - TPat->Vals.Slope.Slope_Mod[U]) / TPat->Vals.Slope.Slope_Mod[V];
	}

	if (!TPat->Vals.Slope.Altit_Len)
	{
		/* Clamp to 1.0. */
		if ( value1 == 1.0 )
		{
			value1= value1- EPSILON;
		}
		else
		{
			value1 = (value1 < 0.0) ? 1.0 + fmod(value1, 1.0) : fmod(value1, 1.0);
		}
		return value1; /* no altitude defined */
	}

	/* Calculate projection of Epoint along altitude vector */
	if (TPat->Vals.Slope.Altit_Base > 0)
		/* short case 1: altitude vector in x, y or z direction */
		value2 = EPoint[TPat->Vals.Slope.Altit_Base - 1];
	else if (TPat->Vals.Slope.Altit_Base < 0)
		/* short case 2: altitude vector in negative x, y or z direction */
		value2 = -EPoint[-TPat->Vals.Slope.Altit_Base - 1];
	else
		/* projection of Epoint along altitude vector */
		VDot(value2, EPoint, TPat->Vals.Slope.Altit_Vector);

	if (0.0 != TPat->Vals.Slope.Altit_Mod[V])
	{
		value2 = (value2 - TPat->Vals.Slope.Altit_Mod[U]) / TPat->Vals.Slope.Altit_Mod[V];
	}

	value = TPat->Vals.Slope.Slope_Len * value1 + TPat->Vals.Slope.Altit_Len * value2;

	/* Clamp to 1.0. */
	if ( value - 1.0 < EPSILON && value >= 1.0 )
	{
		/* 1.0 is a very common value to get *exactly*.  We don't want to wrap
		   it to the bottom end of the map. */
		value = value - EPSILON;
	}
	else
	{
		value = (value < 0.0) ? 1.0 + fmod(value, 1.0) : fmod(value, 1.0);
	}
	return value;

}


/*****************************************************************************
*
* FUNCTION
*
*   aoi_pattern
*
* INPUT
*
*   Intersection - intersection struct
*   Ray          - Ray information
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   J. Grimbert
*
* DESCRIPTION
*
*  Return a value related to angle of incidence
*   (angle between the normal at the intersection and the ray)
*
* CHANGES
*
*   Mar 2010 : modified by [CLi]
*
******************************************************************************/

static DBL aoi_pattern (const Intersection *Isection, const Ray *ray)
{
	VECTOR    a, b;
	DBL       cosAngle, angle;

	if ((Isection == NULL) || (ray == NULL))
		return 0.0;

	VNormalize(a, Isection->PNormal);
	VNormalize(b, ray->Direction);
	VDot(cosAngle, a, b);

	// clip to [-1.0; 1.0], just to be sure
	// (should never be necessary since both vectors are normalized)
	cosAngle = clip(cosAngle, -1.0, 1.0);
	angle = acos(cosAngle) / M_PI;

	return angle;
}


/*****************************************************************************
*
* FUNCTION
*
*   spiral1_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Dieter Bayer
*
* DESCRIPTION
*   Spiral whirles around z-axis.
*   The number of "arms" is defined in the TPat.
*
* CHANGES
*
*   Aug 1994 : Creation.
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL spiral1_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator)
{
	DBL rad, phi, turb_val;
	DBL x = EPoint[X];
	DBL y = EPoint[Y];
	DBL z = EPoint[Z];
	const TURB *Turb;

	if ((Turb=Search_For_Turb(TPat->Warps)) != NULL)
	{
		turb_val = Turb->Turbulence[X] * Turbulence(EPoint,Turb,noise_generator);
	}
	else
	{
		turb_val = 0.0;
	}

	/* Get distance from z-axis. */

	rad = sqrt(x * x + y * y);

	/* Get angle in x,y-plane (0...2 PI). */

	if (rad == 0.0)
	{
		phi = 0.0;
	}
	else
	{
		if (x < 0.0)
		{
			phi = 3.0 * M_PI_2 - asin(y / rad);
		}
		else
		{
			phi = M_PI_2 + asin(y / rad);
		}
	}

	return(z + rad + (DBL)TPat->Vals.Arms * phi / TWO_M_PI + turb_val);
}


/*****************************************************************************
*
* FUNCTION
*
*   spiral2_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Dieter Bayer
*
* DESCRIPTION
*   Spiral whirles around z-axis.
*   The number of "arms" is defined in the TPat.
*
* CHANGES
*
*   Aug 1994 : Creation.
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL spiral2_pattern (const VECTOR EPoint, const TPATTERN *TPat, int noise_generator)
{
	DBL rad, phi, turb_val;
	DBL x = EPoint[X];
	DBL y = EPoint[Y];
	DBL z = EPoint[Z];
	const TURB *Turb;

	if ((Turb=Search_For_Turb(TPat->Warps)) != NULL)
	{
		turb_val = Turb->Turbulence[X] * Turbulence(EPoint,Turb,noise_generator);
	}
	else
	{
		turb_val = 0.0;
	}

	/* Get distance from z-axis. */

	rad = sqrt(x * x + y * y);

	/* Get angle in x,y-plane (0...2 PI) */

	if (rad == 0.0)
	{
		phi = 0.0;
	}
	else
	{
		if (x < 0.0)
		{
			phi = 3.0 * M_PI_2 - asin(y / rad);
		}
		else
		{
			phi = M_PI_2 + asin(y / rad);
		}
	}

	turb_val = Triangle_Wave(z + rad + (DBL)TPat->Vals.Arms * phi / TWO_M_PI +
	                         turb_val);

	return(Triangle_Wave(rad) + turb_val);
}


/*****************************************************************************
*
* FUNCTION
*
*   spherical_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   -
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static DBL spherical_pattern (const VECTOR EPoint)
{
	register DBL value;

	VLength(value, EPoint);
	CLIP_DENSITY(value);

	return(value);
}


/*****************************************************************************
*
* FUNCTION
*
*   waves_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION   : Note this pattern is only used for pigments and textures.
*                 Normals have a specialized pattern for this.
*
* CHANGES
*
*   Nov 1994 : adapted from normal by [CY]
*
******************************************************************************/

static DBL waves_pattern (const VECTOR EPoint, const TPATTERN *TPat, const TraceThreadData *Thread)
{
	register unsigned int i;
	register DBL length, index;
	DBL scalar = 0.0;
	VECTOR point;

	for (i = 0 ; i < Thread->numberOfWaves ; i++)
	{
		VSub (point, EPoint, *Thread->waveSources[i]);
		VLength (length, point);

		if (length == 0.0)
		{
			length = 1.0;
		}

		index = length * TPat->Frequency * Thread->waveFrequencies[i] + TPat->Phase;

		scalar += cycloidal(index)/Thread->waveFrequencies[i];
	}

	scalar = 0.2*(2.5+(scalar / (DBL)Thread->numberOfWaves));

	return(scalar);
}


/*****************************************************************************
*
* FUNCTION
*
*   wood_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   TPat   -- Texture pattern struct
*
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
* CHANGES
*
*   Oct 1994 : adapted from pigment by [CY]
*
******************************************************************************/

static DBL wood_pattern (const VECTOR EPoint, const TPATTERN *TPat)
{
	register DBL length;
	VECTOR WoodTurbulence;
	VECTOR point;
	DBL x=EPoint[X];
	DBL y=EPoint[Y];
	const TURB *Turb;

	if ((Turb=Search_For_Turb(TPat->Warps)) != NULL)
	{
		DTurbulence (WoodTurbulence, EPoint,Turb);
		point[X] = cycloidal((x + WoodTurbulence[X]) * Turb->Turbulence[X]);
		point[Y] = cycloidal((y + WoodTurbulence[Y]) * Turb->Turbulence[Y]);
	}
	else
	{
		point[X] = 0.0;
		point[Y] = 0.0;
	}
	point[Z] = 0.0;

	point[X] += x;
	point[Y] += y;

	/* point[Z] += z; Deleted per David Buck --  BP 7/91 */

	VLength (length, point);

	return(length);
}


/*****************************************************************************
*
* FUNCTION
*
*   wrinkles_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
*   
* OUTPUT
*   
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*   
* AUTHOR
*
*   POV-Ray Team
*   
* DESCRIPTION   : Note this pattern is only used for pigments and textures.
*                 Normals have a specialized pattern for this.
*
* CHANGES
*
*   Nov 1994 : adapted from normal by [CY]
*
******************************************************************************/

static DBL wrinkles_pattern (const VECTOR EPoint, int noise_generator)
{
	register int i;
	DBL lambda = 2.0;
	DBL omega = 0.5;
	DBL value;
	VECTOR temp;
	DBL noise;

	if(noise_generator>1)
	{
		noise = Noise(EPoint, noise_generator)*2.0-0.5;
		value = min(max(noise,0.0),1.0);
	}
	else
	{
		value = Noise(EPoint, noise_generator);
	}

	for (i = 1; i < 10; i++)
	{
		VScale(temp,EPoint,lambda);

		if(noise_generator>1)
		{
			noise = Noise(temp, noise_generator)*2.0-0.5;
			value += omega * min(max(noise,0.0),1.0);
		}
		else
		{
			value += omega * Noise(temp, noise_generator);
		}

		lambda *= 2.0;

		omega *= 0.5;
	}

	return(value/2.0);
}


/*****************************************************************************
*
* FUNCTION
*
*   IntPickInCube(tvx,tvy,tvz, p1)
*    a version of PickInCube that takes integers for input
*
* INPUT
*
*   ?
*
* OUTPUT
*   
* RETURNS
*
*   long integer hash function used, to speed up cacheing.
*   
* AUTHOR
*
*   original PickInCube by Jim McElhiney
*   this integer one modified by Nathan Kopp
*   
* DESCRIPTION
*
*   A subroutine to go with crackle.
*
*   Pick a random point in the same unit-sized cube as tv, in a
*   predictable way, so that when called again with another point in
*   the same unit cube, p1 is picked to be the same.
*
* CHANGES
*
******************************************************************************/

static int IntPickInCube(int tvx, int tvy, int tvz, VECTOR  p1)
{
	size_t seed;

	seed = size_t(Hash3d(tvx&0xFFF,tvy&0xFFF,tvz&0xFFF));

	p1[X] = tvx + PatternRands(seed);
	p1[Y] = tvy + PatternRands(seed + 1);
	p1[Z] = tvz + PatternRands(seed + 2);

	return (int)seed;
}


/*****************************************************************************
*
* FUNCTION
*
*   PickInCube(tv, p1)
*
* INPUT
*
*   ?
*
* OUTPUT
*   
* RETURNS
*
*   long integer hash function used, to speed up cacheing.
*   
* AUTHOR
*
*   Jim McElhiney
*   
* DESCRIPTION
*
*   A subroutine to go with crackle.
*
*   Pick a random point in the same unit-sized cube as tv, in a
*   predictable way, so that when called again with another point in
*   the same unit cube, p1 is picked to be the same.
*
* CHANGES
*
******************************************************************************/

int PickInCube(const VECTOR tv, VECTOR  p1)
{
	size_t seed;
	VECTOR flo;

	/*
	 * This uses floor() not FLOOR, so it will not be a mirror
	 * image about zero in the range -1.0 to 1.0. The viewer
	 * won't see an artefact around the origin.
	 */

	flo[X] = floor(tv[X] - EPSILON);
	flo[Y] = floor(tv[Y] - EPSILON);
	flo[Z] = floor(tv[Z] - EPSILON);

	seed = size_t(Hash3d((int)flo[X], (int)flo[Y], (int)flo[Z]));

	p1[X] = flo[X] + PatternRands(seed);
	p1[Y] = flo[Y] + PatternRands(seed + 1);
	p1[Z] = flo[Z] + PatternRands(seed + 2);

	return (int)seed;
}

/*****************************************************************************
*
* FUNCTION
*
*   NewHash(tvx, tvy, tvz)
*
* INPUT
*
*   3D integer coordinates of the cell
*
* OUTPUT
*   
* RETURNS
*
*   long integer hash value
*   
* AUTHOR
*
*   Christoph Hormann based on MechSim Hash function by Daniel Jungmann
*   
* DESCRIPTION
*
*   New Hash function for the crackle pattern.
*
* CHANGES
*
*   -- Aug 2005 : Creation
*
******************************************************************************/

#ifndef HAVE_BOOST_HASH
unsigned long int NewHash(long int tvx, long int tvy, long int tvz)
{
	unsigned long int seed;
	long int r;

	tvx *= 73856093L;
	tvy *= 19349663L;
	tvz *= 83492791L;

	r = tvx ^ tvy ^ tvz;
	seed = abs(r);
	if (tvx<0) seed += LONG_MAX/2;
	if (tvy<0) seed += LONG_MAX/4;
	if (tvz<0) seed += LONG_MAX/8;

	return (seed);
}
#endif


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

const DBL INV_SQRT_3_4 = 1.154700538;
DBL quilt_cubic(DBL t, DBL p1, DBL p2)
{
	DBL it=(1-t);
	DBL itsqrd=it*it;
	/* DBL itcubed=it*itsqrd; */
	DBL tsqrd=t*t;
	DBL tcubed=t*tsqrd;
	DBL val;

	/* Originally coded as...

	val= (DBL)(itcubed*n1+(tcubed)*n2+3*t*(itsqrd)*p1+3*(tsqrd)*(it)*p2);

	re-written by CEY to optimise because n1=0 n2=1 always.

	*/

	val = (tcubed + 3.0*t*itsqrd*p1 + 3.0*tsqrd*it*p2) * INV_SQRT_3_4;

	return(val);
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static DBL fractal_exterior_color(const TPATTERN *TPat, int iters, DBL a, DBL b)
{
	switch(TPat->Vals.Fractal.exterior_type)
	{
		case 0:
			return  (DBL)TPat->Vals.Fractal.efactor;
		case 1:
			return (DBL)iters / (DBL)TPat->Vals.Fractal.Iterations;
		case 2:
			return a * (DBL)TPat->Vals.Fractal.efactor;
		case 3:
			return b * (DBL)TPat->Vals.Fractal.efactor;
		case 4:
			return a*a * (DBL)TPat->Vals.Fractal.efactor;
		case 5:
			return b*b * (DBL)TPat->Vals.Fractal.efactor;
		case 6:
			return sqrt(a*a+b*b) * (DBL)TPat->Vals.Fractal.efactor;
		case 7: // range 0.. (n-1)/n
			return  (DBL)( iters % (unsigned int)TPat->Vals.Fractal.efactor )/(DBL)TPat->Vals.Fractal.efactor;
		case 8: // range 0.. 1
			return  (DBL)( iters % (unsigned int)(1+TPat->Vals.Fractal.efactor) )/(DBL)TPat->Vals.Fractal.efactor;
	}
	return 0;
}


/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/

static DBL fractal_interior_color(const TPATTERN *TPat, int /*iters*/, DBL a, DBL b, DBL mindist2)
{
	switch(TPat->Vals.Fractal.interior_type)
	{
		case 0:
			return  (DBL)TPat->Vals.Fractal.ifactor;
		case 1:
			return sqrt(mindist2) * (DBL)TPat->Vals.Fractal.ifactor;
		case 2:
			return a * (DBL)TPat->Vals.Fractal.ifactor;
		case 3:
			return b * (DBL)TPat->Vals.Fractal.ifactor;
		case 4:
			return a*a * (DBL)TPat->Vals.Fractal.ifactor;
		case 5:
			return b*b * (DBL)TPat->Vals.Fractal.ifactor;
		case 6:
			return a*a+b*b * (DBL)TPat->Vals.Fractal.ifactor;
	}
	return 0;
}


/*****************************************************************************
*
* FUNCTION
*
*   Create_Density_File
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Dieter Bayer
*
* DESCRIPTION
*
*   Create a density file structure.
*
* CHANGES
*
*   Dec 1996 : Creation.
*
******************************************************************************/

DENSITY_FILE *Create_Density_File()
{
	DENSITY_FILE *New;

	New = reinterpret_cast<DENSITY_FILE *>(POV_MALLOC(sizeof(DENSITY_FILE), "density file"));

	New->Interpolation = NO_INTERPOLATION;

	New->Data = reinterpret_cast<DENSITY_FILE_DATA *>(POV_MALLOC(sizeof(DENSITY_FILE_DATA), "density file data"));

	New->Data->References = 1;

	New->Data->Name = NULL;

	New->Data->Sx =
	New->Data->Sy =
	New->Data->Sz = 0;

	New->Data->Type = 0;

	New->Data->Density32 = NULL;
	New->Data->Density16 = NULL;
	New->Data->Density8 = NULL;

	return (New);
}


/*****************************************************************************
*
* FUNCTION
*
*   Copy_Density_File
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Dieter Bayer
*
* DESCRIPTION
*
*   Copy a density file structure.
*
* CHANGES
*
*   Dec 1996 : Creation.
*
******************************************************************************/

DENSITY_FILE *Copy_Density_File(DENSITY_FILE *Old)
{
	DENSITY_FILE *New;

	if (Old != NULL)
	{
		New = reinterpret_cast<DENSITY_FILE *>(POV_MALLOC(sizeof(DENSITY_FILE), "density file"));

		*New = *Old;

		New->Data->References++;
	}
	else
	{
		New=NULL;
	}

	return(New);
}


/*****************************************************************************
*
* FUNCTION
*
*   Destroy_Density_File
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Dieter Bayer
*
* DESCRIPTION
*
*   Destroy a density file structure.
*
* CHANGES
*
*   Dec 1996 : Creation.
*
******************************************************************************/

void Destroy_Density_File(DENSITY_FILE *Density_File)
{
	if(Density_File != NULL)
	{
		if((--(Density_File->Data->References)) == 0)
		{
			POV_FREE(Density_File->Data->Name);

			if(Density_File->Data->Type == 4)
			{
				POV_FREE(Density_File->Data->Density32);
			}
			else if(Density_File->Data->Type == 2)
			{
				POV_FREE(Density_File->Data->Density16);
			}
			else if(Density_File->Data->Type == 1)
			{
				POV_FREE(Density_File->Data->Density8);
			}

			POV_FREE(Density_File->Data);
		}

		POV_FREE(Density_File);
	}
}

void Read_Density_File(IStream *file, DENSITY_FILE *df)
{
	size_t x, y, z, sx, sy, sz, len;

	if (df == NULL)
		return;

	/* Allocate and read density file. */

	if((df != NULL) && (df->Data->Name != NULL))
	{
		sx = df->Data->Sx = readushort(file);
		sy = df->Data->Sy = readushort(file);
		sz = df->Data->Sz = readushort(file);

		file->seekg(0, IOBase::seek_end);
		len = file->tellg() - 6;
		file->seekg(6);

		// figure out the data size
		if((sx * sy * sz * 4) == len)
		{
			df->Data->Type = 4;

			unsigned int *map = reinterpret_cast<unsigned int *>(POV_MALLOC(sx * sy * sz * sizeof(unsigned int), "media density file data 32 bit"));

			for (z = 0; z < sz; z++)
			{
				for (y = 0; y < sy; y++)
				{
					for (x = 0; x < sx; x++)
						map[z * sy * sx + y * sx + x] = readuint(file);
				}
			}

			df->Data->Density32 = map;
		}
		else if((sx * sy * sz * 2) == len)
		{
			df->Data->Type = 2;

			unsigned short *map = reinterpret_cast<unsigned short *>(POV_MALLOC(sx * sy * sz * sizeof(unsigned short), "media density file data 16 bit"));

			for (z = 0; z < sz; z++)
			{
				for (y = 0; y < sy; y++)
				{
					for (x = 0; x < sx; x++)
						map[z * sy * sx + y * sx + x] = readushort(file);
				}
			}

			df->Data->Density16 = map;
		}
		else if((sx * sy * sz) == len)
		{
			df->Data->Type = 1;

			unsigned char *map = reinterpret_cast<unsigned char *>(POV_MALLOC(sx * sy * sz * sizeof(unsigned char), "media density file data 8 bit"));

			for (z = 0; z < sz; z++)
			{
				for (y = 0; y < sy; y++)
					file->read(&(map[z * sy * sx + y * sx]), sizeof(unsigned char) * sx);
			}

			df->Data->Density8 = map;
		}
		else
			throw POV_EXCEPTION_STRING("Invalid density file size");

		if (file != NULL)
		{
			delete file;
		}
	}
}

static unsigned short readushort(IStream *infile)
{
	short i0 = 0, i1 = 0;

	if ((i0 = infile->Read_Byte ()) == EOF || (i1 = infile->Read_Byte ()) == EOF)
	{
		throw POV_EXCEPTION_STRING("Error reading density_file");
	}

	return (((unsigned short)i0 << 8) | (unsigned short)i1);
}

static unsigned int readuint(IStream *infile)
{
	int i0 = 0, i1 = 0, i2 = 0, i3 = 0;

	if ((i0 = infile->Read_Byte ()) == EOF || (i1 = infile->Read_Byte ()) == EOF ||
	    (i2 = infile->Read_Byte ()) == EOF || (i3 = infile->Read_Byte ()) == EOF)
	{
		throw POV_EXCEPTION_STRING("Error reading density_file");
	}

	return (((unsigned int)i0 << 24) | ((unsigned int)i1 << 16) | ((unsigned int)i2 << 8) | (unsigned int)i3);
}

static void InitializeBinomialCoefficients(void)
{
	int* ptr = BinomialCoefficients;
	*ptr = 1; ++ptr;

	for(unsigned n=1; n<=FRACTAL_MAX_EXPONENT; ++n)
	{
		*ptr = 1; ++ptr;
		for(unsigned k=1; k<n; ++k)
		{
			*ptr = *(ptr-(n+1)) + *(ptr-n); ++ptr;
		}
		*ptr = 1; ++ptr;
	}
	ptr = BinomialCoefficients+1;
	for(unsigned m=1; m<=FRACTAL_MAX_EXPONENT; ++m)
	{
		++ptr;
		for(unsigned k=1; k<m; ++k)
		{
			if((k&2)!=0) *ptr = -(*ptr);
			++ptr;
		}
		if((m&2)!=0) *ptr = -(*ptr);
		++ptr;
	}
}

static void InitialiseCrackleCubeTable(void)
{
	int     *p = CrackleCubeTable;

	// the crackle cube table is a list of offsets in the range -2 ... 2 which
	// are applied to the EPoint while evaluating the Crackle pattern, in order
	// to look up points in close-by cubes. consider the EPoint to be in a cube
	// at the center of a 3x3 grid of cubes; candidate cubes are that are within
	// a "3d knight move" away (i.e. not more than 2 units). we use a lookup table
	// to speed up iteration of the cube list by avoiding branch tests.
	for(int addx = -2; addx <= 2; addx++)
	{
		for(int addy = -2; addy <= 2; addy++)
		{
			for(int addz = -2; addz <= 2; addz++)
			{
				if((abs(addx) == 2) + (abs(addy) == 2) + (abs(addz) == 2) <= 1)
				{
					*p++ = addx;
					*p++ = addy;
					*p++ = addz;
				}
			}
		}
	}
}

// This should be called once, at povray start
void InitializePatternGenerators(void)
{
	InitializeBinomialCoefficients();
	InitialiseCrackleCubeTable();
}

}
