Difference between revisions of "ESM Math Library"

From the Oblivion ConstructionSet Wiki
Jump to navigation Jump to search
imported>Wrye
m (Stage function repository moved to ESM Math Library: More accurate. This is really info on a specific package of stage functions.)
imported>Diarrhoe
(Added stage 40 and 50 for natural logarithm and exponentiation)
Line 106: Line 106:
float tan
float tan
;float ang
;float ang
;S40 FUNCTION NaturalLogarithm
short LN_n
;float t1
;float t2
;float t3
;float t5
;float t7
;float t9
;float t11
float t13
;S50 FUNCTION Exponentiation
float base
float exponent
;float t1
;float t2
;float t3
float t4
;float t5
;float t6
;float t7
float t8
;float t9
float t10
;float t11


; Set constants first
; Set constants first
Line 548: Line 574:
set myResultAngle to f.fout</pre>
set myResultAngle to f.fout</pre>


=== Stage 40: NaturalLogarithm ===
Takes an antilogarithm a (float) and returns the exponent b (float) to the base e(=2.71828...):<br>
a=e^b --> b=LN(a)<br>
<br>
Serial approximation is described [http://www.convertit.com/Go/EducationPlanet/Reference/AMS55.ASP?Res=150&Page=68 here].<br>
Code to include in FunctionQuestScript:
<pre>;S40 FUNCTION NaturalLogarithm
short LN_n
float t1
float t2
float t3
float t5
float t7
float t9
float t11
float t13</pre>
Code for the Stage Result Script:
<pre>;FUNCTION float LN(float input)
if f.LN_n == 0
set f.LN_n to 1
endif
if f.fin1 <= 0
set f.fout to 0
set f.LN_n to 0
elseif f.fin1 > 4 || f.fin1 < 0.25 ;change values for better or worse accuracy (makes the script faster)
;LN(x) = n * LN( nthRoot(x) )
set f.LN_n to (f.LN_n * 2)
setStage f 5
set f.fin1 to f.fout
setStage f 40
else
set f.t1 to ((f.fin1 - 1) / (f.fin1 + 1))
set f.t2 to (f.t1*f.t1)
set f.t3 to (f.t1*f.t2)
set f.t5 to (f.t3*f.t2)
set f.t7 to (f.t5*f.t2)
set f.t9 to (f.t7*f.t2)
set f.t11 to (f.t9*f.t2)
set f.t13 to (f.t11*f.t2)
set f.fout to (2 * f.LN_n * (f.t1 + f.t3/3 + f.t5/5 + f.t7/7 + f.t9/9 + f.t11/11 + f.t13/13))
set f.LN_n to 0
endif</pre>
Actually, there are two ways to increase accuracy. You can either use more terms or change the values above (nearer 1 increases accuracy, farther 1 decreases it).<br>
Keep in mind, that this is only useful in the relation: firstValue = 1 / secondValue
Usage in another script:
<pre>;CALL float NaturalLogarithm(float x)
set f.fin1 to xVar
setStage f 40
set exponent to f.fout</pre>
=== Stage 50: Exponentiation ===
Takes a base x (positive) and an exponent y and returns a=x^y (all floats).<br>
Script uses Taylor-Series for exponential functions from [http://www.convertit.com/Go/EducationPlanet/Reference/AMS55.ASP?Res=150&Page=69 here].
Code to include in FunctionQuestScript:
<pre>;S50 FUNCTION Exponentiation
float base
float exponent
float t1
float t2
float t3
float t4
float t5
float t6
float t7
float t8
float t9
float t10
float t11</pre>
Code for the Stage Result Script:
<pre>;FUNCTION float Exponentiation(float base, float exponent)
set f.base to f.fin1
set f.exponent to f.fin2
setStage f 40
;x^y = e^(LN(x) * y) , e=2.71828...
set f.t1 to (f.fout*f.exponent)
set f.t2 to (f.t1*f.t1)
set f.t3 to (f.t2*f.t1)
set f.t4 to (f.t3*f.t1)
set f.t5 to (f.t4*f.t1)
set f.t6 to (f.t5*f.t1)
set f.t7 to (f.t6*f.t1)
set f.t8 to (f.t7*f.t1)
set f.t9 to (f.t8*f.t1)
set f.t10 to (f.t9*f.t1)
set f.t11 to (f.t10*f.t1)
set f.fout to (1 + f.t1 + f.t2/2 + f.t3/6 + f.t4/24 + f.t5/120 + f.t6/720 + f.t7/5040 + f.t8/40320 + f.t9/362880 + f.t10/3628800 + f.t11/39916800)</pre>
Actually, more terms won't increase accuracy any more, because Oblivion can't handle higher values.
Usage in another script:
<pre>;CALL float Exponentiation(float base, float exponent)
set f.fin1 to base
set f.fin2 to exponent
setStage f 50
set result to f.fout</pre>
[[Category: Useful Code]]
[[Category: Useful Code]]

Revision as of 08:15, 21 December 2010

This is a complete quest+function set-up. For a modular system, see Stage Functions

Welcome

to the Stage Function Repository! The goal of the Repository is, to create a huge database of available functions all using the method described in the article Simulating new functions. If you've written such a function and you think it might be helpful to others, don't hesitate to contribute to this repository.

The easy way: Math Library ESM

The easiest way to use the functions shown in this article is by using this ESM Math Library. To use it copy the ESM file to your oblivion data folder and select it as an additional master file when loading your mod with the Construction Set. By doing this all the necessary setup steps are already done for you. All you need to do to use a function is to call it. Examples of how to call each function are shown below in the "Usage" blocks of each stage function. Be aware that other users of your mod will need the Library too to run it.

Download: ESM Math Library v1.0.

Do it yourself: Step by Step

To get this up and running follow there simple setup steps:

  • Open any Plugin you wish or create a new one with the Construction Set
  • Create a new Quest called "f" (yes, just the letter "f", nothing more)
  • Activate the Checkbox "Allow repeated stages". (This is VERY important!!)
  • Create a new Quest Script and copy the whole content of the "Quest Script" section from this article into this script. Don't forget to attach it to your f-Quest!
  • Create a new stage in this quest for each function you want to use from this repository and copy all code from it's section in this article to the related Result Script textbox. Make sure to select the right stage number for the function.
  • That's it! You should be able to use them now!


Note for Stage function writers

It's great to see so many contributions. Keep it up. However, please try to make things as clear as possible for users. This repository will be most useful to users who aren't that good at scripting themselves. It isn't safe to assume that every user is going to check through the code of a function before using it.

At the least, each stage function should make clear what its inputs and outputs are. E.g. for trig functions this means specifying whether inputs/outputs are in degrees or radians. It also means making it clear (preferably outside the function code) when a certain function uses another one - e.g. Arccos calling Arcsin. Users should ideally not have to look through the code of each stage function to check whether it uses other stage functions.

Making things clear shouldn't take long, and will be helpful to users. It's also no more than your code deserves :).


Quest Script

ScriptName FunctionQuestScript

; Internals
short doOnce
float fQuestDelayTime

; Constants
float pi
float radToDeg
float degToRad

; Function In- and Output
float fin1
float fin2
float fin3
float fout
float fout2
float fout3

;S5 FUNCTION sqrt
float sqr

;S6 FUNCTION NormalizeAngle360

;S7 FUNCTION NormalizeAngle180

;S10 FUNCTION Hypotenuse
;float sqr
float n

;S15 FUNCTION SinCosTan 1
float ang
float sin
float cos
float tan
float exp

;S16 FUNCTION SinCodTan 2
;float ang
;float sin
;float cos
float t1
float t2
float t5
float t6

;S17 FUNCTION SinCosTan 3
;float ang
;float sin
;float cos
;float t2

;S20 FUNCTION Arcsine 1
;float sin
;float ang
;float n

;S21 FUNCTION Arcsine 2
float t3
;float t5
float t7

;S22 FUNCTION Arccosine 1

;S23 FUNCTION Arccosine 2

;S24 FUNCTION Arctan
;float t3
;float t5
;float t7

;S30 FUNCTION getAngle
float tan
;float ang

;S40 FUNCTION NaturalLogarithm
short LN_n
;float t1
;float t2
;float t3
;float t5
;float t7
;float t9
;float t11
float t13

;S50 FUNCTION Exponentiation
float base
float exponent
;float t1
;float t2
;float t3
float t4
;float t5
;float t6
;float t7
float t8
;float t9
float t10
;float t11

; Set constants first
Begin Gamemode
  if doOnce == 0
    set pi to 3.1415927
    set radToDeg to 180.0/pi    ;always multiply to convert
    set degToRad to 1/radToDeg

    set fQuestDelayTime to 30
    set doOnce to 1
  endif
End

This Quest Script already contains everything needed for the following functions.

Stage 5: Square Root

Based on Square Root Article


Code to include in FunctionQuestScript:

;S5 FUNCTION sqrt
float sqr

Code for the Stage Result Script:

;FUNCTION float sqrt(float input)
if (f.fin1 <= 0)
  set f.fout to 0
else
  set f.sqr to f.fin1/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.fout to f.sqr
endif

Usage in another script:

;CALL float sqrt(float input)
set f.fin1 to myVarForInput
setStage f 5
set myResult to f.fout

Stage 6: Normalize Angle (0 till 360)

Takes an Angle and normalizes it to a range from 0 to 360 degrees. This is a functional loop!

Code for the Stage Result Script:

;FUNCTION float NormalizeAngle360(float Angle)
if (f.fin1 >= 0) && (f.fin1 <= 360)
  set f.fout to f.fin1
else
  if f.fin1 < 0
    set f.fin1 to (f.fin1 + 360)
  else
    set f.fin1 to (f.fin1 - 360)
  endif
  ;CALL float NormalizeAngle360(float angle)
  setStage f 6
endif

Usage in another script:

;CALL float NormalizeAngle360(float angle)
set f.fin1 to myVarForAngle
setStage f 6
set myResult to f.fout

Stage 7: Normalize Angle (-180 till 180)

Takes an Angle and normalizes it to a range from -180 to 180 degrees.

Code for the Stage Result Script:

;FUNCTION float NormalizeAngle180(float Angle)
if (f.fin1 < 0) || (f.fin1 > 360)
  ;CALL float NormalizeAngle360(float Angle)
  setStage f 6
else
  set f.fout to f.fin1
endif
if f.fout > 180
  set f.fout to (f.fout - 360)
endif

Usage in another script:

;CALL float NormalizeAngle180(float angle)
set f.fin1 to myVarForAngle
setStage f 7
set myResult to f.fout

Stage 10: Hypotenuse

Takes the length of both Cathesus in a right triangle as two floats.
Returns the length of the Hypotenuse as a float.

Code to include in FunctionQuestScript:

;S10 FUNCTION Hypotenuse
float n

Code for the Stage Result Script:

;FUNCTION float Hypotenuse(float CathetusA, float CathetusB)

set f.n to ((f.fin1 * f.fin1) + (f.fin2 * f.fin2))
;CALL float sqrt(float input)
set f.fin1 to f.n
setStage f 5
;fout is already the result

Usage in another script:

;CALL float Hypotenuse(float CathetusA, float CathetusB)
set f.fin1 to myVarForCathetusA
set f.fin2 to myVarForCathetusB
setStage f 10
set myResult to f.fout

Stage 15: Sin Cos Tan

Based on Trigonometry Functions Article

Takes an angle in DEGREES.
Returns three floats: sin, cos and tan of the angle.

Code to include in FunctionQuestScript:

;S15 FUNCTION SinCosTan
float ang
float sin
float cos
float exp

Code for the Stage Result Script:

;FUNCTION float,float,float SinCosTan(float Angle)
;Taylor Series Variant 1 - script by Galerion

;CALL float NormalizeAngle180(float Angle)
setStage f 7
set f.ang to f.fout

;approximate
set f.ang to (f.ang*f.degToRad)
set f.cos to 1
set f.exp to f.ang
set f.sin to f.exp
set f.exp to (f.exp * f.ang)
set f.cos to (f.cos - f.exp / 2)
set f.exp to (f.exp * f.ang)
set f.sin to (f.sin - f.exp / 6)
set f.exp to (f.exp * f.ang)
set f.cos to (f.cos + f.exp / 24)
set f.exp to (f.exp * f.ang)
set f.sin to (f.sin + f.exp / 120)
set f.exp to (f.exp * f.ang)
set f.cos to (f.cos - f.exp / 720)
set f.exp to (f.exp * f.ang)
set f.sin to (f.sin - f.exp / 5040)
set f.exp to (f.exp * f.ang)
set f.cos to (f.cos + f.exp / 40320)
set f.exp to (f.exp * f.ang)
set f.sin to (f.sin + f.exp / 362880)

set f.fout to f.sin
if f.cos == 0
  set f.cos to 0.0001
endif
set f.fout2 to f.cos
set f.fout3 to (f.sin/f.cos) ;tan

Usage in another script:

;CALL float,float,float SinCosTan(float Angle)
set f.fin1 to myVarForAngle
setStage f 15
set myResultSin to f.fout
set myResultCos to f.fout2
set myResultTan to f.fout3

Stage 16: Sin Cos Tan 2

Based on Trigonometry Functions Article

Takes an angle in DEGREES.
Returns three floats: sin, cos and tan of the angle.

Code to include in FunctionQuestScript:

;S16 FUNCTION SinCodTan 2
float ang
float sin
float cos
float t1
float t2
float t5
float t6

Code for the Stage Result Script:

;FUNCTION float,float,float SinCosTan(float Angle)
;Taylor Series Variant 2 - script by JOG

;CALL float NormalizeAngle180(float Angle)
setStage f 7
set f.ang to f.fout

set f.t1 to (f.ang*f.degToRad)
set f.t2 to (f.t1*f.t1)
set f.t5 to (f.t2*f.t2*f.t1)
set f.t6 to (f.t5*f.t1)
set f.sin to (f.t1 - (f.t1*f.t2/6) + (f.t5/120) - (f.t5*f.t2/5040) + (f.t6*f.t2*f.t1/362880))
set f.cos to (1 - (f.t2/2) + (f.t2*f.t2/24) - (f.t6/720) + (f.t6*f.t2/40320))

set f.fout to f.sin
if f.cos == 0
  set f.cos to 0.0001
endif
set f.fout2 to f.cos
set f.fout3 to f.sin/f.cos ;tan

Usage in another script:

;CALL float,float,float SinCosTan(float Angle)
set f.fin1 to myVarForAngle
setStage f 16
set myResultSin to f.fout
set myResultCos to f.fout2
set myResultTan to f.fout3

Stage 17: Sin Cos Tan 3

Based on a posting by Galsiah in THIS THREAD

Takes an angle in DEGREES.
Returns three floats: sin, cos and tan of the angle.

Code to include in FunctionQuestScript:

;S17 FUNCTION SinCosTan 3
float ang
float sin
float cos
float t2

Code for the Stage Result Script:

;FUNCTION float,float,float SinCosTan(float Angle)
;script by Galsiah

;CALL float NormalizeAngle360(float Angle)
setStage f 6
set f.ang to (f.fout*f.degToRad)

set f.n to 1
if (f.ang > 4.7123)
    Set f.ang to (f.ang - 6.2832)
elseif ( f.ang > 1.5708 )
    Set f.ang to (f.ang - 3.1416)
    Set f.n to -1
endif

set f.t2 to (f.ang * f.ang)
set f.sin to (f.ang*(1 - (f.t2*(0.16605 - (0.00761*f.t2)))))
set f.sin to (f.sin*f.n)

set f.cos to (1 - (f.t2*(0.4967 - (0.03705*f.t2))))
set f.cos to (f.cos*f.n)

set f.fout to f.sin
set f.fout2 to f.cos

;NB division is safe, since the above cos approximation cannot return 0.
set f.fout3 to f.sin/f.cos ;tan

Usage in another script:

;CALL float,float,float SinCosTan(float Angle)
set f.fin1 to myVarForAngle
setStage f 17
set myResultSin to f.fout
set myResultCos to f.fout2
set myResultTan to f.fout3

Stage 20: Arcsine

Based on Trigonometry Functions Article

Takes a float.
Returns the Arcsine in DEGREES as a float.

Code to include in FunctionQuestScript:

;S20 FUNCTION Arcsine
float sin
float ang
float n

Code for the Stage Result Script:

;FUNCTION float Arcsine(float sin)
;Abramowitz/Stegun Approximation - script by JOG

set f.sin to f.fin1
set f.n to 1 - f.sin
set f.ang to f.n/2
set f.ang to (f.ang+(f.n/f.ang))/2
set f.ang to (f.ang+(f.n/f.ang))/2
set f.ang to (f.ang+(f.n/f.ang))/2
set f.n to (f.ang+(f.n/f.ang))/2

set f.fout to f.radToDeg*(1.5707963 - f.n*(1.5707288 - 0.2121144*f.sin+0.0742610*f.sin*f.sin - 0.0187293*f.sin*f.sin*f.sin))

Usage in another script:

;CALL float Arcsine(float sin)
set f.fin1 to myVarForSin
setStage f 20
set myResult to f.fout

Stage 21: Arcsine 2

Based on Trigonometry Functions Article

Takes a float.
Returns the Arcsine in RADIANS as a float.

Code to include in FunctionQuestScript:

;S21 FUNCTION Arcsine 2
float t3
float t5
float t7

Code for the Stage Result Script:

;FUNCTION float Arcsine(float sin)
;Approximation by Taylor Series - script by DragoonWraith

set f.t3 to (f.fin1 * f.fin1 * f.fin1)
set f.t5 to (f.t3 * f.fin1 * f.fin1)
set f.t7 to (f.t5 * f.fin1 * f.fin1)

set f.fout to (f.fin1 + (1/2)*(f.t3/3) + (3/8)*(f.t5/5) + (15/48)*(f.t7/7) )

Usage in another script:

;CALL float Arcsine(float sin)
set f.fin1 to myVarForSin
setStage f 21
set myResult to f.fout

Stage 22: Arccosine

Based on Trigonometry Functions Article

Takes a float.
Returns the Arccosine in DEGREES as a float.
Implementation relies on Stage 20.

Code for the Stage Result Script:

;FUNCTION float Arccosine(float cos)
setStage f 20 ;Call Arcsine with same input (DEGREES version) 
set f.fout to (90 - f.fout)

Usage in another script:

;CALL float Arccosine(float cos)
set f.fin1 to myVarForCos
setStage f 22
set myResult to f.fout

Stage 23: Arccosine 2

Based on Trigonometry Functions Article

Takes a float.
Returns the Arccosine in RADIANS as a float.
Implementation relies on Stage 21.

Code for the Stage Result Script:

;FUNCTION float Arccosine(float cos)
setStage f 21 ;Call Arcsine with same input (RADIANS version)
set f.fout to ((f.pi / 2) - f.fout)

Usage in another script:

;CALL float Arccosine(float cos)
set f.fin1 to myVarForCos
setStage f 23
set myResult to f.fout

Stage 24: Arctan

Based on Trigonometry Functions Article

Takes a float.
Returns the Arctan in RADIANS as a float.

Code to include in FunctionQuestScript:

;S24 FUNCTION Arctan
float t3
float t5
float t7

Code for the Stage Result Script:

;FUNCTION float Arctan(float tan)
;Approximation by Taylor Series - script by DragoonWraith

set f.t3 to (f.fin1 * f.fin1 * f.fin1)
set f.t5 to (f.t3 * f.fin1 * f.fin1)
set f.t7 to (f.t5 * f.fin1 * f.fin1)

set f.fout to (f.fin1 - (f.t3/3) + (f.t5/5) - (f.t7/7))

Usage in another script:

;CALL float Arctan(float tan)
set f.fin1 to myVarForTan
setStage f 24
set myResult to f.fout

Stage 30: getVectorAngle

Takes the X and Y dimension of a 2D Vector as two floats.
Returns the Angle of the Vector in DEGREES as a float.

Code to include in FunctionQuestScript:

;S30 FUNCTION getVectorAngle
float tan
float ang

Code for the Stage Result Script:

;FUNCTION float getVectorAngle(float x, float y)

if f.fin1 == 0
  set f.fin1 to 0.001
endif
if f.fin2 == 0
  set f.fin2 to 0.001
endif

set f.tan to (f.fin1/f.fin2)
if f.tan >1 || f.tan < -1
  set f.tan to (f.fin2/ -f.fin1)
  if f.fin1 >= 0
    set f.ang to 90
  else
    set f.ang to -90
  endif
else
  if f.fin2 >= 0
    set f.ang to 0
  else
    set f.ang to 180
  endif
endif

;CALL float Arctan(float tan)
set f.fin1 to f.tan
setStage f 24

if f.fout > (f.pi/2)
  set f.fout to (f.pi/2)
elseif f.fout < (f.pi/ -2)
  set f.fout to (f.pi/ -2)
endif
set f.fout to ((f.fout*f.radToDeg) + f.ang)

Usage in another script:

;CALL float getVectorAngle(float x, float y)
set f.fin1 to myVarForX
set f.fin2 to myVarForY
setStage f 30
set myResultAngle to f.fout

Stage 40: NaturalLogarithm

Takes an antilogarithm a (float) and returns the exponent b (float) to the base e(=2.71828...):
a=e^b --> b=LN(a)

Serial approximation is described here.

Code to include in FunctionQuestScript:

;S40 FUNCTION NaturalLogarithm
short LN_n
float t1
float t2
float t3
float t5
float t7
float t9
float t11
float t13

Code for the Stage Result Script:

;FUNCTION float LN(float input)

if f.LN_n == 0
 set f.LN_n to 1
endif

if f.fin1 <= 0
 set f.fout to 0
 set f.LN_n to 0
elseif f.fin1 > 4 || f.fin1 < 0.25 ;change values for better or worse accuracy (makes the script faster)
 ;LN(x) = n * LN( nthRoot(x) )
 set f.LN_n to (f.LN_n * 2)
 setStage f 5
 set f.fin1 to f.fout
 setStage f 40
else
 set f.t1 to ((f.fin1 - 1) / (f.fin1 + 1))
 set f.t2 to (f.t1*f.t1)
 set f.t3 to (f.t1*f.t2)
 set f.t5 to (f.t3*f.t2)
 set f.t7 to (f.t5*f.t2)
 set f.t9 to (f.t7*f.t2)
 set f.t11 to (f.t9*f.t2)
 set f.t13 to (f.t11*f.t2)
 set f.fout to (2 * f.LN_n * (f.t1 + f.t3/3 + f.t5/5 + f.t7/7 + f.t9/9 + f.t11/11 + f.t13/13))
 set f.LN_n to 0
endif

Actually, there are two ways to increase accuracy. You can either use more terms or change the values above (nearer 1 increases accuracy, farther 1 decreases it).
Keep in mind, that this is only useful in the relation: firstValue = 1 / secondValue

Usage in another script:

;CALL float NaturalLogarithm(float x)
set f.fin1 to xVar
setStage f 40
set exponent to f.fout

Stage 50: Exponentiation

Takes a base x (positive) and an exponent y and returns a=x^y (all floats).
Script uses Taylor-Series for exponential functions from here.

Code to include in FunctionQuestScript:

;S50 FUNCTION Exponentiation
float base
float exponent
float t1
float t2
float t3
float t4
float t5
float t6
float t7
float t8
float t9
float t10
float t11

Code for the Stage Result Script:

;FUNCTION float Exponentiation(float base, float exponent)

set f.base to f.fin1
set f.exponent to f.fin2
setStage f 40
;x^y = e^(LN(x) * y) , e=2.71828...

set f.t1 to (f.fout*f.exponent)
set f.t2 to (f.t1*f.t1)
set f.t3 to (f.t2*f.t1)
set f.t4 to (f.t3*f.t1)
set f.t5 to (f.t4*f.t1)
set f.t6 to (f.t5*f.t1)
set f.t7 to (f.t6*f.t1)
set f.t8 to (f.t7*f.t1)
set f.t9 to (f.t8*f.t1)
set f.t10 to (f.t9*f.t1)
set f.t11 to (f.t10*f.t1)

set f.fout to (1 + f.t1 + f.t2/2 + f.t3/6 + f.t4/24 + f.t5/120 + f.t6/720 + f.t7/5040 + f.t8/40320 + f.t9/362880 + f.t10/3628800 + f.t11/39916800)

Actually, more terms won't increase accuracy any more, because Oblivion can't handle higher values.

Usage in another script:

;CALL float Exponentiation(float base, float exponent)
set f.fin1 to base
set f.fin2 to exponent
setStage f 50
set result to f.fout