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. nums 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 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 GetActorPitch and 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 Tau.

e

#define MATH_E 2.7182818284590452353602874713526624977572470937

The number 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 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 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 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 x^y. Uses a simple multiplication loop.

fpow

fixed fpow(fixed x, fixed y)

returns x^y. 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:

sin is already built into 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 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

VectorLength3D

fixed VectorLength3D(fixed x, fixed y, fixed z)

A 3D version of VectorLength. Returns the length of the 3D vector.

dot

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)

Returns the dot product of two vectors. Shorthand for writing out FixedMul(x1, x2) + FixedMul(y1, y2) + ….

normalize

2D: fixed, fixed normalize2d(fixed x, fixed y)

3D: fixed, fixed, fixed normalize3d(fixed x, fixed y, fixed z)

Normalizes the vector and returns the result using Multiple return values.

Example usage

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 inverted pitch angles on actors. ACSUtils functions don't invert 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 inverted 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 inverted pitches on actors. ACSUtils functions don't invert pitches.

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.