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
, withN
bits of precision.
- type FixedN[N: static int] = FixedT[cint, N]¶
A signed 32-bit fixed-point number with
N
bits 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
T
usingN
bits 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
-1
whenx
is negative,1
whenx
is positive, or0
whenx
is0
.(This procedure comes from the Nim standard library.)
- func sgn(x: FixedT): cint¶
Get the sign of a fixed-point number.
Returns
-1
whenx
is negative,1
whenx
is positive, or0
whenx
is0
.
- func sgn2(x: SomeNumber|FixedT): cint¶
Returns
1
or-1
depending on the sign ofx
.Note: This never returns
0
. Usesgn
if you want something that does.
- func approach[T: SomeNumber|FixedT](x: var T, target, step: T)¶
Move
x
towardstarget
bystep
without exceeding target.step
should be a positive number.
- func lerp[A: SomeNumber|FixedT, F: FixedT](a, b: A, t: F): A¶
Linear interpolation between
a
andb
using the weight given byt
.t
should be a fixed point value in the range of0.0 .. 1.0
.
Lookup Tables¶
- type Angle = uint32¶
An angle value, where
0x10000
is equivalent to 2π.
- func luSin(theta: Angle): FixedN[12]¶
Look-up a sine value.
- Theta:
An unsigned integer angle, where
0x10000
is a full turn.
Returns a 20.12 fixed-point number between
-1.0
and1.0
.
- func luCos(theta: Angle): FixedN[12]¶
Look-up a cosine value.
- Theta:
An unsigned integer angle, where
0x10000
is a full turn.
Returns a 20.12 fixed-point number between
-1.0
and1.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-integerx
via (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
x
cint y
cint
- type Vec2f = object¶
Fixed-point
24:8
2D vector/point typeField
Type
x
Fixed y
Fixed
- 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-1
Field
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
n
on all sides
- func inflate(r: var Rect, dw, dh: int)¶
Increase size of rectangle by
dw
horizontally,dh
vertically
- 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¶