====== 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.