====== Math ======
ACSUtils provides a rich math library and covers for several missing ZDoom functions.
===== Notes =====
==== What is "num"? ====
''num'' means "any numeric type" (either int or fixed). Functions accepting ''num'' accept both ints and fixeds, e.g:
num max(num a, num b)
translates to:
"The function ''max'' accepts two numbers and returns a number. If both arguments are ints, it returns an int, and if both arguments are fixeds, it returns a fixed".
This allows ''max'' to be used both ways:
max(2, 5) == 5
max(2.0, 5.0) == 5.0
**Don't mix ints and fixeds when using ''num''.** ''num''s in a function signature are either all int or all fixed! E.g., this is incorrect:
max(5, 2.0)
It will return ''2.0'' because ''max'' uses a simple ''a > b'' comparison that only works correctly if both numbers are ints or both numbers are fixeds. ''2.0'' (65536) is greater than 5.
However, not all arguments of a function may be ''num''. Some arguments may be required to be int or fixed, or the function may always return int or fixed:
num OneOfTheArgumentsIsRequiredToBeInt(num a, int b)
fixed ThisFunctionAlwaysReturnsFixed(num a, num b)
==== Angles ====
All angles throughout ACSUtils are [[zdoom>Definitions#Fixed_point_angles|fixed-point angles]] (turns), and they are allowed to be outside the ''0.0''..''1.0'' range. All such cases are handled correctly by both ZDoom and ACSUtils.
Also, ZDoom expects inverted actor pitch (positive = looking down) in [[zdoom>GetActorPitch]] and [[zdoom>SetActorPitch]]. ACSUtils doesn't invert pitch angles, so remember to invert pitch when retrieving it with ''GetActorPitch'' and when setting it with ''SetActorPitch''.
===== Constants =====
ACSUtils provides a set of mathematical constants. All fixed-point constants have a lot of digits after the point to maximize precision. Actually more digits than needed.
==== Pi and Tau ====
* ''#define PI 3.1415926535897932384626433832795''
* ''#define TAU 6.2831853071795864769252867665590''
The numbers Pi and [[https://tauday.com/tau-manifesto|Tau]].
==== e ====
''#define MATH_E 2.7182818284590452353602874713526624977572470937''
The number [[wp>E_(mathematical_constant)|e]]. Called ''MATH_E'' to avoid conflicts with variable name ''e''.
==== Significant square roots and logarithms ====
Precalculated to maximum precision. The precalculated logarithms are used to implement ''ln'' and ''log10''.
* ''#define SQRT_2 1.41421356237'' -- sqrt(2)
* ''#define LOG2_E 1.44269504089'' -- log2(e)
* ''#define LOG2_10 3.32192809489'' -- log2(10)
===== Generic math functions =====
==== abs ====
''num abs(num x)''
Returns the absolute value of X:
* If X is zero or positive, returns X.
* If X is negative, returns -X.
**Beware of using this function with ''[[limits#INT_MIN]]''.**
''abs(INT_MIN) == INT_MIN'' because ''-INT_MIN == INT_MIN''. This is the only case when ''abs'' returns a negative number.
==== sgn ====
''int sgn(int x)''
Returns the sign of X:
* If X is positive, returns ''1''.
* If X is negative, returns ''-1''.
* If X is zero, returns ''0''.
=== Example usage ===
// Make X velocity 15.0, but with the same sign as foo
int xvel = sgn(foo) * 15.0;
==== min ====
''num min(num a, num b)''
Returns the lesser of two numbers.
==== max ====
''num max(num a, num b)''
Returns the greater of two numbers.
==== clamp ====
''num clamp(num x, num mininum, num maximum)''
Limits X to the specified range:
* If X < mininum, returns the minumum.
* If X is between mininum and maximum, returns X.
* If X > maximum, returns the maximum.
==== lerp ====
''fixed lerp(fixed a, fixed b, fixed alpha)''
Linear interpolation function commonly used in game development. Performs [[wp>Linear interpolation|linear interpolation]] (or extrapolation) between A and B and returns the result.
Alpha can be outside of ''0.0''..''1.0'' range. In that case, it's linear extrapolation.
=== example usage ===
Simple movement animation between two points:
for (int time = 0; time < 1.0; time += 0.05)
{
int x = lerp(x1, x2, time);
int y = lerp(y1, y2, time);
DrawSomething(x, y);
Delay(1);
}
==== IntDiv ====
''fixed IntDiv(int a, int b)''
An equivalent of [[zdoom>FixedDiv]] for integers that works for any two integers as long as the result fits into a fixed-point number. Divides integer A by integer B and returns the result as a fixed-point number.
If the result is greater than 32767, the function returns 0.
Can also be used as a more readable alternative to ''%%FixedDiv(a<<16, b<<16)%%''.
==== cmp ====
''int cmp(num a, num b)''
Compares two numbers and returns the result:
* If A > B, returns ''1''.
* If A = B, returns ''0''.
* If A < B, returns ''-1''.
==== fract ====
''fixed fract(fixed x)''
Returns the fractional part of X, e.g ''fract(12.345) == 0.345''.
The result is not affected by the sign of the number, e.g. ''fract(-12.345) == 0.345''. The result is always between ''0.0'' and ''1.0''.
==== ipow ====
''int ipow(int x, int y)''
Returns xy. Uses a simple multiplication loop.
==== fpow ====
''fixed fpow(fixed x, fixed y)''
returns xy. Supports powers below ''1.0''. Uses a simple loop with ''FixedMul'' or ''FixedDiv'', depending on whether the power is below ''1.0''.
===== Trigonometry =====
ACSUtils provides a full set of trigonometric functions:
* [[zdoom>sin]] is already built into ZDoom.
* [[zdoom>cos]] is already built into ZDoom.
* ''fixed tan(fixed angle)'' -- implemented as ''FixedDiv(sin(x), cos(x))''.
* ''fixed cot(fixed angle)'' -- implemented as ''FixedDiv(cos(x), sin(x))''.
* ''fixed sec(fixed angle)'' -- implemented as ''FixedDiv(1.0, sin(x))''.
* ''fixed cosec(fixed angle)'' -- implemented as ''FixedDiv(1.0, cos(x))''.
Inverse trigonometric functions:
* ''fixed atan(fixed x)'' -- implemented as ''[[zdoom>VectorAngle]](1.0, x)''.
* ''fixed acot(fixed x)'' -- implemented as ''0.25 - atan(x)''.
* ''fixed asin(fixed x)'' -- implemented as ''atan(FixedDiv(x, FixedSqrt(1.0 - FixedMul(x, x))))''.
* ''fixed acos(fixed x)'' -- implemented as ''ang(2 * atan(FixedSqrt(FixedDiv(1.0 - x, 1.0 + x))))''.
* ''fixed asec(fixed x)'' -- implemented as ''acos(FixedDiv(1.0, x))''.
* ''fixed acosec(fixed x)'' -- implemented as ''asin(FixedDiv(1.0, x))''.
===== Vector math =====
Vector math functions come in 2D and 3D versions. All vector math functions operate only on fixed-point vectors. No integer versions are available.
Vectors are passed as arguments named ''x'', ''y'', ''z''.
If a function returns a vector, it uses [[multiple return values]], returning vector components in the same XYZ order, so ''r1'' is X, ''r2'' is Y and ''r3'' is Z.
If a function accepts more than one vector, the first vector is passed as ''x1'', ''y1'', ''z1'', and the second one as ''x2'', ''y2'', ''z2''.
==== Length ====
* 2D: [[zdoom>VectorLength]] -- already built into ZDoom.
* 3D: ''fixed VectorLength3D(fixed x, fixed y, fixed Z)''
These functions return the length of the input vector. ''VectorLength3D'' is implemented efficiently as ''VectorLength(VectorLength(x, y), z)''.
==== Dot product ====
* 2D: ''fixed dot2(fixed x1, fixed y1, fixed x2, fixed y2)''
* 3D: ''fixed dot3(fixed x1, fixed y1, fixed z1, fixed x2, fixed y2, fixed z2)''
These functions return the sum of components of a vector obtained by componentwise multiplication of the two input vectors, also known as the [[wp>dot product]]. They are shorthands for writing out ''FixedMul(x1, x2) + FixedMul(y1, y2) + ...''.
==== Normalization ====
* 2D: ''fixed, fixed normalize2d(fixed x, fixed y)''
* 3D: ''fixed, fixed, fixed normalize3d(fixed x, fixed y, fixed z)''
These functions return the input vector scaled to have length ''1.0'', also known as [[wp>Normalized vector|normalizing]] the input vector, and return the result using [[multiple return values]].
=== Examples ===
normalize3d(x, y, z);
x = r1;
y = r2;
z = r2;
normalize2d(x, y);
int normalizedX = r1;
int normalziedY = r2;
==== RotateVector ====
''fixed, fixed RotateVector(fixed x, fixed y, fixed angle)''
Rotates the vector by the angle and returns the result using [[multiple return values]].
=== Example usage ===
RotateVector(x, y, angle);
int newX = r1;
int newY = r2;
// Rotate a point around the origin
// Use RotatePoint instead of this example code
RotateVector(x - originX, y - originY, angle);
int newX = r1 + originX;
int newY = r2 + originY;
==== RotateVectorCS ====
''fixed, fixed RotateVector(fixed x, fixed y, fixed cos, fixed sin)''
A faster version of RotateVector that accepts cos(angle) and sin(angle) instead of computing them.
=== Example usage ===
int c = cos(angle);
int s = sin(angle);
// Rotate first vector
RotateVectorCS(x1, y1, c, s);
x1 = r1;
y1 = r2;
// Rotate second vector without recalculating sine and cosine
RotateVectorCS(x2, y2, c, s);
x2 = r1;
y2 = r2;
==== RotatePoint ====
''fixed, fixed RotatePoint(fixed x, fixed y, fixed originX, fixed originY, fixed angle)''
Rotates the point around the specified origin by the angle and returns the result using [[multiple return values]].
=== Example usage ===
RotatePoint(x, y, originX, originY, angle);
int newX = r1;
int newY = r2;
==== SqVectorLength ====
* ''fixed SqVectorLength(fixed x, fixed y)''
* ''fixed SqVectorLength3D(fixed x, fixed y, fixed z)''
Returns vector length squared.
Because this function returns a fixed, it won't work correctly for vectors longer than ''256.0''.
==== VectorToAngles ====
''fixed, fixed VectorToAngles(fixed x, fixed y, fixed z)''
Converts the vector's direction to a pair of angles (angle, pitch) and returns them using [[multiple return values]].
Remember that ZDoom uses negated pitch angles on actors. ACSUtils functions don't use negated pitches.
=== Example usage ===
// This function makes the actor look in the direction
// specified as a 3D vector.
function void LookInDirection(int tid, int x, int y, int z)
{
VectorToAngles(x, y, z);
int angle = r1;
int pitch = r2;
SetActorAngle(tid, angle);
SetActorPitch(tid, -pitch); // SetActorPitch expects negated pitch.
}
==== AnglesToVector ====
''fixed, fixed, fixed AnglesToVector(fixed angle, fixed pitch)''
Converts a pair of angles (angle, pitch) to a 3D vector of length ''1.0'', which points in the same direction, and returns it using [[multiple return values]].
Remember that ZDoom uses negated pitch angles on actors. ACSUtils functions don't use negated pitches.
=== Example usage ===
AnglesToVector(GetActorAngle(0), -GetActorPitch(0)); // Un-negate actor pitch
int dirX = r1;
int dirY = r2;
int dirZ = r3;
===== Logarithms =====
Base-2 logarithms are the fundamental algorithm. All other bases are implemented using the property "''log_b(x) = log_2(x) / log_2(b)'' if b > 1".
All logarithm functions return a fixed, but there are two versions of every logarithm function: one accepting an integer X and one accepting a fixed-point X. The integer-accepting functions have the ''i'' prefix.
==== log2 ====
* ''fixed log2(fixed x)''
* ''fixed ilog2(int x)''
Returns the base-2 logarithm of X.
==== log10 ====
* ''fixed log10(fixed x)''
* ''fixed ilog10(int x)''
Returns the base-10 logarithm of X. Implemented by dividing log2(X) by the precalculated constant ''LOG2_10''.
==== ln ====
* ''fixed ln(fixed x)''
* ''fixed iln(int x)''
Returns the natual (base-e) logarithm of X. Implemented by dividing log2(X) by the precalculated constant ''LOG2_E''.
==== logb ====
* ''fixed logb(fixed x, fixed base)''
* ''fixed ilogb(int x, fixed base)''
Returns an arbitrary-base logarithm of X. Calculates ''FixedDiv(log2(x), log2(base))''.
Only bases higher than ''1.0'' are supported.
Using these functions with bases very close to ''1.0'' may produce a result that doesn't fit into a fixed.
Note that even the integer version has a fixed-point base.