# 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`, with `N` bits of precision.

type FixedN[N: static int] = FixedT[int, N]#

A signed 32-bit fixed-point number with `N` bits of precision.

type Fixed = FixedN#

A signed 32-bit fixed-point number with 8 bits of precision.

### Conversion Templates#

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` using `N` 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): int#

Convert a fixed-point number to an integer, always rounding down.

func ceil(n: FixedT): int#

Convert a fixed-point number to an integer, always rounding up.

Get the sign of a number.

Returns `-1` when `x` is negative, `1` when `x` is positive, or `0` when `x` is `0`.

(This procedure comes from the Nim standard library.)

func sgn(x: FixedT): int#

Get the sign of a fixed-point number.

Returns `-1` when `x` is negative, `1` when `x` is positive, or `0` when `x` is `0`.

Returns `1` or `-1` depending on the sign of `x`.

Note: This never returns `0`. Use `sgn` if you want something that does.

func approach[T: SomeNumber|FixedT](x: var T, target, step: T)#

Move `x` towards `target` by `step` 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` and `b` using the weight given by `t`.

`t` should be a fixed point value in the range of `0.0 .. 1.0`.

## Lookup Tables#

type Angle = uint32#

An angle value, where `0x10000` is equivalent to 2π.

func luSin(theta: Angle): FixedN#

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` and `1.0`.

func luCos(theta: Angle): FixedN#

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` and `1.0`.

func luDiv(x: range[0..256]): FixedN#

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-integer `x` via (linear) interpolation between `f(x)` and `f(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

type Vec2f = object#

Fixed-point `24:8` 2D vector/point type

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

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#

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)#

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#

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)#

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 = 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 = 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 = pos.toBgPoint()
```

## Rectangles#

type Rect = object#

Rectangle type. Ranges from `left..right-1`, `top..bottom-1`

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#