//-----------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
#ifndef( MA_Helpers_Temp)
	#declare MA_Helpers_Temp = version;
	#version 3.7;
	#ifndef( Colors_Temp)		#include "colors.inc"	#end
	#ifndef( Strings_Temp)		#include "strings.inc"	#end
	//Here we're still inside an If-Block !! (the first #ifndef)
//-----------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------


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

--------------------------------------------------------
MA_Helpers
--------------------------------------------------------
Purpose:		Provide usefull macros of all types
Author:			Martin Seydler
Website:		www.maetes.com
Documentation:	http://www.maetes.com/en/povray-os/ma_helpers
Date:			2017 an later
Version:		1 (December 2024)
Copyright:		See above (author), below near any macro (orignal author and sources) and everywhere (www.povray.org)
License:		Use it in your projects as you like. A mention and a link to my website would be nice.


Some very helpful macros, often needed in some of my packages.
For a complete list see below, sortet more or less by the type of helper.
Maybe a newer version is available at www.maetes.com/en/povray

IT DOESNT HURT TO INCLUDE THIS FILE !!!
Many of my packages need one or more of this helpers, and maybe this file looks a littlebit big and oversized.
But it doesnt make sense to not include it to save render-time, even for animations or in small test-files.
You can include dozends of files like this, with complex macros and what else, it will not break out anything.
So there is also no need to split them in smaller pieces (like "math", "debug", "array", ...).

What really does steal your time while povraying:
- include big files like pictures (planetary maps weighting many MB)
- include and calculate fake-databases with many, thousands of rows (big povray-arrays with stars-data)
- render very detailed objects (a star-destroyer with millions of small spheres and rectangles and ...)
- render atmospheres, fogs, media, transparencies and other diffuse stuff
- render glass and reflections
- render hollow cylinders with something inside and a hole in the curved sites to look into (that once broke my neck)
- use areal lights or others that are not point-like, or use more than one light-source


Helpful Command-Lines:
---------------------------
povray +Ima_helpers.inc +Otest/ma_helpers.png +w800 +h470 -GR +A
povray +Ima_helpers.inc +Otest/ma_helpers.png +w800 +h600 -GR +A
povray +Ima_helpers.inc +Otest/ma_helpers.png +w600 +h600 +GD +GF -GR +GS +GW +Q9 +A

Try this with xExample = "get-slope-2"
povray +Ima_helpers.inc +Oanim/ma_helpers- +W200 +H150 +KFI1 +KFF60
povray +Ima_helpers.inc +Oanim/ma_helpers- +W800 +H500 +KFI1 +KFF20
povray +Ima_helpers.inc +Oanim/ma_helpers- +W800 +H500 +KFI1 +KFF60

povray +Ima_helpers.inc +Oanim/ma_helpers- +W800 +H500 +A +KI0.0 +KF1.0 +KFI1 +KFF20

//Anim-Gifs
convert -delay 10 anim/ma_helpers-*.png anim/anim0_0.gif
convert -delay 5 anim/ma_helpers-*.png anim/anim0_0.gif
convert -delay 5 anim/ma_helpers-*.png -resize 200x118 anim/anim0_0.gif

rm anim/ma_helpers-*.png


To open the resulting picture in a Browser (change the path):
-----------------------------------------------------
file:///home/pi/Projekte/povray/bibs/misc/ma_helpers/test/


Weitere Ideen
-----------------------
Logfile
Könnte sich besonders bei Fehlermeldungen in komplexen Skripten nützlich machen.
Jede Funktion meldet sich beim Logfile, das Funktionsname, Text und ne Variable sofort abspeichert.
Schmiert das Skript ab, hat man den ganzen Verlauf der letzten Schritte vor sich.
Bräuchte sicherlich wie bei MA_Terminal_T* mehrere Funktionen, eben für jede Art Variable eine.

Logfile- und Terminal-Makros in "Debug" zusammenführen?
Da dann noch weitere Helferlein einbauen.
Z.B. die geplanten Textausgaben am Bildschirm (welcher Frame/Second, welche Kamera-Pos, usw.)
Oder ist Terminal (in meinen Bibs) so verbreitet, das es doch in der oft genutzten ma_helpers drinbleiben sollte?
Oder generell hier in ma_helpers eine Ecke mit Debug-Zeuch fest vorsehen?


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



/* ----------------------------------------------------------------------------------------------------------------------------
Declare some variables

There is no need to change the values here, these are defaults only.
Instead do this in your including-script, bevor you include this file.
	
MA_Terminal_PreText
	A small text that all terminal-messages will start with.
	Nice to find them between all the other povray-outputs.
	
MA_Deprecated_Show
MA_Warning_Show
	1 or 0 or true or false
	switches the output of messages
	
MA_Warning_Type
	"fake" uses a simple #debug-message (by using MA_Terminal*())
	"real" uses the standard #warning-directive
	
MA_Deprecated_Counter
MA_Warning_Counter
	Counts the number of warnings and deprecated-messages


MA_Deg2Vector_RA_Offset
	Use this to move the position of Rectascension = 0.
	  0 places RA=0 at positive Z-Axis (default, constellations Pegasus/Andromeda/Pisces)
	-90 positive X-Axis (rotate to the left)
	+90 negative X-Axis (rotate to the right)

MA_Include_Talk
	True/false if MA_Include*() is allowed to send Messages to the terminal.
	Only useful if something does not happen as expected.
MA_Include_Folders
	The Folders to search for include-files.
	Relative path (!) with respect from the folder of the executed pov-file.

Lig*
Cam*
	I decided to use Lig and Cam instead of Light and Camera/Cam to avoid Name-Conflicts, so dont be confused or angry about this.
	A prefix like gCamRotate (like global) also doesnt look nice to me.
	The LigIntensity can be used to fade out a scene, where 1 = 100% of light, 0 = no light.
	But dont forget, maybe you have to alter other light-sources also (especially ambient-values)
	
MA_Cmd*
	This Variables only comes to action, if you ...
	use them in the command-line, AND if you use the macro MA_CameraAndLight().
	I use them out of php-scripts, that creates the left picture, the right,
	than use imagemagick to combine both to one anaglyph (here not explained).
	With this variables and use of function MA_CameraAndLight() its easy to create the pictures
	without manually editing the script for each of the pictures.
	Only change the variables inside the command-line.
	The Value MA_CmdClock is usefull for movies, even for stereo-anaglyph-movies.
	Examples:
	povray +Ipovraytest.pov +Opovraytest_l.png +w1280 +h720 +GA +A Declare=MA_CmdStereo=-1 Declare=MA_CmdStereoOff=0.8 Declare=MA_CmdClock=0.25
	povray +Ipovraytest.pov +Opovraytest_r.png +w1280 +h720 +GA +A Declare=MA_CmdStereo=+1 Declare=MA_CmdStereoOff=0.8 Declare=MA_CmdClock=0.25
	povray +Ipovraytest.pov +Opovraytest_l.png +w1280 +h720 +GA +A Declare=MA_CmdStereo=-1 Declare=MA_CmdStereoOff=0.8 Declare=MA_CmdClock=0.5
	povray +Ipovraytest.pov +Opovraytest_r.png +w1280 +h720 +GA +A Declare=MA_CmdStereo=+1 Declare=MA_CmdStereoOff=0.8 Declare=MA_CmdClock=0.5
	.....................................^..............................................^..........................^^^.....................^^^..
	Here we declare 3 constants (povray allowes floats only, no strings):
	MA_CmdStereo	= -1
	MA_CmdStereoOff	=0.8
	MA_CmdClock		=0.25
	This one we can use as they were declared inside the script like:
	#declare MA_CmdStereo = -1; // ... and so on.
	
MA_CmdStereo*
	This one only came into action, if you use the macro MA_CameraAndLight() and some of my PHP-Scripts.
MA_CmdStereo
	 0  = default, does nothing
	<0 = left picture is ordered
	>0 = right picture
	You can use this information manually or let run the automatic.
	If MA_CmdStereoOff = 0 (with Off ant the end, see below) then the value of MA_CmdStereo (without Off!) is used as a camera-rotation at the Y-Axis.
	On this way the Camera slightly moves to the left or the right (rotates around the CamLookAt-Vector, most of the time <0,0,0>).
MA_CmdStereoOff
	Float, Difference +/- from left to right picture, used as Camera-Rotation Y-Axis.
	Easiest way is to set it = 0, so the values of MA_CmdStereo defines the Camera-Rotation around Y-Axis.
	But if you want to have more control inside the script, define MA_CmdStereoOff inside the script.
	Then MA_CmdStereo only defines wich picture is ordered ( < 0 = left and > 0 = right) and MA_CmdStereoOff defines the real value + or -.
	Default = 0, so it will alter nothing.
MA_CmdClock
	Normally animations use the clock-variable, maybe set inside an ini-file.
	But if you like to use other executing scripts like php, you cant define directly the clock-variable.
	So use of MA_CmdClock is a workaround, to adjust it from outside.
	Use Values from 0 to 1 and multiply something like rotations or flight-path.
	Default = 1, so multiplied with anything it will alter nothing.

MA_Seed
	To generate random-numbers with rand(MA_Seed).
MA_GetParam_Break
	Decides if the first found parameter counts, or if the search continues till the end of all parameters.
	Set it to "first" or "last".
MA_GetParam_NoValue
	String that will be returned in case the param wasnt found.
	Only in use if return-types are string or boolean.
	Prevents that a value "" will overwritten by the default, because "" can be also a valid string.
MA_GetParam_CacheIndex
	Number of cached items AND index of next (!) item to insert
MA_GetParam_CacheStrings
MA_GetParam_CacheArrays
	Caches the param-Strings and prepared array to fasten the macro MA_GetParam*()
	
	
MA_SlopeDefault
	Default-Value for slopes. The string only ("linear", "cosinus", ...) See MA_GetSlope() for more
MA_SlopeCPPFactor
	Defines the steepness of slope "cosinus++".
	2 = about the same as "cosinus+"
	3 = bit steeper
	5 = very steep but not like a switch. You should use no bigger value than 5.

MA_Clock
	Sumilates the variable clock if animation isnt running. See macro MA_SetClock() for more infos.

MA_String2Array_Esc
	Escape-character to allow separators part of the string.
	Example: "one; two/;two; three" retunrs array [3] ["one", "two;two", "three]
	"\" as escape-char is not allowed by povray, sorry.
	Crys if the string ist "one; two\;two; three" => "Illegal escape sequence in string"
MA_Vector2String_Dims
MA_Vector2String_Digs
	Default-Values for dimensions and digits, the macro MA_Vector2String() works with.
	3 dimensions means you cant use this macro to convert 4-vector-colors, in this case use MA_Vector2String_Base().

MA_Texture_Dark1
MA_Texture_Light1
	Standard-Textures for some other Textures or Makros
	
	
RS_*	*** Doku fehlt noch ***

MA_TestArray_1D
MA_TestArray_2D
	Arrays to test some things/macros. Sometimes nice to have during development.
	
*/// --------------------------------------------------------------------------------------------------------------------------

#ifndef (MA_Terminal_PreText)		#declare MA_Terminal_PreText		= "*** MA => ";		#end	//String

#ifndef (MA_Deprecated_Show)			#declare MA_Deprecated_Show		= 1;					#end	//1/0/true/false
#ifndef (MA_Warning_Show)			#declare MA_Warning_Show			= 1;					#end	//1/0/true/false
#ifndef (MA_Warning_Type)			#declare MA_Warning_Type			= "fake";			#end	//fake or real
#ifndef (MA_Notice_Show)				#declare MA_Notice_Show			= 1;					#end	//1/0/true/false

#ifndef (MA_Deprecated_Counter)		#declare MA_Deprecated_Counter	= 0;					#end	//dont change this
#ifndef (MA_Warning_Counter)			#declare MA_Warning_Counter		= 0;					#end	//dont change this



#ifndef (MA_Deg2Vector_RA_Offset)	#declare MA_Deg2Vector_RA_Offset	= 0;					#end	//Float, -180 to +180, Default = 0

#ifndef (MA_Include_Talk)			#declare MA_Include_Talk			= 0;					#end	//1/0/true/false
#ifndef (MA_Include_Act_Path)		#declare MA_Include_Act_Path		= "";				#end	//
#ifndef (MA_Include_Last_Path)		#declare MA_Include_Last_Path	= "";				#end	//

#ifndef (MA_Include_Folders)
	//#declare MA_Include_Folders = array[3] { "../", "", "base-files/" };							//Array
	#declare MA_Include_Folders = array[6] { "../../misc/", "../../astro/", "../../space/", "../../nature/", "", "base-files/" };							//Array
#end



#ifndef (CamLocation)				#declare CamLocation		= <0,2,-10>;			#end	//Vector
#ifndef (CamLookAt)					#declare CamLookAt		= <0,0,0>;			#end	//Vector
#ifndef (CamAngle)					#declare CamAngle		= 60;				#end	//Float
#ifndef (CamRotate)					#declare CamRotate		= <0,0,0>;			#end	//Vector, use <0,something,0> to fly around CamLookAt
#ifndef (CamSky)						#declare CamSky			= <0,1,0>;			#end	//Vector
#ifndef (CamType)					#declare CamType			= "perspective";		#end	//String

#ifndef (LigVector)					#declare LigVector		= <10,3,-10>*1000;	#end	//Vector
#ifndef (LigColor)					#declare LigColor		= rgb <1,1,1>;		#end	//Color
#ifndef (LigIntensity)				#declare LigIntensity	= 1;					#end	//Multiplier to Color
#ifndef (LigRotate)					#declare LigRotate		= <0,0,0>;			#end	//Vector (mostly used as <0,something,0> for sun-flyarounds


#ifndef (MA_CmdStereo)				#declare MA_CmdStereo	= 0;					#end	//Float, or -1, 0, +1
#ifndef (MA_CmdStereoOff)			#declare MA_CmdStereoOff	= 1;					#end	//Float, Default = 1 (bigger means more stereo-effect but more "strange" parts)
#ifndef (MA_CmdClock)				#declare MA_CmdClock		= -1;				#end	//Float, 0 to 1, Default = 1 --- or -1 if Helper has to ignore the value at all


#ifndef (MA_Seed)					#declare MA_Seed			= seed(611);			#end	//Integer

#ifndef (MA_GetParam_Break)			#declare MA_GetParam_Break		= "last";			#end	//"first" or "last"
#ifndef (MA_GetParam_NoValue)		#declare MA_GetParam_NoValue		= "*-NoValue-*";		#end	//String
#ifndef (MA_GetParam_CacheIndex)		#declare MA_GetParam_CacheIndex	= 0;					#end
#ifndef (MA_GetParam_CacheStrings)	#declare MA_GetParam_CacheStrings= array[30];			#end
#ifndef (MA_GetParam_CacheArrays)	#declare MA_GetParam_CacheArrays	= array[30];			#end

#ifndef (MA_SlopeDefault)			#declare MA_SlopeDefault	= "linear";					#end	//String
#ifndef (MA_SlopeCPPFactor)			#declare MA_SlopeCPPFactor	= 3.7;					#end	//Float (use 2 to 5)

#ifndef (MA_Clock)					#declare MA_Clock			= 0;						#end	//Float (0 to 1)


#ifndef (MA_String2Array_Esc)		#declare MA_String2Array_Esc	= "/";					#end	//Char, "\" not allowed by povray
#ifndef (MA_Vector2String_Dims)		#declare MA_Vector2String_Dims	= 3;					#end	//Integer (3 to 5)
#ifndef (MA_Vector2String_Digs)		#declare MA_Vector2String_Digs	= 5;					#end	//Integer (> 1 recommended)


#ifndef (MA_Texture_Dark1)
	#declare MA_Texture_Dark1 =	texture {
		pigment { color rgbt <1,0.55,0, 0.9> }
		finish  { phong 1 }
	}
#end
#ifndef (MA_Texture_Light1)
	#declare MA_Texture_Light1 = texture {
		pigment{ color rgbt <1,1,1, 0.9> }
		finish { phong 1 }
	}
#end


//Some colors to identify sites
#ifndef (MA_Color_Aft)			#declare MA_Color_Aft	= color rgb <0,0,1>;			#end
#ifndef (MA_Color_For)			#declare MA_Color_For	= color rgb <1,0,0>;			#end
#ifndef (MA_Color_Left)			#declare MA_Color_Left	= color rgb <0,1,0>;			#end
#ifndef (MA_Color_Right)			#declare MA_Color_Right	= color rgb <1,1,0>;			#end
#ifndef (MA_Color_Top)			#declare MA_Color_Top	= color rgb <0,1,1>;			#end
#ifndef (MA_Color_Bottom)		#declare MA_Color_Bottom	= color rgb <1,0,1>;			#end


//The following variables to not use the prefix MA_, its easier to write when often used.
#declare RS_Dev		= 0;
#declare RS_Tec		= 0;
#declare RS_Real	= 1;
#declare RS_Hell	= 0;
#declare RS_Level	= 3.01;
#declare RS_Act		= "r";
#declare RS_Last	= "r";

#declare Nothing		= 0.0001;
#declare Nothing2	= Nothing*2;


// Constants used for the text macros, borrowed from shapes.inc
//#ifndef (Align_Left)	#declare Align_Left		= 1;	#end
//#ifndef (Align_Right)	#declare Align_Right	= 2;	#end
//#ifndef (Align_Center)	#declare Align_Center	= 3;	#end


//Paar weitere Rows/Cols hinzufügen (was mit alphabetischen Sachen, u.a. Umlaute, Zahlen über 10, für Sort-Geschichten.)
#declare MA_TestArray_1D			= array[5] { "alpha", "beta", "gamma", "delta", "bongo" }
#declare MA_TestArray_2D			= array[9][4] {
{	"0",	"zero",		"null",		"_0-null"		}
{	"1",	"one",		"eins",		"01-unos"		}
{	"2",	"two",		"zwei",		"-2-dos"			}
{	"3",	"three",		"drei",		".30-tres"		}
{	"4",	"four",		"vier",		"#4-quattros"	}
{  "10",	"ten",		"zehn",		"#10-"			}
{ "010",	"ten",		"zehn",		"#10-"			}
{ "10a",	"ten-abc",	"zehn-a",	"#10-"			}
{ "10b",	"ten-b",		"zehnb",	"#10-"				}
}


//Example for the usage of MA_Default. See MA_CameraAndLight_Prepare()
#if (false)
	#macro MA_Default()
		#default {
			texture { pigment { color rgb <0.9,0.0,0.5> } }
			finish { ambient 0.1 * LigIntensity diffuse 0.99 } //"LigIntensity" is the magic word, to fadein/out whole scenes
		}
	#end
#end



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


Debug and Logfile


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




/* ****************************************************************************************************************************
MA_Terminal_T*

Sends Text and/or variables to the terminal-window. Nice to have during development of programming-tasks.
Unfortunately povray doesnt know something about ...
- optional parameters
- on-the-fly variable-converting
- functions to learn about the type of a variable
so we need for every type of variable its own macro.

Date:
	somewhen 2017, 17.4.2019
	
Example:
	MA_Terminal_F ("Value object-width", max_extend(myObject).x)

MA_Terminal() sends one simple String to the Terminal.
All others a string and ...
_F	= Float
_I	= Integer
_V	= vector, allways handled if the vector has 5 Dimensions and 3 Digits, dont worry about following 0.000s
_S	= ... another string - nice to send them without concating them first
_AS	= string-array
_AF	= float-array
*/// **************************************************************************************************************************
#macro MA_Terminal		(mText)			MA_Terminal_Base (mText, "")									#end
#macro MA_Terminal_S	(mText, mValue)	MA_Terminal_Base (mText, mValue)								#end
#macro MA_Terminal_F	(mText, mValue)	MA_Terminal_Base (mText, str(mValue,0,-1))						#end
#macro MA_Terminal_I	(mText, mValue)	MA_Terminal_Base (mText, str(mValue,0,0))						#end
#macro MA_Terminal_V	(mText, mValue)	MA_Terminal_Base (mText, MA_Vector2String_Base(mValue, 5, 3))	#end
#macro MA_Terminal_C	(mText, mValue)	MA_Terminal_Base (mText, MA_Vector2String_Base(mValue, 5, 3))	#end
#macro MA_Terminal_AS	(mText, mValue)	MA_Terminal_Base (mText, MA_ArrayString2String(mValue, ","))	#end	//1D-Arrays only! Use MA_OutputArrayString() instead.
#macro MA_Terminal_AF	(mText, mValue)	MA_Terminal_Base (mText, MA_ArrayFloat2String (mValue, ","))	#end	//Dito
#macro MA_Terminal_Base	(mText, mValue)
	#local mText = concat (MA_Terminal_PreText, mText);
	#if (mValue != "") #local mText = concat (mText, ": ", mValue); #end
	#debug concat ( mText, "\n")
#end





/* ****************************************************************************************************************************
MA_Error
Raises an error. After this call the execution of the script stops.
Date:		17.4.2019
Example:	MA_Error ("MyFunction", "Image-File not found. Or to big or whatever.")
Params:
	mMacro, mText	String
*/// **************************************************************************************************************************
#macro MA_Error (mMacro, mText)
	#local mText = concat("Macro '", mMacro, "' says: ", mText);
	#error mText
#end


/* ****************************************************************************************************************************
MA_Warning
Sends a warning-message to the terminal-window.
Date:		17.4.2019
Example:	MA_Warning ("MyFunction", "Index of array 'mArray' higher than expected")
Params:
	mMacro, mText	String
*/// **************************************************************************************************************************
#macro MA_Warning (mMacro, mText)
	#declare MA_Warning_Counter = MA_Warning_Counter + 1;
	#if (MA_Warning_Show = true)
		#local mText = concat("WARNING (", str(MA_Warning_Counter,0,0), "): Macro '", mMacro, "' says: ", mText);
		#if (MA_Warning_Type = "fake")	
			MA_Terminal(mText)		//fake-warning (uses simple debug-message)
		#else 	
			#warning mText		//real povray-warning
		#end
			
	#end
#end

/* ****************************************************************************************************************************
MA_Notice
Sends a notice-message to the terminal-window.
Date:		7.5.2019
Example:	MA_Notice ("MyFunction", "Caching done successfully")
Params:
	mMacro, mText	String
*/// **************************************************************************************************************************
#macro MA_Notice (mMacro, mText)
	#if (MA_Notice_Show = true)
		#local mText = concat("Notice: Macro '", mMacro, "' says: ", mText);
		MA_Terminal(mText)
	#end
#end


/* ****************************************************************************************************************************
MA_Deprecated
Sends a deprecated-message to the terminal-window.
Date:		17.4.2019
Example:	MA_Deprecated ("OldMacro", "NewMacro)
Params:
	mOldMacro, mNewMacro	String
*/// **************************************************************************************************************************
#macro MA_Deprecated (mOldMacro, mNewMacro)
	#declare MA_Deprecated_Counter = MA_Deprecated_Counter + 1;
	#if (MA_Deprecated_Show = true)
		MA_Terminal(concat("*** DEPRECATED (", str(MA_Deprecated_Counter,0,0), ") ***: Macro '", mOldMacro, "' is old and risky, use '", mNewMacro, "' instead!!!"))
	#end
#end




/* ****************************************************************************************************************************
MA_Finish
Shows statistics
Maybe in the future also number of loaded images, saved logfiles, ...
Date:	17.4.2019
*/// **************************************************************************************************************************
#macro MA_Finish ()
	#debug	"------------------------------\n"
	#debug	"MA_Finish() - Statistics\n"
	#debug	"------------------------------\n"
	#debug	concat ("warnings       : ", str(MA_Warning_Counter,0,0), "\n")
	#debug	concat ("deprecated     : ", str(MA_Deprecated_Counter,0,0), "\n")
	#ifdef (MA_Screenplay_Temp)
		#debug	concat ("SCR_DataIndex  : ", str(SCR_DataIndex,0,0), "\n")
	#end
	#debug	"------------------------------\n"
#end





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


Specials


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



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

Prepares all Variables for camera and light

Problem when using CamRotate to get 3D-Pictures:
	To rotate the camera around the Y-Axis works almost perfectly, if the camera is at about Y = 0 (relative to the look_at-Vector)
	In this case a rotation actually moves the camera, all objects in all edges of the picture show good 3D-Effects as expected,
	that means blue and red appearance will get seen horizontally.
	But if its well above or below Y = 0, especially at > 70 degrees (with respect to the LookAt-Point), rotations not means moving,
	it really means rotation. That ends in not horizontally appearance of red and blue parts of 3D-Objects,
	instead this will happen more and more vertically, the farer the objects are away from the center of the image.
	At the moment (17.9.2018) I dont know how to handle this.
	Will think about.
	
See also:
	http://www.f-lohmueller.de/pov_tut/camera_light/light_d1.htm#area
	http://www.f-lohmueller.de/pov_tut/camera_light/camera_d1.htm

Params:
	none
Return:
	none
*/// **************************************************************************************************************************

#macro MA_CameraAndLight_Prepare ()

	//----------------------------------------------------------------------
	//Screenplay-values overwrites the values set in Cam* and Lig*
	//----------------------------------------------------------------------
	#ifdef( MA_Screenplay_Temp )
		#declare CamLocation	= SCR_GetVector ("cam", "", "location",	CamLocation	);
		#declare CamLookAt		= SCR_GetVector ("cam", "", "lookat",	CamLookAt	);
		#declare CamAngle		= SCR_GetNumber ("cam", "", "angle",	CamAngle	);
		#declare CamRotate		= SCR_GetVector ("cam", "", "rotate",	CamRotate	);
		#declare CamSky			= SCR_GetVector ("cam", "", "sky",		CamSky		);
		#declare CamType		= SCR_GetString ("cam", "", "type",		CamType		);
		#declare LigVector		= SCR_GetVector ("lig", "", "vector",	LigVector	);
		#declare LigColor		= SCR_GetVector ("lig", "", "color" ,	LigColor	);
		#declare LigIntensity	= SCR_GetNumber ("lig", "", "intensity",LigIntensity);
		#declare LigRotate		= SCR_GetVector ("lig", "", "rotate",	LigRotate	);
	#end

	
	//----------------------------------------------------------------------
	//Call macro that sets the default-values
	//----------------------------------------------------------------------
	#ifdef("MA_Default")
		MA_Default()
	#end
#end




/* ****************************************************************************************************************************
Sets Camera and Light
	
See also:
	http://www.f-lohmueller.de/pov_tut/camera_light/light_d1.htm#area
	http://www.f-lohmueller.de/pov_tut/camera_light/camera_d1.htm

Params:
	none
Return:
	None
*/// **************************************************************************************************************************
#macro MA_CameraAndLight()	   MA_CameraAndLight_Set()  #end
#macro MA_CameraAndLight_Set()

	//----------------------------------------------------------------------
	//Call the prepare-macro.
	//----------------------------------------------------------------------
	MA_CameraAndLight_Prepare ()

	
	//----------------------------------------------------------------------
	//Evaluate Command-Line Variables
	//This will come into action, if you create stereo-images,
	//with the help of one of my PHP-scripts (not yet published)
	//----------------------------------------------------------------------
	#if (MA_CmdStereo = 0)
		//Nothing to do here. Default = 0 means do nothing. Single-Picture or no Stereo.
	#elseif(MA_CmdStereo < 0)
		//Stereo, Movement to the left
		#if (MA_CmdStereoOff = 0)
			#declare CamRotate = CamRotate + <0,MA_CmdStereo,0>;		//Use MA_CmdStereo as Value
		#else
			#declare CamRotate = CamRotate + <0,-MA_CmdStereoOff,0>;	//Use MA_CmdStereoOff, as negative value
		#end
		#declare MA_CmdStereo = 0;
	#elseif(MA_CmdStereo > 0)
		//Stereo, Movement to the right
		#if (MA_CmdStereoOff = 0)
			#declare CamRotate = CamRotate + <0,MA_CmdStereo,0>;		//Use MA_CmdStereo as Value
		#else
			#declare CamRotate = CamRotate + <0,MA_CmdStereoOff,0>;	//Use MA_CmdStereoOff, as positive value
		#end
		#declare MA_CmdStereo = 0;
	#end
	
	//----------------------------------------------------------------------
	//Some other checks
	//----------------------------------------------------------------------
	#ifdef(LigLocation)
		MA_Warning ("MA_CameraAndLight_Set", "Variable 'LigLocation' is defined, maybe instead of correct Variable 'LigVector'?")
	#end
	
	//----------------------------------------------------------------------
	//Set the light first
	//The LigIntensity can nicely used to fade in/out an animation.
	//----------------------------------------------------------------------
	light_source {
		LigVector
		color 	LigColor * LigIntensity
		rotate	LigRotate
	}

	//----------------------------------------------------------------------
	//Set Camera
	//Unfortunately its not possible to set the camera-type by a variable.
	//This way it works, nevertheless I apologize for this ugly construction.
	//The difference is nothing else than the Line behind "camera {".
	//----------------------------------------------------------------------
	#if		(CamType = "ultra_wide_angle" | CamType = "ultrawideangle")
		camera	{
			ultra_wide_angle
			right		x*image_width/image_height
			location		CamLocation
			look_at		CamLookAt
			angle		CamAngle
			sky			CamSky
			rotate 		CamRotate
		}
	#elseif (CamType = "orthographic")
		camera	{
			orthographic
			right		x*image_width/image_height
			location		CamLocation
			look_at		CamLookAt
			angle		CamAngle
			sky			CamSky
			rotate 		CamRotate
		}
	#else
		//Warning, choosen type not implemented so far
		//Copy the block above into your script, change the type, be happy
		#if (CamType != "perspective")
			MA_Warning ("MA_CameraAndLight_Set", concat("Unexpected camera-type: ", CamType))
		#end
		camera	{
			perspective //its default anyway
			right		x*image_width/image_height
			location		CamLocation
			look_at		CamLookAt
			angle		CamAngle
			sky			CamSky
			rotate 		CamRotate
		}
	#end
#end


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

MA_RenderStyle_Set

24.11.2018

Example for using in calling pov-files (not includes):
See example bit lower at RenderStyle_Reset()

Example at creating objects:
	sphere { 0,1
		texture {
			#if (RS_Dev)	pidgment { color Gray10 }		#end
			#if (RS_Tec)	pidgment { color Green }		#end
			#if (RS_Real)	pidgment { color Silver }		#end
			#if (RS_Hell)	pidgment { color Silver2 }		#end
		}
		//Or the same for Tech + Dev and Real + Hell combined:
		texture {
			#if (RS_Level >= 3)	pidgment { color Silver2 } 	//real or hell
			#else				pidgment { color Gray10 }	//dev or tec
			#end
		}
	}

Todos/Issues:
	Maybe add styles "a" and "b" or something, to use them independently.
	One could show a technical drawing, only the dimensions and them mostly transparent.
	Another a fake-infrared-view, showing temperature-variation on the outside.
	Or whatever.
	
Used from:
	Most of my own macros that returns visible objects.
	Example: RS_Dev=1 => Prevents rendering of planet-atmospheres in ma_horizons.inc.

Params:
	mStyle:				String (a word or a char only), see description above
Return:
	none
*/// **************************************************************************************************************************
#macro MA_RenderStyle_Set(mStyle)

	#local mStyle	= strlwr(mStyle);

	#if		( mStyle = "d"	| mStyle = "dev"	| mStyle = "debug" )
		#declare RS_Dev		= 1;
		#declare RS_Tec		= 0;
		#declare RS_Real	= 0;
		#declare RS_Hell	= 0;
		#declare RS_Level	= 1;
		#declare RS_Last	= RS_Act;
		#declare RS_Act		= "d";
	#elseif	( mStyle = "t"	| mStyle = "tec" )
		#declare RS_Dev		= 0;
		#declare RS_Tec		= 1;
		#declare RS_Real	= 0;
		#declare RS_Hell	= 0;
		#declare RS_Level	= 2;
		#declare RS_Last	= RS_Act;
		#declare RS_Act		= "t";
	#elseif	( mStyle = "r"	| mStyle = "real" )
		#declare RS_Dev		= 0;
		#declare RS_Tec		= 0;
		#declare RS_Real	= 1;
		#declare RS_Hell	= 0;
		#declare RS_Level	= 3;
		#declare RS_Last	= RS_Act;
		#declare RS_Act		= "r";
	#elseif	( mStyle = "h"	| mStyle = "hell" )
		#declare RS_Dev		= 0;
		#declare RS_Tec		= 0;
		#declare RS_Real	= 0;
		#declare RS_Hell	= 1;
		#declare RS_Level	= 4;
		#declare RS_Last	= RS_Act;
		#declare RS_Act		= "h";
	#else
		//Error/Warning
	#end
		
#end

/* ****************************************************************************************************************************
Toggles back to the last Render-Style

24.11.2018

Example:
	MA_RenderStyle_Set("real") // or "dev", whatever.
	object { Get_far_away_object__No_need_to_be_too_detailed() }
	MA_RenderStyle_Set("hell")
	object { Get_something_very_near_with_all_possible_details() }
	MA_RenderStyle_Reset()
	object { Get_another_far_away_object__No_need_to_be_too_detailed() }
	
Used from:
	Many of my "executable" povray-files (not or very rarely in include-files)
	
*/// **************************************************************************************************************************
#macro MA_RenderStyle_Reset()
	MA_RenderStyle_Set(RS_Last)
	#declare RS_Last = RS_Act;
#end




/* ****************************************************************************************************************************
Checks a string for containing Parameter/Value-Pair

Nice to squeeze many macro-parameter into one only
Also you can modify the macro without modifying the macro-header, and all callings of it in other scripts.
xParamName is not-case-sensitive.

Date
	30.11.18 / 3.5.19

Example see:
	https://www.maetes.com/de/povray-os/ma_helpers#ma_getparam
	https://www.maetes.com/en/povray-os/ma_helpers#ma_getparam
	
Params:
	xParams:		String of parameters and values. Both separated by semicolon (;), next pair or param/value followes after semicolon (;)
	xParamName:		The name of the param
	mDefault:		Return-Value if no param was found
Return:
	Mixed:			String/Float/Vector/Boolean, depends on used macro
*/// **************************************************************************************************************************
//Returns a STRING
#macro MA_GetParam_S(xParams, xParamName, mDefault)
	#local mResult = MA_GetParam_Base(xParams, xParamName, MA_GetParam_NoValue);
	#if (mResult = MA_GetParam_NoValue) 	#local mResult = mDefault;	#end
	mResult
#end
//Returns a FLOAT
#macro MA_GetParam_F(xParams, xParamName, mDefault)
	#local mResult = MA_GetParam_Base(xParams, xParamName, "");
	#if (mResult != "")
		#local mResult = val(mResult);
	#else
		#local mResult = mDefault;
	#end
	mResult
#end
//Returns a VECTOR
#macro MA_GetParam_V(xParams, xParamName, mDefault)
	#local mResult = MA_GetParam_Base(xParams, xParamName, "");
	#if (mResult != "")
		#local mResult = MA_String2Vector(mResult);
	#else
		#local mResult = mDefault;
	#end
	mResult
#end
//Returns a BOOL (true or false, use parameter without value for it, "door-open" instead of "door-open: 1")
#macro MA_GetParam_B(xParams, xParamName)
	#local mResult = MA_GetParam_Base(xParams, xParamName, "false");
	#if (mResult = "false")
		#local mResult = false;
	#else
		#local mResult = true;
	#end
	mResult
#end

//The macro used of all macros above
#macro MA_GetParam_Base (xParams, xParamName, mNoValue)
	#local mParamName	= strlwr(xParamName);
	#local mParams		= xParams;
	#local mReturn		= mNoValue;
	#local mArray		= MA_GetParam_GetCachedArray(xParams);
	
	#for (mIndex, 0, dimension_size(mArray,1) - 1)	//MA_Terminal_S("Pair", mArrayParamPairs[mIndex])

		#local mParam	= strlwr(mArray[mIndex][0]);
		#local mValue	= mArray[mIndex][1];
		
		//Look for param without value (boolean-like). If found, set value = param-name
		#if (mValue = "")
			#if (mParam = mParamName)	//MA_Terminal_S("Param without value", mParam)
				//#local mReturn = mNoValue;
				#local mReturn = "";
				//#break //Strange: This break hurts if the param is the last one, dont know why. Seems to break the macro (without return-value), not the for-loop only.
			#end
		//Look for param and value
		#else
			//Pair of param and value
			//#local mValue	= mArrayOneParam[1];		//MA_Terminal_S("Param", mParam)		MA_Terminal_S("Value", mValue)
			//Check if its the requestet param
			#if (mParam = mParamName)					//MA_Terminal_S("Param found", mParam)
				#local mReturn = mValue;
				#if (MA_GetParam_Break = "first")
					#break //With this break the first found param rules, without the break the last will overwrite the first ones (default).
				#end
			#end
		#end
	#end
	//MA_Terminal_S("Return value", mReturn)

	mReturn
#end
//Internal macro to prepare and cache the params.
//Without this and using many params it needs seconds or many seconds only to parse a scene
#macro MA_GetParam_GetCachedArray(xParams)
	#local mParams		= xParams;
	#local mArrayReturn	= false;
	#local mKey			= -1;
	#local mFunc		= "...GetCachedArray";	//For terminal-messages only
	
	#if (MA_GetParam_CacheIndex = 0)
		//Nothing cached at the moment
		#local mKey	= -1;
	#else
		//some are cached, search in strings-array for an identical string to get the key
		#for (mIndex, 0, MA_GetParam_CacheIndex - 1)
			#if (mParams = MA_GetParam_CacheStrings[mIndex])
				#local mKey = mIndex;
				//#break
			#end
		#end
	#end
	#if (mKey > -1)
		//cached
		//MA_Notice(mFunc, concat ("Params (", str(strlen(mParams),1,0), ") are cached"))
		#local mArrayReturn = MA_GetParam_CacheArrays[mKey];
		
	#else
		//not cached, do it
		//MA_Notice(mFunc, "cache new Params")
		
		#local mArrayParamPairs	= MA_String2Array (mParams, ";", 1); //MA_Terminal_F("Param-Dimensions", dimension_size(mArrayParamPairs,1))
		//MA_Terminal_F("dimension-size", dimension_size(mArrayParamPairs,1))
		#local mArrayNew		= array [dimension_size(mArrayParamPairs,1)][2];
		
		#for (mIndex, 0, dimension_size(mArrayParamPairs,1) - 1)	//MA_Terminal_S("Pair", mArrayParamPairs[mIndex])
		
			#local mBoth= MA_String2Array (mArrayParamPairs[mIndex], ":", 1);
			#local mParam = MA_String_TrimChar(mBoth[0], "\t");		//MA_Terminal_S("Param-Name", mParam)
			#local mValue = "";
			
			
			#if (dimension_size(mBoth,1) = 2)
				#local mValue = MA_String_TrimChar(mBoth[1], "\t");
			#end
			#local mArrayNew[mIndex][0]	= mParam;
			#local mArrayNew[mIndex][1]	= mValue;
		#end
		
		#declare MA_GetParam_CacheStrings[MA_GetParam_CacheIndex] = mParams;
		#declare MA_GetParam_CacheArrays [MA_GetParam_CacheIndex] = mArrayNew;
		#declare MA_GetParam_CacheIndex	= MA_GetParam_CacheIndex + 1;
		#local mArrayReturn = mArrayNew;
		
		#if (0) //Saves the new Array to Logfile
			#if (MA_GetParam_CacheIndex = 1) #local mOverwrite = true; #else #local mOverwrite = false; #end
			MA_OutputArrayString(mArrayNew, "", mOverwrite, "cached arrays")
		#end
	#end
	
	mArrayReturn
#end




/* ****************************************************************************************************************************
MA_GetSlope

Alters a linear increase of a value to a more smooth/complex one.
This will help in the most cases to let look a movement more realistic, especially when simulating accelerations.

Types - at the moment available:
	linear		- default
	round 		- rounds up or down (< 0.5 = 0 and >= 0.5 = 1)
	bool 		- boolean (0 = 0, > 0 = 1)
	cosinus		- real unpatched cosinus, a smooth slope, visible different to linear but not far away from it
	cosinus+	- faked cosinus, bit steeper than the real one
	cosinus++	- even more faked and more steep. See note below
	bounce+		- bounces on the floor (like a half sinus-curve)
	bounce-		- bounces at the ceiling
	
Abbreviations for Type:
	l, r, b, c, c+, c++, b+, b-
	
Iterations:
	This is a littlebit strange but i hope it make sense.
	= 1 means at a Value from 0 to 1 it goes up also from 0 to 1. The normal case if you like to smoothen a value.
	= 2 goes up AND at the same rate down.
	And so on. 3 means up, down, up, 4 means up, down, up, down, 20 means up, down, up, down, up, down, up, down, up, down, up, down, up, down, up, down, up, down, up, down, end.

Offset:
	With the offset you can shift the curves, defining the start-point.
	= 0.5 shift half of the way up, starting in the middle on the way upwards
	= 1.0 start at the top of the slope
	= 1.5 starts at the middle of the way down
	= 2.0 same as 0 (default), starting at the bottom.

IMPORTANT NOTE (maybe):
	Type "cosinus++" mathematically doesnt reach 0 or 1 exactly.
	At MA_SlopeCPPFactor = 3.7 (default, see far above) the resultig slope reaches from 0.00117 to 0.99883.
	This isnt very much, at 1000 pixel you lost only a bit more than one Pixel.
	But if you have to do some mathematical calculations or you depend otherwise on real 0s and 1s,
	maybe in compare-function, you should know about this.
	The higher MA_SlopeCPPFactor, the bigger the differences to 0 and 1.
	Check the last if-block inside the macro.
	
Date:
	26.11.2018 and 10.12.2018 and 17.4.2019

See also:
	http://www.f-lohmueller.de/pov_tut/animate/anim220e.htm
	http://www.f-lohmueller.de/pov_tut/animate/anim220d.htm
	http://www.f-lohmueller.de/pov_tut/animate/pov_anid.htm
	http://www.f-lohmueller.de/pov_tut/calc/math_600e.htm
	http://povray.org/documentation/3.7.0/r3_3.html#r3_3_1_5_4		

Examples:
	MA_Terminal_F ("slope-1", MA_GetSlope(0.7, "linear",1,0))		//linear	, 1, 0
	MA_Terminal_F ("slope-2", MA_GetSlope(0.7, "cosinus+",4,-0.5))	//cosinus+	, 4, -0.5
	MA_Terminal_F ("slope-3", MA_GetSlope(0.7, "bounce+",1,0))		//bounce+	, 1, 0
	#local mySlope = MA_GetSlope(0.7, "", 1, 0 );					//linear	, 1, 0

Params:
	xValue:		Value, ranging from 0 to 1. Or higher, but than i guarantee for nothing.
	mType		Type of slope. See description above.
	mIterations	Number of
	mOffset		Shifting of the curve
	
Return:
	float		from 0 to 1
*/// **************************************************************************************************************************
#macro MA_GetSlope ( xValue, mType, mIterations, mOffset )

	#local mValue = xValue * mIterations + mOffset;
	#local mValue = mod(mValue,2); //Allow values only from 0 to 2 (or 1.9999999) so 2.5 will turn into 0.5

	#if		(mValue > 1)	#local mValue	= 2 - mValue;		#end	//falling slope instead of a rising when bigger 1
	#if		(mType  = "")	#local mType	= MA_SlopeDefault;	#end	//Set Default-Type
	
	//The shortcuts (l, c+ s, ...) may change over time, so use with care
	#if		(mType = "linear"	| mType = "l")		#local mResult	= mValue;						//Linear
	#elseif	(mType = "bool"		| mType = "b")		#local mResult	= ceil(mValue);					//bool (0 = 0, > 0 = 1)
	#elseif	(mType = "bounce+"	| mType = "b+")		#local mResult	= sin(pi*mValue/2);				//bounces on the floor (or up)
	#elseif	(mType = "bounce-"	| mType = "b-")		#local mResult	= 1-sin(pi*mod(mValue+1, 2)/2);	//bounces at the ceiling (or down)
	#elseif	(mType = "cosinus"	| mType = "cosine"	| mType = "c")									//Cosinus
		#local mResult	= 0.5-0.5*cos(pi*mValue);													
	#elseif	(mType = "cosinus+"	| mType = "cosine+"	| mType = "c+")									//Special, bit steeper than cosinus
		#local mResult	= mValue*mValue*mValue*(10+mValue*(6*mValue-15));		
	#elseif	(mType = "cosinus++"| mType = "cosine++"| mType = "c++")								//even steeper cosinus
		#local mFactor	= (MA_SlopeCPPFactor-1) * 5;
		#local mResult	= 1/(1+exp(-mValue*mFactor+mFactor/2));
	#elseif	(mType = "round"	| mType = "r")														//rounds up or down (< 0.5 = 0 and >= 0.5 = 1)
		#local mResult	= 0;
		#if (mValue >= 0.5 & mValue <= 1.5)
			#local mResult	= 1;
		#end
	#else
		MA_Warning("MA_GetSlope", concat ("unknown mType '", mType, "', returning linear-value"))
		#local mResult = mValue;
	#end

	//For development only, turn false into true to output the result
	#if (false & (mValue = 0 | mValue = 1))
		MA_Terminal(concat("Type ", MA_String_Pad(mType, 9, " ", "left"), " - Value: ", str(mResult,0,5)))
	#end
	mResult
#end


/* ****************************************************************************************************************************
MA_GetSlope_ByString

Works as (and uses) MA_GetSlope(), but the three parameters mType, mIterations, mOffset can delivered in one String.
Separated by comma or semikolon, one, two, three or even null parameters allowed, everything will get trimmed also.

Examples:
	MA_Terminal_F ("slope-1", MA_GetSlope_ByString(0.7, "linear,1,0"))		//linear	, 1, 0
	MA_Terminal_F ("slope-2", MA_GetSlope_ByString(0.7, "cosinus+,4,-0.5"))	//cosinus+	, 4, -0.5
	MA_Terminal_F ("slope-3", MA_GetSlope_ByString(0.7, "bounce+"))			//bounce+	, 1, 0
	#local mySlope = MA_GetSlope_ByString(0.7, "");							//linear	, 1, 0
Params:
	xValue:		The value, see MA_GetSlope() for further description
	mTypeMixed	the three parameters that define a slope, see MA_GetSlope().
Return:
	float		from 0 to 1
*/// **************************************************************************************************************************
#macro MA_GetSlope_ByString ( xValue, mTypeMixed )
	#local mValue				= xValue;
	
	//Defaults
	#local mDefaultType			= "linear";
	#local mDefaultIterations	= "1";
	#local mDefaultOffset		= "0";

	//Prevents Errors (later)
	#local mType		= "";
	#local mIterations	= "";
	#local mOffset		= "";
	
	//Check mTypeMixed on multiple parameters, can be separated by ',' or ';' 
	#local mSepBySemicolon	= MA_String2Array(mTypeMixed, ";", true)
	#local mSepByComma		= MA_String2Array(mTypeMixed, ",", true)
	
	//Checks wich array is longer - that splitted by semicolons or by comma.
	//Uses the bigger one.
	#if (dimension_size(mSepBySemicolon,1) >= dimension_size(mSepByComma,1))
		#local mSize = dimension_size(mSepBySemicolon,1);
		#if (mSize > 0)	#local mType		= mSepBySemicolon[0]; #end
		#if (mSize > 1)	#local mIterations	= mSepBySemicolon[1]; #end
		#if (mSize > 2)	#local mOffset		= mSepBySemicolon[2]; #end
	#else
		#local mSize = dimension_size(mSepByComma,1);
		#if (mSize > 0)	#local mType		= mSepByComma[0]; #end
		#if (mSize > 1)	#local mIterations	= mSepByComma[1]; #end
		#if (mSize > 2)	#local mOffset		= mSepByComma[2]; #end
	#end

	//Defaults
	#if (mType			= "")	#local mType		= mDefaultType;			#end
	#if (mIterations	= "")	#local mIterations	= mDefaultIterations;	#end
	#if (mOffset		= "")	#local mOffset		= mDefaultOffset;		#end

	//Convert into floats, as MA_GetSlopes() expects its parameters
	#local mIterations	= val(mIterations);
	#local mOffset		= val(mOffset);

	//For development only
	#if (0)
		MA_Terminal_S("MA_GetSlope_ByString->Type  :", mType)
		MA_Terminal_F("MA_GetSlope_ByString->Inter :", mIterations)
		MA_Terminal_F("MA_GetSlope_ByString->OffSet:", mOffset)
	#end
	
	#local mReturn = MA_GetSlope ( mValue, mType, mIterations, mOffset );
	mReturn
#end




/* ****************************************************************************************************************************
MA_SetClock

Only a minor macro to avoid if-blocks. It sets the global variable MA_Clock ...
= clock  -> if the clock and an animation is running
= mValue -> if the clock isnt running, as a standard-value

Date:18.4.2019

Example
	//Moves the sphere during animation along the +z-axis or set it at +2.5 (halfway) if the clock isnt running.
	MA_SetClock(0.5)
	object { sphere { 0,1 } translate <0, 5*MA_Clock, 0> }	

Params:
	mValue:			Float, from 0 to 1
*/// **************************************************************************************************************************
#macro MA_SetClock (mValue)
	/*
	#if (clock_on = true)
		#declare MA_Clock = clock;	//Copy the value from the original povray-delivered clock-constant
	#else
		#declare MA_Clock = mValue;	//Standard-Value if clock isnt running
	#end
	*/
	#if (clock_on = true)
		//Copy the value from the original povray-delivered clock-constant
		#declare MA_Clock = clock;	
	#else
		#if ( MA_CmdClock >= 0 & MA_CmdClock <= 1)
			//Animation running from CMD-Line.
			//See Parameter at the very end:
			//povray +Ima_3d_creator_anim1.pov +Oanim1/pic_l_0004.png +w1280 +h720 -GA -D +A Declare=MA_CmdStereo=-1 Declare=MA_CmdClock=0.3
			#declare MA_Clock = MA_CmdClock;		
		#else
			//Use Macro-Parameter
			//Standard-Value if clock isnt running
			#declare MA_Clock = mValue;	
		#end
	#end
	
#end

//Call the macro to fill the variable instantly with "clock" if clock_on = true
MA_SetClock(MA_Clock)	


/* ****************************************************************************************************************************
Saves the content of a string-array to a file.

One or two dimensions, doesnt matter.

11.12.2018

Examples:
	//Both arrays gets declared in ma_helpers.inc
	MA_OutputArrayString(MA_TestArray_2D, "", 1, "2D-Array")
	MA_OutputArrayString(MA_TestArray_1D, "", 0, "")

Todo/issues:
	Check if file-opening was successfull, otherwise raise error
	Global variable for standard-filename
	Maybe if/else and global variable for output of quotes around the cell-contents.

See also:
	http://povray.org/documentation/3.7.0/r3_3.html#r3_3_2_3

Params:
	mArray:			1 or 2-dimensional Array (completely string-formatted)
	xFile:			Name and path of the file. Leave blank if you want to use a standard-name.
	mOverwrite:		1 or true will open empty file, 0 or false will append to a maybe existing one.
	mComment:		A text that identifys the output
	mHighestIndex	Everything above will be ignored, ends the macro early (optional, Base only).

*/// **************************************************************************************************************************
#macro MA_OutputArrayString(mArray, xFile, mOverwrite, mComment)
	MA_OutputArrayString_Base(mArray, xFile, mOverwrite, mComment, 999999999)
#end
#macro MA_OutputArrayString_Base(mArray, xFile, mOverwrite, mComment, mHighestIndex)
	#if (xFile = "")
		#local mFile = "ma_logfile.txt";
	#else
		#local mFile = xFile;
	#end

	//----------------------------------------------------
	//Find longest content of each col to pad it later
	//----------------------------------------------------
	#if (dimension_size(mArray,2) > 0)
		#local mMaxChars = array[dimension_size(mArray,2)];
		#for (mCol, 0, dimension_size(mArray,2) - 1)			//MA_Terminal_F("mCol", mCol) 
			#local mMaxChars[mCol] = 0;
			#for (mRow, 0, dimension_size(mArray,1) - 1)		//MA_Terminal_F("mRow", mRow)		MA_Terminal_S("Content", mArray[mRow][mCol])
				#ifndef(mArray[mRow][mCol])
					#break; //not initialised field, stop here
				#end
				#if (mMaxChars[mCol] < strlen(mArray[mRow][mCol]))
					#local mMaxChars[mCol] = strlen(mArray[mRow][mCol]);
				#end
				#if (mRow >= mHighestIndex) #break; #end	//Enough done, end the loop here.				
			#end
		#end	//MA_OutputArrayString(mMaxChars, "log2.txt", 1, "MaxChars")
	#end
	
	//---------------------------------
	//Open File
	//---------------------------------
	#if (mOverwrite = 1)
		#fopen pFile mFile write
		#write (pFile, "This logfile was created by povray, file ma_helpers*.inc, macro MA_OutputArrayString_Base().\n")
	#else
		#fopen pFile mFile append
	#end
	
	//---------------------------------
	//Output of some informations
	//---------------------------------
	#write (pFile, "\n\n")
	#write (pFile, "------------------------------------------------------------------\n")
	#if (mComment != "")
		#write (pFile, "Comment: ", mComment, "\n")
	#end
	#write (pFile, "Rows   : ", dimension_size(mArray,1), "\n")
	#if (dimension_size(mArray,2) > 0)
		#write (pFile, "Cols   : ", dimension_size(mArray,2), "\n")
	#end
	#write (pFile, "------------------------------------------------------------------\n")

	//---------------------------------
	//Output all rows and cols
	//---------------------------------
	#for (mRow, 0, dimension_size(mArray,1) - 1)	
		#local mLine = "";
		
		#if (dimension_size(mArray,2) = 0)
			//1D-Array
			#local mLine = mArray[mRow];
		#else
			//2D-Array
			#for (mCol, 0, dimension_size(mArray,2) - 1)
				#ifndef(mArray[mRow][mCol])
					#break; //not initialised field, stop here
				#end
			
				#local mCell = MA_String_Pad(mArray[mRow][mCol], mMaxChars[mCol]+1, "", "left");
				//#local mCell = concat ("\"", MA_String_Pad(mArray[mRow][mCol], mMaxChars[mCol]+3, "", "left"), "\"" );
				//#local mCell = MA_String_Pad(concat ("\"", mArray[mRow][mCol], "\""), mMaxChars[mCol]+3, "", "left");
				//#local mCell = MA_String_Pad(concat ("'", mArray[mRow][mCol], "'"), mMaxChars[mCol]+3, "", "left");
				#local mLine = concat (mLine, ",", mCell);
			#end
			#local mLine = MA_String_TrimChar(mLine, ",");
		#end
		
		#write (pFile, mLine, "\n")
		#if (mRow >= mHighestIndex) #break; #end	//Enough done, end the loop here.
	#end

	//---------------------------------
	//Finishing
	//---------------------------------
	#write (pFile, "------------------------------------------------------------------\n")
	#fclose pFile
	MA_Terminal (concat("MA_OutputArrayString: Array successfully written to file \"", mFile, "\""))

#end





/* ****************************************************************************************************************************
Includes a file

More or less only in use in some of my own macros.
Searches in some folders if the include-file exists. If it does, it gets included.
MA_Include_Void doesnt return anything
Date: 10.9.18 / 26.6.19

Example:
	#ifdef( MA_Helpers_Temp)
		MA_Include_Void("really_nice_objects.inc", "")
	#end
Example 2:
	#local mFolderSkyfakes = MA_Include("ma_skyfakes.inc", "");
	#if (mFolderSkyfakes != "-1")
		MA_Terminal("Skyfakes found")
		#include concat(mFolderSkyfakes, "ma_skyfakes_textures.inc")
	#else
		MA_Terminal("Skyfakes NOT found")
	#end

Params:
	mFile:			Name of the file, without folder
	mBasePath:		From wich the search starts and goes on level down + up.
					Leave blank and search will start where you execute your script.
Return:
	"-1" (as a string!) or the path (if not empty with "/" at the end but without filename) where to find the file (for later include textures in the same path)
*/// **************************************************************************************************************************

#macro MA_Include_Void (mFile, mBasePath)
	#local nevermind = MA_Include (mFile, mBasePath); //Tries to include the file but doesnt care about the result
#end
#macro MA_Include (mFile, mBasePath)

	#local mFolder 		= "";
	#local mReturn		= "-1";
	#local mFoldersAll	= MA_Include_Folders;
	#local mFoldersAll2	= MA_Include_Folders;

	#declare MA_Include_Last_Path = "";

	//Private folder = filename without extension.
	#local mSplitted	= MA_String2Array(mFile, ".", true);
	#if (dimension_size(mSplitted, 1) = 2)
		#local mFolder = mSplitted[0];
	#end
	#if(MA_Include_Talk = true)  MA_Terminal_S("Private folder: ", mFolder) #end
	#if(MA_Include_Talk = true)  MA_Terminal_F("Inc-Folders: ", dimension_size(mFoldersAll2,1)) #end
	
	
	#if (mFolder != "")
		#for (mIndex, 0, dimension_size(mFoldersAll2,1) - 1)
			#local mFoldersAll2[mIndex] = concat(mFoldersAll2[mIndex], mFolder, "/");
		#end
		#local mFoldersAll	= MA_Array_Merge(mFoldersAll, mFoldersAll2);
		//#local mFoldersAll	= mFoldersAll2;
	#end
	
	//Check if BasePath ends with "/"
	//If not and not empty, concat one.
	#if (mBasePath != "")
		#local mLastChar = substr(mBasePath, strlen(mBasePath), 1);
		//#if(MA_Include_Talk = true) MA_Terminal_S("Include: LastChar", mLastChar) #end
		#if (mLastChar != "/")
			#local mBasePath = concat (mBasePath, "/");
		#end
	#end

	//My own files often are in a forlder with similar name.
	//ma_skyfakes.inc lies in "ma_skyfakes/ma_skyfakes.inc" or "../ma_skyfakes/ma_skyfakes.inc"
	//So delete ".inc" from mFile, use this as a folder-name and try to find the file there.
	
	//#local mSplitted	= MA_String2Array(mFile, ".", true);
	//#if (dimension_size(mSplitted, 1) = 2)
	//	#local mFolder = mSplitted[0];
	
	#if (mFolder != "")
		//Also check if between v1 and .inc some text exists, maybe "ma_skyfakes_v1_textures.inc"
		//Delete this text too, there will be no folder named "ma_skyfakes_v1_textures".
		#local mParts = MA_String2Array(mFolder, "_", true);
		//MA_Terminal_AS("mParts", mParts)	MA_Terminal_F("mParts-Dims", dimension_size(mParts, 1))
		#for (mDimAct, dimension_size(mParts, 1), 1, -1)
			#local mPartName = mParts[mDimAct-1];		//MA_Terminal_S("mParts-Part", mParts[mDimAct-1])
			#if (MA_Array_FindString (mPartName, array[10] {"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9"}))
				#local mParts = MA_Array_Cut(mParts, 0, mDimAct - 1, 0);		//MA_Terminal_AS("mParts-New", mParts)
				#local mFolder = MA_ArrayString2String(mParts, "_");
				#break
			#else
				//#if(MA_Include_Talk = true) MA_Terminal_S("NOT last part", mPartName) #end
			#end
		#end
	
		#local mFoldersNew = array[2] {
			concat(mFolder, "/"),
			concat("../", mFolder, "/")
		}
		//#local mFoldersAll	= MA_Array_Merge(mFoldersNew, mFoldersAll);
		#local mFoldersAll	= MA_Array_Merge(mFoldersAll, mFoldersNew);
	#end
	
	//Walk through all folders and look for the file.
	//If found, include.
	#for (mIndex, 0, dimension_size(mFoldersAll,1) - 1)
		#local mFolder		= mFoldersAll[mIndex];
		#local mFilePath	= concat(mBasePath, mFolder, mFile);
		#if(MA_Include_Talk = true)  MA_Terminal_S("Include: Checking file", mFilePath) #end
		//#if(MA_Include_Talk = true)  MA_Terminal_S("Include: Checking folder", mFolder) #end
		#if (file_exists(mFilePath))
			#include mFilePath
			#declare MA_Include_Last_Path = concat(mBasePath, mFolder);
			#if(MA_Include_Talk = true)  MA_Terminal_S("Include: Found", mFilePath) #end
			#local mReturn		= mFolder;
			#break
		#else
			#if(MA_Include_Talk = true)  MA_Terminal("Include: File not found") #end
		#end
	#end
	mReturn
#end




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


Strings


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



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

MA_String_Pad

Pads a string to the given length
Almost at the same was as str_pad() in PHP
https://www.php.net/manual/en/function.str-pad.php

11.12.2018

Todos/Issues:
	Maybe PadPos "center" with even/odd?

Examples:
	#local Test = MA_String_Pad("Bongo", "-", 10, "l");		// will return "-----Bongo"
	#local Test = MA_String_Pad("Bongo", "-", 8, "right");	// will return "Bongo---"

Params:
	xString:		The string to pad
	mLength:		Complete length of the string to be returned
	xPadChar:		Padding-char
	mPadPos:		"left" or "right" (or short "l" and "r")
Return:
	String			The padded string
*/// **************************************************************************************************************************
#macro MA_String_Pad(xString, mLength, xPadChar, mPadPos)

	#local mPadChar = xPadChar;
	#if (xPadChar = "")
		#local mPadChar = " ";
	#end
	#local mString = xString;
	#while (strlen(mString) < mLength)
		#if (mPadPos = "l" | mPadPos = "left")
			#local mString = concat(mPadChar, mString);
		#else
			#local mString = concat(mString, mPadChar);
		#end
	#end
	mString
#end



/* ****************************************************************************************************************************
MA_String_Trim
MA_String_TrimChar

Skips all chars at the start and the end of the string.
MA_String_Trim() trims spaces only, MA_String_TrimChar() the given Char.

Date:
	30.1.17 / 18.8.18
Params:
	Param:		String to trim
	Char:		Char to look for (space in case of MA_String_Trim)
Return:
	String:		trimmed string
*/// **************************************************************************************************************************
#macro MA_String_Trim		(mParam)		MA_String_TrimChar (mParam, " ")		#end
#macro MA_String_TrimChar	(mParam, mChar)
	#local mReturn = mParam;
	//#if (strlen(mParam) > 1)
	//	#while (substr(mReturn, 1, 1) = mChar)					#local mReturn = substr(mReturn, 2, strlen(mReturn)-1);	#end 	//L-Trim
	//#end
	#if (strlen(mReturn) != 0)
		#while (strlen(mReturn) > 1 & substr(mReturn, 1, 1) = mChar)	#local mReturn = substr(mReturn, 2, strlen(mReturn)-1);	#end 	//L-Trim
		#if (strlen(mParam) > 1)
			#while (substr(mReturn, strlen(mReturn), 1) = mChar)	#local mReturn = substr(mReturn, 1, strlen(mReturn)-1);	#end	//R-Trim
		#end
	#end
	mReturn
#end






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


Mixed

A mixed variable in my world is nothing else than a vector formatted as a string.
Needed this to save strings and (pseudo-) vectors inside the same array.
so <1,2,3.3,4.0,5,6> will be "1,2,3.3,4.0,5,6"


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


/* ****************************************************************************************************************************
MA_Mixed_SumValues

Returns the sum of both Mixed-Types.


Example:

#local lala = MA_Mixed_SumValues ("1,2,300", "3, 4.5, -17")	//will return "4, 6.5, 283"

Date:
	17.12.2023
Params:
	mMixed1:	Mixed string
	mMixed2:	Mixed string
Return:
	Mixed:		Mixed string
*/// **************************************************************************************************************************

#macro MA_Mixed_SumValues (mMixed1, mMixed2)

	#local aMixed1 = MA_String2ArrayFloat (mMixed1, ",", 1) 
	#local aMixed2 = MA_String2ArrayFloat (mMixed2, ",", 1) 
	#local aNew = array [dimension_size(aMixed1,1)]
	#for (nX, 0, dimension_size(aMixed1,1)-1)
		#local aNew[nX] = aMixed1[nX] + aMixed2[nX];
	#end
	
	#local mResult = MA_ArrayFloat2String (aNew, ",")		//MA_Terminal_S("Mixed Sum Value", mResult)
	
	mResult

#end






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


Arrays - 1D


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



/* ****************************************************************************************************************************
MA_Array_FindString

Search for a string inside a simple 1d-array of strings.
Returns only true/false, no Index, only for usage as simple/fast as possible in Ifs.
Not Case-sensitive AND it ignores empty strings!

Example:
#local City = "cologne";
#if (MA_Array_FindString(City, array[3] { "Bielefeld", "Cologne", "Posemuckel" }))
	MA_Terminal ("Yes man")
#end
// or ...
#if (!MA_Array_FindString(City, array[3] { "Bielefeld", "Cologne", "Posemuckel" })) ...

Date:
	10.9.2018
Params:
	mString:	String to search for
	mArray:		2d-array
Return:
	Bool:		true/false 
*/// **************************************************************************************************************************
#macro MA_InArray			( mString, mArray ) MA_Array_FindString ( mString, mArray ) #end //Shortcut only
#macro MA_Array_FindString	( mString, mArray )
	#local mReturn = false; 
	#if (mString != "")
		#for (mIndex, 0, dimension_size(mArray,1) - 1)
			#if (strlwr(mString) = strlwr(mArray[mIndex]))
				#local mReturn = true;
				#break
			#end
		#end
	#end
	mReturn
#end


/* ****************************************************************************************************************************
MA_Array_Merge

Creates a new array that contains all entries from both arrays.
Works only for easiest 1d-Array.
Should work for float-, string-, and vector-arrays.

7.4.2018

Params:
	mArray1+2:	The arrays to merge
Return:
	Array:		Resulting 1d-array
*/// **************************************************************************************************************************
#macro MA_Array_Merge (mArray1, mArray2)

	#local mDims1		= dimension_size(mArray1,1);
	#local mDims2		= dimension_size(mArray2,1);
	#local mArrayNew	= array[mDims1 + mDims2];
	#for (mIndex1, 0, mDims1 - 1)
		#local mArrayNew[mIndex1] = mArray1[mIndex1];
	#end
	#for (mIndex2, 0, mDims2 - 1)
		#local mArrayNew[mDims1 + mIndex2] = mArray2[mIndex2];
	#end

	mArrayNew
#end




/* ****************************************************************************************************************************
MA_Array_Cut

Cuts a 1D-Array, returns only a part of it.
Doesnt matter if it contains strings or floats or whatever.
I hope. Tested with strings and floats.

Example:
	#local mArray1 = array[5] { "one", "two", "three", "four", "five" };
	#local mArray2 = array[5] { 1, 2, 3, 4, 5 };

	#local mArray  = MA_Array_Cut(mArray1, 1, 3,-1) //returns { "two", "three", "four" }
	#local mArray  = MA_Array_Cut(mArray2, 0,-1, 3) //returns { 1, 2, 3, 4 }

Date:
	16.4.2019
Params:
	mArray		Array to cut
	mIndexFirst	The first index to survive
	mIndexLast	The last index  to survive	- set it to 0 if you like to work with mRows
	mRows		Number of rows				- set it to 0 if you like to work with mIndexLast
Return:
	Array
*/// **************************************************************************************************************************

#macro MA_Array_Cut ( mArray, mIndexFirst, mIndexLast, mRows )

	//Check out the dimension of the new array
	#if (mIndexLast < 0 & mRows <0)		//Both not given, take as much as available (from mIndexFirst on)
		#local mDim = 999999;
	#elseif (mIndexLast > 0)			//Use Index-Last
		#local mDim = mIndexLast - mIndexFirst + 1;
	#else								//Use number of rows
		#local mDim = mRows;
	#end
	
	//Dont allow bigger dimensions than available
	//if you order more than available the array will be smaller than expected. No empty fields here.
	#if (mDim > dimension_size(mArray,1) - mIndexFirst)
		#local mDim = dimension_size(mArray,1) - mIndexFirst;
	#end
	
	//Create the new Array
	//MA_Terminal_F("New array-dimension", mDim)
	#if (dimensions(mArray) = 1)
		#local mArrayNew = array[mDim];
	#end
	#if (dimensions(mArray) = 2)
		#local mArrayNew = array[mDim][dimension_size(mArray,2)];
	#end
	
	//Copy all nessecary fields from the old into the new
	#for (mIndex, mIndexFirst, dimension_size(mArray,1) - 1)		//MA_Terminal_F("Dimensions in array", dimensions(mArray))
		#if (dimensions(mArray) = 1)
			#local mArrayNew[mIndex - mIndexFirst] = mArray[mIndex];
		#end
		#if (dimensions(mArray) = 2)	//MA_Terminal_F("dimension_size(mArray,2)", dimension_size(mArray,2))
			#for (mIndex2, 0, dimension_size(mArray,2) - 1)
				#local mArrayNew[mIndex - mIndexFirst][mIndex2] = mArray[mIndex][mIndex2];
			#end
		#end
		#if (mDim = mIndex - mIndexFirst + 1)
			#break
		#end
		
	#end
	
	mArrayNew
#end





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


Arrays - 2D


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



/* ****************************************************************************************************************************
MA_Array2D_Merge

Merges two 2D-Arrays
Both can have 1 or 2 dimesions, BUT THE RESULT WILL HAVE 2 DIMENSIONS ! (alternative: see macro MA_Array_Merge() witout 2D)
They both have to have the same number of cols, or number rows of 1d = number colums of 2d.
Doesnt matter if it contains strings or floats or whatever.

Date:
	12.12.2018
	
Examples:
	#declare MA_TestArray_2D_New			= array[2][4] {
	{	"100",	"onehundred",		"einhundert",		"1-hundertos"		}
	{	"200",	"twohundred",		"zweihundert",		"-2-hundertos"		}
	}
	#declare MA_TestArray_1D_New			= array[4] {	"13",	"thirteen",		"dreizehn",		"@keineahnung"		}
	#local ColSortIndex = 0;		//Choose 0 to 3.
	#local NewArray1 = MA_Array2D_Merge(MA_TestArray_2D, MA_TestArray_2D_New);
	//#local NewArray2 = MA_Array2D_MergeAndSort(MA_TestArray_2D, MA_TestArray_2D_New, ColSortIndex, "a", "s");
	//#local NewArray2 = MA_Array2D_MergeAndSort(MA_TestArray_1D_New, MA_TestArray_2D, ColSortIndex, "a", "s");
	#local NewArray2 = MA_ArrayString2D_MergeAndSort(MA_TestArray_2D, MA_TestArray_1D_New, ColSortIndex, "a", "s");
	#local NewArray3 = MA_Array2D_Merge(MA_TestArray_1D_New, MA_TestArray_2D);
	#local NewArray3 = MA_ArrayString2D_Sort(NewArray3, ColSortIndex, "a", "s");
	MA_OutputArrayString(MA_TestArray_2D,	"", 1, "Testarray original"		)
	MA_OutputArrayString(NewArray1,			"", 0, "merged two 2d"					)
	MA_OutputArrayString(NewArray2,			"", 0, "merged 1d + 2d and sorted"		)
	MA_OutputArrayString(NewArray3,			"", 0, "merged 1d + 2d"		)
	
Params:
	xArray1/xArray2:	2- arrays to merge
Return:
	Array
*/// **************************************************************************************************************************

#macro MA_Array2D_Merge(xArray1, xArray2)

	#local mArray1 = xArray1;
	#local mArray2 = xArray2;

	//--------------------------------------------------------
	//Get dimensions
	//--------------------------------------------------------
	#local mNumRows1			= dimension_size(mArray1,1);
	#local mNumCols1			= dimension_size(mArray1,2);
	#local mNumRows2			= dimension_size(mArray2,1);
	#local mNumCols2			= dimension_size(mArray2,2);
	
	//--------------------------------------------------------
	//Transform 1d-arrays into 2d (with one row)
	//--------------------------------------------------------
	#if(mNumCols1 = 0)
		#local mArray1 = array[1][mNumRows1];
		#for (mRow, 0, mNumRows1 - 1)	
			#local mArray1[0][mRow]	= xArray1[mRow];
		#end
		#local mNumRows1			= dimension_size(mArray1,1);
		#local mNumCols1			= dimension_size(mArray1,2);
	#end
	#if(mNumCols2 = 0)
		#local mArray2 = array[1][mNumRows2];
		#for (mRow, 0, mNumRows2 - 1)	
			#local mArray2[0][mRow]	= xArray2[mRow];
		#end
		#local mNumRows2			= dimension_size(mArray2,1);
		#local mNumCols2			= dimension_size(mArray2,2);
	#end

	//--------------------------------------------------------
	//Merge both into one
	//--------------------------------------------------------
	#if (mNumCols1 != mNumCols2)
		//error
		#error concat("*** MA_Array2D_Merge-ERROR ***: Number of columns in mArray1 and mArray2 not the same. Columns: ", Str(mNumCols1), "/", Str(mNumCols2))
		false
	#else
		//-------------------------------
		//Create new array with
		//as many rows as in both arrays
		//-------------------------------
		#local mArrayNew = array [mNumRows1 + mNumRows2][mNumCols1];

		//-------------------------------
		//Copy array1 into new one
		//-------------------------------
		#for (mRow, 0, mNumRows1 - 1)	
			#for (mCol, 0, mNumCols1 - 1)
				#local mArrayNew[mRow][mCol]				= mArray1[mRow][mCol];
			#end
		#end
		//-------------------------------
		//Copy array2 into new one
		//-------------------------------
		#for (mRow, 0, mNumRows2 - 1)	
			#for (mCol, 0, mNumCols2 - 1)
				#local mArrayNew[mRow + mNumRows1][mCol]	= mArray2[mRow][mCol];
			#end
		#end
	#end
	mArrayNew
#end




/* ****************************************************************************************************************************
MA_ArrayString2D_FindRow

Searches for a value inside the given col of a 2D-string-array.
Returns the index of the found row. The index is 0-based so 3rd row will return value 2

Example:
#declare test = array[4][2] {
	{ "berlin",  "3000000" }
	{ "hamburg", "2000000" }
	{ "cologne", "1000000" }
	{ "munich",   "500000" }
}
MA_Terminal_F("found row", MA_ArrayString2D_FindRow ( "cologne", test, 0)) => will return 2

Date:
	24.9.2019
Params:
	mArray:		2d-array
	mString:	String to search for
Return:
	Float:		Index of the found row or -1 if nothing was found
*/// **************************************************************************************************************************
//#macro MA_ArrayString2D_FindRow ( mString, mArray, mCol)
#macro MA_ArrayString2D_FindRow (mArray, mString, mCol)
	#local mReturn = -1; 
	#for (mIndex, 0, dimension_size(mArray,1) - 1)
		//Check if exists (array big enough but not filled)
		#ifdef(mArray[mIndex][mCol])
			#if (strlwr(mString) = strlwr(mArray[mIndex][mCol]))
				#local mReturn = mIndex;
				#break
			#end
		#end
	#end
	mReturn
#end


/* ****************************************************************************************************************************
MA_ArrayString2D_FindRow_Multi

Similar to MA_ArrayString2D_FindRow, but searches for 2 different values in 2 columns, both have to match.

Date:
	somewhen 2024
Params:
	mArray:			2d-array
	mString1-2:		Strings to search for
	mCol1+2:		Columns to search in
	mIndexFirst:	Where to start
	mIndexLast:		Where to stop
Return:
	Float:			Index of the found row or -1 if nothing was found
*/// **************************************************************************************************************************

#macro MA_ArrayString2D_FindRow_Multi ( mArray, mString1, mString2, mCol1, mCol2, mIndexFirst, mIndexLast)
	#local mReturn = -1; //if nothing was found
	#for (mIndex, mIndexFirst, dimension_size(mArray,1) - 1)
		#if (mIndex > mIndexLast | mIndexLast = -1)
			#local mReturn = -1; //if nothing was found
			#break
		#end
		#if (strlwr(mString1) = strlwr(mArray[mIndex][mCol1]) & strlwr(mString2) = strlwr(mArray[mIndex][mCol2]))
			#local mReturn = mIndex;
			#break
		#end
	#end
	mReturn
#end




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

MA_ArrayString2D_Sort

Sorts a 2d-string-array

Procedure:
	Creates a new, empty array the same size and fills in the 1st row of the old array.
	Than it checks, if the value of mColSort in the 2nd row of the old array ist smaller/bigger than mColSort in the new Array.
	If it is, it moves the first line of new one down and fills in the 2nd old-row where the 1st one in the new previously was.
	And so on. Bit complicated to describe with medium-englisch, sorry.
	The approach for every row is from below, check who is the smaller/bigger and walk the array up till it found its place.
	Like jumping in the queue till the guy in front has wider shoulders.

Date: 11.12.18

Compare-Type:
	Use the Comp-Type to decide, what calculation will be used sort the array, string or number (or correct: alphanumeric or numeric).
	"10" as a string is smaller than "2" (first char rulez), so use at least for completely number-filled sortcolums Comp-Type "number".

Todos/Issues:
	In case of two equal items in ColSort, there are no further checks, which one is the smaller (which row).
	I really dont wanted to go another way and another way and so on.
	This stuff works nice for me, did cost me hours, dont want to invest days.

Examples:
	#local ColSortIndex = 0;		//Choose 0 to 3 (the test-array has 4 columns)
	#local NewArray1 = MA_ArrayString2D_Sort(MA_TestArray_2D, ColSortIndex, "asc", "string")
	#local NewArray2 = MA_ArrayString2D_Sort(MA_TestArray_2D, ColSortIndex, "desc", "s")
	#local NewArray3 = MA_ArrayString2D_Sort(MA_TestArray_2D, ColSortIndex, "a", "number")
	#local NewArray4 = MA_ArrayString2D_Sort(MA_TestArray_2D, ColSortIndex, "d", "n")
	MA_OutputArrayString(MA_TestArray_2D,	"", 1, "Testarray original"	)
	MA_OutputArrayString(NewArray1,			"", 0, "asc  string"		)
	MA_OutputArrayString(NewArray2,			"", 0, "desc string"		)
	MA_OutputArrayString(NewArray3,			"", 0, "asc  number"		)
	MA_OutputArrayString(NewArray4,			"", 0, "desc number"		)

See also:
	http://povray.org/documentation/3.7.0/r3_3.html#r3_3_1_5_4

Params:
	mArrayOld:			The old/org/unsorted array
	mColSort:			The colum that houses the values to get sorted. Dont forget, the most left column in your array is ColSort = 0.
	xDirection:			"asc" or "desc" (or short: "a" and "d")
	xCompType:			"string" or "number" (or short: "s", "str", "n", "num", "numeric"), see description above.
Return:
	Array:				The sorted array
*******************************************************************************************************************************
*/// **************************************************************************************************************************
#macro MA_ArrayString2D_Sort(mArrayOld, mColSort, xDirection, xCompType)

	//------------------------------------------------------------------
	//Prepare macro-parameter
	//------------------------------------------------------------------
	#local mDirection	= "asc";
	#local mCompType	= "str";
	#if (xDirection = "d" | xDirection = "desc")												#local mDirection = "desc";   #end
	#if (xCompType  = "n" | xCompType  = "num" | xCompType = "number" | xCompType = "numeric")	#local mCompType  = "num";    #end

	//------------------------------------------------------------------
	//Get Array-Dimensions and create a new one of the same size
	//------------------------------------------------------------------
	#local mNumRows		= dimension_size(mArrayOld,1);
	#local mNumCols		= dimension_size(mArrayOld,2);
	#local mArrayNew	= array [mNumRows][mNumCols];

	//------------------------------------------------------------------
	//Fill all cells with a string to format them.
	//If all goes well maybe useless, but during development nice to have.
	//Povray can be very bitchy without that.
	//------------------------------------------------------------------
	#if (0)
		#for (mRow, 0, mNumRows - 1)	
			#for (mCol, 0, mNumCols - 1)
				#local mArrayNew[mRow][mCol] = "";
			#end
		#end
	#end
	
	//------------------------------------------------------------------
	//Insert the first row directly into the new array
	//------------------------------------------------------------------
	#for (mCol, 0, mNumCols - 1)
		#local mArrayNew[0][mCol]	= mArrayOld[0][mCol];
	#end

	//------------------------------------------------------------------
	//Check one row after the other
	//------------------------------------------------------------------
	#local mMaxRowNew		= 0; //0 is the first
	#for (mRowOld, 1, mNumRows - 1)	
		#local mMaxRowNew = mRowOld - 1;
		#for (mRowNew, mMaxRowNew, 0, -1)
			#local mInsertAndEnd	= false;
			#local mMoveAndNext		= false;
			
			//Yes, I know.
			#if (0)
			#elseif (mCompType = "str" & mDirection = "asc"  & strcmp(mArrayOld[mRowOld][mColSort], mArrayNew[mRowNew][mColSort]) >= 0) //String, asc
				#local mInsertAndEnd	= true;
			#elseif (mCompType = "str" & mDirection = "desc" & strcmp(mArrayOld[mRowOld][mColSort], mArrayNew[mRowNew][mColSort]) < 0)	//String, desc
				#local mInsertAndEnd	= true;
			#elseif (mCompType = "num" & mDirection = "asc"  & val(mArrayOld[mRowOld][mColSort]) >= val(mArrayNew[mRowNew][mColSort]))	//Number, asc
				#local mInsertAndEnd	= true;
			#elseif (mCompType = "num" & mDirection = "desc" & val(mArrayOld[mRowOld][mColSort]) < val(mArrayNew[mRowNew][mColSort]))	//Number, desc
				#local mInsertAndEnd	= true;
			#else
				#local mMoveAndNext		= true;
			#end
			
			//---------------------------------------------------------------
			//Jump in the queue, therefore move the row before one down.
			//---------------------------------------------------------------
			#if (mMoveAndNext = true)
				#for (mCol, 0, mNumCols - 1)
					#local mArrayNew[mRowNew+1][mCol]	= mArrayNew[mRowNew][mCol];
					#local mArrayNew[mRowNew][mCol]		= "";
				#end
			#end
			//---------------------------------------------------------------
			//Insert it at the actual position. Job for this row done.
			//---------------------------------------------------------------
			#if (mInsertAndEnd = true | (mMoveAndNext = true & mRowNew = 0))
				//Special-Case if the tested line inside ArrayNew is the most upper.
				//Move-Next AND Insert, to do this alter mRowNew (normally inserts happens in the next for-loop, but here we will have no next loop)
				#if (mMoveAndNext = true & mRowNew = 0)
					#local mRowNew			= -1;
				#end
				#local mMaxRowNew = mMaxRowNew + 1;			//MA_Terminal_F("Write Line at Row", mRowNew)
				#for (mCol, 0, dimension_size(mArrayOld,2) - 1)
					#local mArrayNew[mRowNew + 1][mCol]	= mArrayOld[mRowOld][mCol];
				#end
				#local mRowNew = 0;	//End the loop, on an unusual way
				//#break			//Despite the official documentation, this break does break both loops, not the innermost only.
			#end
		#end //end inner-for (mRowNew)
	#end  //end outer-for (mRowOld)

	mArrayNew
#end



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

MA_ArrayString2D_MergeAndSort

easily calls MA_Array2D_Merge() at first and then MA_Array2D_Sort()

Date:
	12.12.2018

Params:
	xArray1/xArray2:	1d or 2d arrays to merge 
	For mColSort, xDirection, xCompType see description of MA_ArrayString2D_Sort
Return:
	Array:				2d-array
*******************************************************************************************************************************
*/// **************************************************************************************************************************

#macro MA_ArrayString2D_MergeAndSort(xArray1, xArray2, mColSort, xDirection, xCompType)
	//At first merge, than sort
	#local mArrayNew = MA_Array2D_Merge(xArray1, xArray2);
	#local mArrayNew = MA_ArrayString2D_Sort(mArrayNew, mColSort, xDirection, xCompType);
	mArrayNew
#end







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


Converts/Transforms


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




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

MA_String2Array

Splits a string and returns an array with all parts.

Date:
	23.3.18 - first version
	 3.5.19 - escaped separators

Example:
	#local gArray = MA_String2Array ("Hiroshima, Dresden, , NSA,,Trump,"   , ",", 1);
	//Will return:	array[7] { "Hiroshima", "Dresden", "", "NSA", "", "Trump", "")
	//Check it by:
	#for (gIndex, 0, dimension_size(gArray,1) - 1)		MA_Terminal_S ("Eintrag", gArray[gIndex]) 	#end
	
Todo:
	Check on Backslashes to avoid commas inside the strings
	Maybe a 4th parameter to ignore empty ones ("bingo, ,,bongo"), so the Example above would return array[4] instead [7]
	Maybe rename in MA_String2Array()

Params:
	mString:	String to split
	mSep:		Separator,most common ',' which is default
	mTrim:		1/true = all strings will be freed of leading and trailing spaces (" "), 0/false = ignores them

Return:
	Array:		one array-dimension for every string
*/// **************************************************************************************************************************
#macro MA_String2Array			(mString, mSep, mTrim)
	MA_String2Array_Base		(mString, mSep, mTrim, "string")
#end
#macro MA_String2ArrayString	(mString, mSep, mTrim)
	MA_String2Array_Base		(mString, mSep, mTrim, "string")
#end
#macro MA_String2ArrayFloat		(mString, mSep, mTrim)
	MA_String2Array_Base		(mString, mSep, mTrim, "float")
#end
#macro MA_String2Array_Base (mString, mSep, mTrim, mDataType)

	#local mNumEsc		= 0;			//Counts the number of escaped separators

	//MA_Terminal_S("Split String", mString)
	#if (mSep = "")		#local mSep		= ",";		#end
	#if (mString = "")	#local mString	= "";	#end //Set a Default if you like (not recommended)
	
	#if (mString = "")
		#if (mDataType = "string")
			//Return the empty string (as an array)
			#local mReturn = array[1] { "" };	
		#else
			#local mReturn = array[1] { 0 };	
			
		#end
	#else
		#local mLen = strlen(mString);
		#local mDim = 1;
		
		//At first, count num separators to find out the array-dimension
		#for (mNum, 1, mLen)
			#if (substr(mString, mNum, 1) = mSep)
				#local mDim = mDim + 1;
			#end
		#end
		//MA_Terminal_F ("Dimensions", mDim)
		
		#local mReturn		= array[mDim];	//Create new array
		#local mStart		= 1;			//Fist char in string is 1 (not 0)
		#local mDim			= 0;			//Array-dimension (first element is index 0)
		#local mNewWord		= "";
		#local mWordLen		= 0;
		#local mCharLast	= "";			//Needed to check on escaped separators
		
		#for (mNum, 1, mLen)
			#local mWordLen	= mWordLen + 1;
			#local mCharAct	= substr(mString, mNum, 1);
			#if (substr(mString, mNum, 1) = mSep | mNum = mLen)
				// --- Separator found or End-Of-Line found
				//Check if its an escaped separator
				#if (mCharLast != MA_String2Array_Esc | mNum = mLen)
					//Valid (not escaped) separator or EOF found
				
					// - End of line, act char is not a separator but part of the word, so increment wordlen
					#if (substr(mString, mNum, 1) != mSep & mNum = mLen)
						#local mWordLen = mWordLen + 1;
					#end
					#local mNewWord = substr(mString, mStart, mWordLen - 1);
					#if (mTrim = 1)
						#local mNewWord = MA_String_Trim(mNewWord);
					#end
					//MA_Terminal_S ("mNewWord", mNewWord) MA_Terminal_F ("mLen", mLen) MA_Terminal_F ("mNum", mNum) MA_Terminal_S ("Char", substr(mString, mNum, 1))
					
					#if (mDataType = "string")
						#local mReturn[mDim]	= mNewWord;
					#else
						#local mReturn[mDim]	= val(mNewWord);
					#end
					#local mWordLen			= 0;
					#local mDim				= mDim + 1;
					#local mStart			= mNum + 1;
				#else
					#local mNumEsc = mNumEsc + 1;
				#end
			#end
			//If this is the last char AND it is a Separator, fill in a empty field
			//To clean the array isnt the job of this macro, it will return what it gets.
			#if (substr(mString, mNum, 1) = mSep & mNum = mLen)
				#if (mDataType = "string")
					#local mReturn[mDim]	= "";
				#else
					#local mReturn[mDim]	= 0;
				#end
			#end
			#local mCharLast = mCharAct;
		#end
	#end
	//If escaped separators were found, cut the array by the number of them
	#if (mNumEsc > 0)
		//MA_Terminal_F("Cut array by", mNumEsc)
		#local mReturn = MA_Array_Cut(mReturn, 0, mDim - mNumEsc, 0);
	#end
	
	mReturn
#end




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

MA_ArrayString2String

Converts a string-array into one string.

Date: 29.11.2018 and 15.04.2019

Params:
	mArray:		The arrays to convert, containing string-items
	mSeparator:	The char between
Return:
	Array:		Resulting array
*/// **************************************************************************************************************************
#macro MA_ArrayString2String (mArray, mSep)
	#local mString		= "";
	#for (mIndex, 0, dimension_size(mArray,1) - 1)
		#local mString = concat(mString, mSep, mArray[mIndex]);
	#end
	#local mString = MA_String_TrimChar (mString, mSep)
	mString
#end


/* ****************************************************************************************************************************
MA_ArrayFloat2String

Converts a float-array into one string.

Date: 29.11.2018 and 15.04.2019

Params:
	mArray:		The arrays to convert, containing float-items
	mSeparator:	The char between
Return:
	Array:		Resulting array
*/// **************************************************************************************************************************

#macro MA_ArrayFloat2String (mArray, mSep)
	#local mString		= "";
	#for (mIndex, 0, dimension_size(mArray,1) - 1)
		#local mString = concat(mString, mSep, Str(mArray[mIndex]));
	#end
	#local mString = MA_String_TrimChar (mString, mSep)
	mString
#end






/* ****************************************************************************************************************************
MA_String2Vector

Transforms a string into a vector.
Input can be "1,1,1" or "<1,1,1>" or "   1   , 1,1  "
Special: string-floats like "1" gets transformed in 3-dimensional vector like <1,1,1>

Example:
	#local Result = MA_String2Vector("1,2,5"); // The same as #local Result = <1,2,5>;
Date:
	18.8.18
Params:
	mString:	The string to transform
Return:
	Vector:		Resulting vector, 2 to 5 dimensions
*/// **************************************************************************************************************************
#macro MA_String2Vector (mString)

	#local mString	= MA_String_TrimChar(mString, " ");
	#local mString	= MA_String_TrimChar(mString, "<");
	#local mString	= MA_String_TrimChar(mString, ">");
	
	#local mArray	= MA_String2Array (mString, ",", 1);
	#local mVector	= MA_ArrayString2Vector (mArray);
	
	mVector
#end

#macro MA_String2Int (mString) MA_String2Integer (mString) #end
#macro MA_String2Integer (mString)
	#local nInt		= int(val(mString));
	nInt
#end
#macro MA_String2Float (mString)
	#local nFloat	= val(mString);
	nFloat
#end

/* ****************************************************************************************************************************
MA_ArrayString2Vector

Transforms a string-containing array into a vector.

Date:
	15.4.19
Params:
	mArray:		The string-array to transform. 
Return:
	Vector:		Resulting vector, 2 to 5 dimensions
*/// **************************************************************************************************************************
#macro MA_ArrayString2Vector (mArray)

	#if		(dimension_size(mArray,1) = 1)
		#local mVector	= <val(mArray[0]), val(mArray[0]), val(mArray[0])>;
	#elseif	(dimension_size(mArray,1) = 2)
		#local mVector	= <val(mArray[0]), val(mArray[1])>;
	#elseif	(dimension_size(mArray,1) = 3)
		#local mVector	= <val(mArray[0]), val(mArray[1]), val(mArray[2])>;
	#elseif	(dimension_size(mArray,1) = 4)
		#local mVector	= <val(mArray[0]), val(mArray[1]), val(mArray[2]), val(mArray[3])>;
	#elseif	(dimension_size(mArray,1) = 5)
		#local mVector	= <val(mArray[0]), val(mArray[1]), val(mArray[2]), val(mArray[3]), val(mArray[4])>;
	#end

	mVector
#end



/* ****************************************************************************************************************************
MA_Vector2String
MA_Vector2String_Base

Transforms a Vector into a String
Nice to save a Vector in a string-declared array
A similar function exists in povray with vstr()
http://povray.org/documentation/3.7.0/r3_3.html#r3_3_1_9_4

MA_Vector2String transforms only 3-dimensional vectors with 3 digits behind the comma (easier/faster to type/use).
MA_Vector2String_Base needs parameters for number of dimension and number of digits.
Both doesnt cry if you send the wrong number of dimensions inside mVector.
It cuts if there more or ads a 0.0 to the existing ones.
I havent found a povray-function to find out the numbers of vector-dimensions (array-dims - yes, vector-dims - no).

Date:
	18.8.18
Params:
	mVector:	The Vector to transform
Return:
	String:		
*/// **************************************************************************************************************************
#macro MA_Vector2String (mVector)
	#local mString = MA_Vector2String_Base (mVector, MA_Vector2String_Dims, MA_Vector2String_Digs)
	mString
#end
#macro MA_Vector2String_Base (mVector, mDimensions, mDigits)
	#local mString = vstr(mDimensions, mVector, ",", 0,mDigits); //Dimensions, Vector, Separator, Useless (0), Decimal-Points
	mString
#end





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

MA_Deg2Vector*

Converts Spherical Degree (Rectascension and Declination)
into a common Povray-Vector (X,Y,Z), also known as Cartesian Coordinates.
RA = 0 will appear along the positiven Z-Axis, otherwise adjust Variable MA_Deg2Vector_RA_Offset (see top of the file).
MA_Deg2Vector_RA_Offset = -90 will move it to the right, so RA = 0 will appear at positive X-Axis.

In fact, I really dont know what happens inside the function, but it works (after x hours trial and error).

Internally it calculates with Radians, and Radians dont like negative values.
So use ...
MA_Deg2Vector00() - if you have positive values only (RA = 0 to 360 and DE = 0 to 180)
MA_Deg2Vector10() - If your RA ist -180 to 180 (will add 180 to all RAs)
MA_Deg2Vector01() - If your DE ist -90 to 90 (will add 90 to all DEs)
MA_Deg2Vector11() - If both RA and DE ranges from negative to positive values.
 
See also:
https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
https://en.wikipedia.org/wiki/Cartesian_coordinate_system#Three_dimensions
http://www.stargazing.net/kepler/rectang.html

Todo:
	The opposite macro (Vector2Deg) ... uh ...

Params:
	mRadius:			Float, Distance of the object from <0,0,0>, a few thousands maybe are a good choice when creating star-maps
	mRA:				Float, Rectascension in Degree (0 to 360 or -180 to +180)
	mDE:				Float, Declination in Degree (0 to 180 or -90 to +90)

Return:
	Vector
*/// **************************************************************************************************************************
#macro MA_Deg2Vector00    (mRA, mDE, mRadius)	MA_Deg2Vector_Base(mRA	+   0, mDE +  0, mRadius)	#end
#macro MA_Deg2Vector10    (mRA, mDE, mRadius)	MA_Deg2Vector_Base(mRA	+ 180, mDE +  0, mRadius)	#end
#macro MA_Deg2Vector01    (mRA, mDE, mRadius)	MA_Deg2Vector_Base(mRA	+   0, mDE + 90, mRadius)	#end
#macro MA_Deg2Vector11    (mRA, mDE, mRadius)	MA_Deg2Vector_Base(mRA	+ 180, mDE + 90, mRadius)	#end
#macro MA_Deg2Vector_Base (mRA, mDE, mRadius)
	#local mRect	= radians(mRA + MA_Deg2Vector_RA_Offset + 90);
	#local mDecl	= radians(mDE);
	#local mPosX	= mRadius * sin(mDecl) * cos (mRect);
	#local mPosY	= mRadius * cos(mDecl);
	#local mPosZ	= mRadius * sin(mDecl) * sin (mRect);
	#local mVector	= <mPosX, mPosY * -1, mPosZ>;
	mVector
#end







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


Repaired or "missing"


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




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

MA_Circle_Text_Valigned

A copy of the original macro Circle_Text_Valigned from the include-file shapes.inc.

This macro has a small error, at inverted = true the text ist too far away from the object.
I corrected this in my copy, hope it works for most of the possible usages.

Date: 20.1.2017

See:
http://www.povray.org/documentation/view/3.7.0/468/
http://www.povray.org/documentation/3.7.0/r3_4.html#r3_4_5_1_16

Params:
	See original macro

Return:
	Object
*/// **************************************************************************************************************************
#macro MA_Circle_Text_Valigned ( F, // Font, i.e.: "arial.ttf", 
                             T, // Text, i.e.: "POVRay",
                             S, // LetterSize,    i.e.:  0.75, 
                             Sp,// LetterSpacing, i.e.: 0.025,
                             Th,// Deepth,        i.e.: 15.00, 
                             R, // Radius,        i.e.: 1.25
                             I, // Inverted,      0 or 1
                             J, // Justification: Align_Left, Align_Right, or Align_Center  
                             A, // Circle angle
                             Valign// Valign:  Rotates the letters. -90 = right side up, 90 = upside-down, 0 = horzontal.
                           ) //----------------------------------------------------------------------------------------------  
                           
	#if (T = "")
		object { sphere {0,00001} }
	#else
		#local FW = Text_Width(F, T, S, Sp);
		#local TO = text {ttf F T 1 0 scale<S, S, 1>}
		#local TH = max_extent(TO).y;
		#local C = array[strlen(T)]
		#if(FW > 2*pi*R)
			#error concat("\n\n**** Text string \"", T, "\" is too long for a circle of the specified radius.\n\n\n")
		#end
		#local AW = -FW*180/pi/R;
		#local SA = A;
		#local EA = A + AW;
		#if(((J = Align_Right) & !I)|((J = Align_Left) & I))
			#local SA = A - AW;
			#local EA = A;
		#else
			#if(J = Align_Center)
				#local SA = A - AW/2;
				#local EA = A + AW/2;
			#end
		#end

		#local CI = 1;
		#while(CI <= strlen(T))
			#local OE = Text_Width(F, substr(T,CI,1), S, Sp);
			#local LW = Text_Width(F, substr(T,1,CI), S, Sp) - OE;
			#local LA = SA + AW*LW/FW + OE/2/FW*AW;
			#if(I)
				#local LA = EA - (LA - SA);
			#end
			#local TO = text {ttf F substr(T, CI, 1) Th 0 scale<S,S,1> rotate x*Valign}
			#if(I)
				#local C[CI-1] =
				object {TO
				rotate 180*z
				translate <OE/2, Th 0> //Th with small "h"
				rotate -90*z
				translate R*x
				rotate LA*z
				
				//Old
				/*
				rotate 180*z
				translate <OE/2, TH, 0>
				rotate -90*z
				translate R*x
				rotate LA*z
				*/
				}
			#else
				#local C[CI-1] =
				object {TO
				translate -OE/2*x
				rotate -90*z
				translate R*x
				rotate LA*z
				}
			#end
			#local CI = CI + 1;
		#end

		// Create the final object, a union of individual text object letters.
		union {
			#local CI=0;
			#while(CI < strlen(T))
				object {C[CI]}
				#local CI = CI + 1;
			#end
		}	
	#end

#end



//Klappte nicht, am Samstag den 17.10.2020 probiert.
//http://www.f-lohmueller.de/pov_tut/trans/trans_470d.htm
//http://wiki.povray.org/content/Reference:Transforms.inc
//http://www.povray.org/documentation/view/3.6.1/49/


#macro Spline_Trans_Rotate (Spline, Time, Sky, Foresight, Banking)
   #local NullVector	= <0,0,0>;
   #local Location = <0,0,0>+Spline(Time);
   #local LocationNext = <0,0,0>+Spline(Time+Foresight);
   #local LocationPrev = <0,0,0>+Spline(Time-Foresight);
   #local Forward = vnormalize(LocationNext-Location);
   #local Right   = VPerp_To_Plane(Sky,Forward);
   #local Up      = VPerp_To_Plane(Forward,Right);
   //#local Matrix = Matrix_Trans(Right,Up,Forward,NullVector)
   #local Matrix = Shear_Trans(Right,Up,Forward)
   #local BankingRotation =
   degrees(atan2(
      VRotation(
         VProject_Plane((LocationNext-Location),Sky),
         VProject_Plane((Location-LocationPrev),Sky),
         Up
      )*Banking
      ,1
   ));
   //transform {
   rotate {
      rotate BankingRotation*z
      transform Matrix
   }
#end
#macro Spline_Trans_Transform (Spline, Time, Sky, Foresight, Banking)
   #local NullVector	= <0,0,0>;
   #local Location = <0,0,0>+Spline(Time);
   #local LocationNext = <0,0,0>+Spline(Time+Foresight);
   #local LocationPrev = <0,0,0>+Spline(Time-Foresight);
   #local Forward = vnormalize(LocationNext-Location);
   #local Right   = VPerp_To_Plane(Sky,Forward);
   #local Up      = VPerp_To_Plane(Forward,Right);
   //#local Matrix = Matrix_Trans(NullVector,NullVector,NullVector,Location)
   #local Matrix = Matrix_Trans(Right,Up,Forward,Location)
   #local BankingRotation =
   degrees(atan2(
      VRotation(
         VProject_Plane((LocationNext-Location),Sky),
         VProject_Plane((Location-LocationPrev),Sky),
         Up
      )*Banking
      ,1
   ));
   transform {
      rotate BankingRotation*z
      transform Matrix
   }
#end








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


--- Experimental ---

In use in some of my example-files.

THEY WILL CHANGE OVER TIME, use them on your own risk !


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



/*
//Test only, 7.5.19
#declare testarray = array[3] {
	MA_TestArray_2D, MA_TestArray_2D, MA_TestArray_2D
}

#local myarray = testarray[2];
MA_OutputArrayString(myarray, "", true, "testarray")
*/






//-----------------------------------------------------------------------------------------------------------------------------
//This is the end of the very first If (that checks if this file was included before)
//Never delete it, or else ...
	#version MA_Helpers_Temp;
#end
//-----------------------------------------------------------------------------------------------------------------------------
