math¶
This module provides mathematical utilities, including fixed-point numbers, sin, cos & div lookup tables, 2D vectors and rectangles.
Fixed-Point Numbers¶
Fixed-point arithmetic allows you to treat some integer as if it were fractional, which is much faster than using floats on the GBA.
The Fixed type makes working with ‘24.8’ fixed-point values most convenient. These can be created using the fp template. For example:
let a = fp(1.25)
let b = fp(8.5)
assert a + b == fp(9.75)
Working at other precisions can be done with the FixedN type, and using a different underlying integer type is supported with FixedT.
These are created with the toFixed template:
let k = (1.5).toFixed(int8, 4) # an 8-bit value with 4 bits of precision
static:
echo typeof(k) # prints "FixedT[int8, 4]"
Types¶
- type FixedT[T: SomeInteger, N: static int] = distinct T¶
A fixed-point number based on type
T, withNbits of precision.
- type FixedN[N: static int] = FixedT[cint, N]¶
A signed 32-bit fixed-point number with
Nbits of precision.
- type Fixed = FixedN[8]¶
A signed 32-bit fixed-point number with 8 bits of precision.
Conversion Templates¶
- template fp(n: SomeNumber|FixedT): Fixed¶
Convert a value to fixed-point with 8 bits of precision.
- template toFixed(n: SomeNumber, F: typedesc[FixedT]): untyped¶
Convert a number to the fixed-point type
F.
- template toFixed[T, N](n: FixedT, F: typedesc[FixedT[T,N]]): untyped¶
Convert from one fixed-point format to another.
- template toFixed(n: SomeNumber|FixedT, T: typedesc[SomeInteger], N: static int): untyped¶
Convert a value to fixed-point, with a base type of
TusingNbits of precision.
- template toInt(n: FixedT): int¶
Convert a fixed-point value to an integer.
- template toFloat32(n: FixedT): float32¶
Convert a fixed-point value to floating point.
- template raw[F: FixedT](a: F): untyped¶
Arithmetic Operators¶
The basic operators available to fixed-point numbers are given below. These are implemented as templates to encourage the Nim compiler to evaluate them at compile-time.
Note
There are operators defined between fixed-point and integer types, so fp(0.5) * 2 is allowed, and will give a fixed-point result.
However, there are no implicit converters defined, and you can’t mix fixed-point types of differing size and precision.
To add two fixed-point numbers of different precision together, one must be converted to the other:
let a = fp(0.5) # 8 bits precision
let b = (0.5).toFixed(10) # 10 bits precision
# let sum1 = a + b # Not allowed!
let sum2 = a + fp(b) # Ok, both 8 fractional bits
let sum3 = a.toFixed(10) + b # Ok, both 10 fractional bits
- template `+`[F: FixedT](a, b: F): F¶
- template `-`[F: FixedT](a, b: F): F¶
- template `*`[F: FixedT](a, b: F): F¶
- template `/`[F: FixedT](a, b: F): F¶
- template `==`[F: FixedT](a, b: F): bool¶
- template `<`[F: FixedT](a, b: F): bool¶
- template `<=`[F: FixedT](a, b: F): bool¶
- template `+`[F: FixedT](a: F): F¶
- template `-`[F: FixedT](a: F): F¶
- template `+=`[F: FixedT](a: var F, b: F)¶
- template `-=`[F: FixedT](a: var F, b: F)¶
- template `*=`[F: FixedT](a: var F, b: F)¶
- template `/=`[F: FixedT](a: var F, b: F)¶
- template `+`[F: FixedT, I: SomeInteger](a: F, b: I): F¶
- template `-`[F: FixedT, I: SomeInteger](a: F, b: I): F¶
- template `*`[F: FixedT, I: SomeInteger](a: F, b: I): F¶
- template `/`[F: FixedT, I: SomeInteger](a: F, b: I): F¶
- template `+`[F: FixedT, I: SomeInteger](a: I, b: F): F¶
- template `-`[F: FixedT, I: SomeInteger](a: I, b: F): F¶
- template `*`[F: FixedT, I: SomeInteger](a: I, b: F): F¶
- template `==`[F: FixedT, I: SomeInteger](a: F, b: I): bool¶
- template `<`[F: FixedT, I: SomeInteger](a: F, b: I): bool¶
- template `<=`[F: FixedT, I: SomeInteger](a: F, b: I): bool¶
- template `==`[F: FixedT, I: SomeInteger](a: I, b: F): bool¶
- template `<`[F: FixedT, I: SomeInteger](a: I, b: F): bool¶
- template `<=`[F: FixedT, I: SomeInteger](a: I, b: F): bool¶
- template `+=`[F: FixedT, I: SomeInteger](a: var F, b: I)¶
- template `-=`[F: FixedT, I: SomeInteger](a: var F, b: I)¶
- template `*=`[F: FixedT, I: SomeInteger](a: var F, b: I)¶
- template `/=`[F: FixedT, I: SomeInteger](a: var F, b: I)¶
- template `shr`[F: FixedT, I: SomeInteger](a: F, b: I): F¶
- template `shl`[F: FixedT, I: SomeInteger](a: F, b: I): F¶
- template mul64[F: FixedT](a, b: F): F¶
Multiply two fixed-point values using 64-bit math (to help avoid overflows)
- template div64[F: FixedT](a, b: F): F¶
Divide two fixed-point values using 64-bit math (to help avoid overflows)
- template abs[F: FixedT](a: F): F¶
General Math Functions¶
- func flr(n: FixedT): cint¶
Convert a fixed-point number to an integer, always rounding down.
- func ceil(n: FixedT): cint¶
Convert a fixed-point number to an integer, always rounding up.
- proc sgn[T: SomeNumber](x: T): int¶
Get the sign of a number.
Returns
-1whenxis negative,1whenxis positive, or0whenxis0.(This procedure comes from the Nim standard library.)
- func sgn(x: FixedT): cint¶
Get the sign of a fixed-point number.
Returns
-1whenxis negative,1whenxis positive, or0whenxis0.
- func sgn2(x: SomeNumber|FixedT): cint¶
Returns
1or-1depending on the sign ofx.Note: This never returns
0. Usesgnif you want something that does.
- func approach[T: SomeNumber|FixedT](x: var T, target, step: T)¶
Move
xtowardstargetbystepwithout exceeding target.stepshould be a positive number.
- func lerp[A: SomeNumber|FixedT, F: FixedT](a, b: A, t: F): A¶
Linear interpolation between
aandbusing the weight given byt.tshould be a fixed point value in the range of0.0 .. 1.0.
Lookup Tables¶
- type Angle = uint32¶
An angle value, where
0x10000is equivalent to 2π.
- func luSin(theta: Angle): FixedN[12]¶
Look-up a sine value.
- Theta:
An unsigned integer angle, where
0x10000is a full turn.
Returns a 20.12 fixed-point number between
-1.0and1.0.
- func luCos(theta: Angle): FixedN[12]¶
Look-up a cosine value.
- Theta:
An unsigned integer angle, where
0x10000is a full turn.
Returns a 20.12 fixed-point number between
-1.0and1.0.
- func luDiv(x: range[0..256]): FixedN[16]¶
Look-up a division value between 0 and 256.
Returns
1/x, represented as a 16.16 fixed point number.
- func luLerp[A: SomeInteger|FixedT, F: FixedT](lut: openArray[A], x: F): A¶
Linear interpolator for LUTs.
An LUT (lookup table) is essentially the discrete form of a function,
f(x). You can get values for non-integerxvia (linear) interpolation betweenf(x)andf(x+1).- Lut:
The LUT to interpolate from.
- X:
Fixed-point number to interpolate at.
Example:
let myLut* {.importc.}: array[100, int16] # some array of data. let n: int16 = luLerp(myLut, fp(10.75)) # get a value ¾ between the 10th and 11th entry of `myLut`.
2D Vectors¶
- type Vec2i = object¶
Integer 2D vector/point type
Field
Type
xcint ycint
- type Vec2f = object¶
Fixed-point
24:82D vector/point typeField
Type
xFixed yFixed
- func vec2i(x, y: int): Vec2i¶
Initialise an integer vector
- func vec2i(): Vec2i¶
Initialise an integer vector to 0,0
- func vec2i(v: Vec2f): Vec2i¶
Convert an integer vector to a fixed-point vector
- func vec2f(x: SomeNumber|FixedT, y: SomeNumber|FixedT): Vec2f¶
Initialise a fixed-point vector
- func vec2f(): Vec2f¶
Initialise a fixed-point vector to 0,0
- func vec2f(v: Vec2i): Vec2f¶
Convert a fixed-point vector to an integer vector
- func `+`(a, b: Vec2i): Vec2i¶
Add two vectors
- func `-`(a, b: Vec2i): Vec2i¶
Subtract two vectors
- func `*`(a, b: Vec2i): Vec2i¶
Component-wise multiplication of two vectors
- func `*`(a: Vec2i, n: int): Vec2i¶
Scale vector by n
- func `*`(n: int, a: Vec2i): Vec2i¶
Scale vector by n
- func `/`(a, b: Vec2i): Vec2i¶
Component-wise division of two vectors
- func `/`(a: Vec2i, n: int): Vec2i¶
Scale vector by 1/n
- func `/`(n: int, a: Vec2i): Vec2i¶
Scale vector by 1/n
- func dot(a, b: Vec2i): int¶
Dot product of two vectors
- func `-`(a: Vec2i): Vec2i¶
Equivalent to a * -1
- func `+=`(a: var Vec2i, b: Vec2i)¶
Vector compound addition
- func `-=`(a: var Vec2i, b: Vec2i)¶
Vector compound subtraction
- func `*=`(a: var Vec2i, b: Vec2i)¶
Vector component-wise compound multiplicatoin
- func `*=`(a: var Vec2i, n: int)¶
Compound scale a vector by n
- func `/=`(a: var Vec2i, b: Vec2i)¶
Vector component-wise compound division
- func `/=`(a: var Vec2i, n: int)¶
Compound scale a vector by 1/n
- func `+`(a, b: Vec2i): Vec2i¶
Add two vectors
- func `-`(a, b: Vec2i|Vec2f): Vec2f¶
Subtract two fixed-point vectors
- func `*`(a: Vec2f, b: Vec2i|Vec2f): Vec2f¶
Component-wise multiplication of two vectors
- func `*`(a: Vec2f, n: Fixed|int): Vec2f¶
Scale a fixed-point vector by n
- func `*`(n: Fixed|int, a: Vec2f): Vec2f¶
Scale a fixed-point vector by n
- func `/`(a: Vec2f, b: Vec2i|Vec2f): Vec2f¶
Component-wise division of two vectors
- func `/`(a: Vec2f, n: Fixed|int): Vec2f¶
Scale a fixed-point vector by 1/n
- func `/`(n: Fixed|int, a: Vec2f): Vec2f¶
Scale a fixed-point vector by 1/n
- func dot(a, b: Vec2i): int¶
Dot product of two vectors
- func `-`(a: Vec2f): Vec2f¶
Equivalent to a * -1
- func `+=`(a: var Vec2i, b: Vec2i)¶
Vector compound addition
- func `-=`(a: var Vec2i, b: Vec2i)¶
Vector compound subtraction
- func `*=`(a: var Vec2f, b: Vec2i|Vec2f)¶
Vector component-wise compound multiplicatoin
- func `/=`(a: var Vec2f, b: Vec2i|Vec2f)¶
Vector component-wise compound division
- func `*=`(a: var Vec2f, n: Fixed|int)¶
Compound scale a vector by n
- func `/=`(a: var Vec2f, n: Fixed|int)¶
Compound scale a vector by 1/n
Vector Conversion¶
- func initBgPoint(x = 0'i16, y = 0'i16): BgPoint¶
Create a new pair of values used by the BG scroll registers, e.g.
bgofs[0] = initBgPoint(10, 20)
- func toBgPoint(a: Vec2i): BgPoint¶
Convert a vector to a pair of values used by the BG scroll registers, e.g.
bgofs[0] = pos.toBgPoint()
- func toBgPoint(a: Vec2f): BgPoint¶
Convert a fixed-point vector to a pair of values used by the BG scroll registers, e.g.
bgofs[0] = pos.toBgPoint()
Rectangles¶
- type Rect = object¶
Rectangle type. Ranges from
left..right-1,top..bottom-1Field
Type
- func rectBounds(left, top, right, bottom: int): Rect¶
- func rectAt(x, y, width, height: int): Rect¶
- func x(r: Rect): int¶
- func y(r: Rect): int¶
- func w(r: Rect): int¶
- func h(r: Rect): int¶
- func width(r: Rect): int¶
- func height(r: Rect): int¶
- func `x=`(r: var Rect, x: int)¶
- func `y=`(r: var Rect, y: int)¶
- func `w=`(r: var Rect, w: int)¶
- func `h=`(r: var Rect, h: int)¶
- func `width=`(r: var Rect, w: int)¶
- func `height=`(r: var Rect, h: int)¶
- func move(r: var Rect, dx, dy: int)¶
Move rectangle by (
dx,dy)
- func move(r: var Rect, vec: Vec2i)¶
Move rectangle by
vec
- func inflate(r: var Rect, n: int)¶
Increase size of rectangle by
non all sides
- func inflate(r: var Rect, dw, dh: int)¶
Increase size of rectangle by
dwhorizontally,dhvertically
- func center(r: Rect): Vec2i¶
Get the center point of a rectangle
- func `center=`(r: var Rect, p: Vec2i)¶
Set the center point of a rectangle
- func topLeft(r: Rect): Vec2i¶
- func topRight(r: Rect): Vec2i¶
- func bottomLeft(r: Rect): Vec2i¶
- func bottomRight(r: Rect): Vec2i¶