a-game

2D platformer written from scratch.
git clone git://git.amin.space/a-game.git
Log | Files | Refs | README | LICENSE

am_math.h (17827B)


      1 #ifndef M_PI
      2 #define M_PI 3.14159265359f
      3 #endif
      4 
      5 // TODO: make sure these functions are inlineable and that the compiler does
      6 // indeed inline them.
      7 
      8 internal f32 math_min(f32 a, f32 b)
      9 {
     10     f32 min = a < b ? a : b;
     11     return min;
     12 }
     13 
     14 internal f32 math_max(f32 a, f32 b)
     15 {
     16     f32 max = a > b ? a : b;
     17     return max;
     18 }
     19 
     20 internal u32 math_pow_of_2(u32 exponent)
     21 {
     22     u32 result = 1 << exponent;
     23     return result;
     24 }
     25 
     26 internal i32 math_round(f32 n)
     27 {
     28     return n < 0.0f ? n - 0.5 : n + 0.5;
     29 }
     30 
     31 internal f32 math_wrap(f32 n, f32 min, f32 max)
     32 {
     33     if (n > max)
     34     {
     35         return n - max;
     36     }
     37     else if (n < min)
     38     {
     39         return n + max;
     40     }
     41     else
     42     {
     43         return n;
     44     }
     45 }
     46 
     47 // TODO: Test this against floorf
     48 internal f32 math_floor(f32 x)
     49 {
     50     f32 result = (f32)((i32)x - (x < 0.0f));
     51     return result;
     52 }
     53 
     54 // TODO: Test this against ceilf
     55 internal i32 math_ceil(f32 x)
     56 {
     57     i32 result = -math_floor(-x);
     58     return result;
     59 }
     60 
     61 internal bool math_f32_is_i32(f32 x)
     62 {
     63     bool result = ((i32)x) == x;
     64     return result;
     65 }
     66 
     67 internal m4 math_m4_init_id()
     68 {
     69     m4 m = {0};
     70     m.E[0][0] = 1.0f;
     71     m.E[1][1] = 1.0f;
     72     m.E[2][2] = 1.0f;
     73     m.E[3][3] = 1.0f;
     74     return m;
     75 }
     76 
     77 internal void math_m4_valueptr(m4 m, f32* out_valueptr)
     78 {
     79     for (u8 v = 0; v < 16; ++v)
     80     {
     81         u8 row = v / 4;
     82         u8 col = v % 4;
     83         out_valueptr[v] = m.E[row][col];
     84     }
     85 }
     86 
     87 internal bool math_m4m4_eq(m4 mat1, m4 mat2)
     88 {
     89     for (u8 i = 0; i < 4; ++i)
     90     {
     91         for (u8 j = 0; j < 4; ++j)
     92         {
     93             if (mat1.E[i][j] != mat2.E[i][j])
     94             {
     95                 return false;
     96             }
     97         }
     98     }
     99     return true;
    100 }
    101 
    102 internal m4 math_m4m4_m(m4 mat1, m4 mat2)
    103 {
    104     m4 r = {
    105         .E[0][0] = (mat1.E[0][0] * mat2.E[0][0]) + (mat1.E[0][1] * mat2.E[1][0]) + (mat1.E[0][2] * mat2.E[2][0]) + (mat1.E[0][3] * mat2.E[3][0]),
    106         .E[0][1] = (mat1.E[0][0] * mat2.E[0][1]) + (mat1.E[0][1] * mat2.E[1][1]) + (mat1.E[0][2] * mat2.E[2][1]) + (mat1.E[0][3] * mat2.E[3][1]),
    107         .E[0][2] = (mat1.E[0][0] * mat2.E[0][2]) + (mat1.E[0][1] * mat2.E[1][2]) + (mat1.E[0][2] * mat2.E[2][2]) + (mat1.E[0][3] * mat2.E[3][2]),
    108         .E[0][3] = (mat1.E[0][0] * mat2.E[0][3]) + (mat1.E[0][1] * mat2.E[1][3]) + (mat1.E[0][2] * mat2.E[2][3]) + (mat1.E[0][3] * mat2.E[3][3]),
    109 
    110         .E[1][0] = (mat1.E[1][0] * mat2.E[0][0]) + (mat1.E[1][1] * mat2.E[1][0]) + (mat1.E[1][2] * mat2.E[2][0]) + (mat1.E[1][3] * mat2.E[3][0]),
    111         .E[1][1] = (mat1.E[1][0] * mat2.E[0][1]) + (mat1.E[1][1] * mat2.E[1][1]) + (mat1.E[1][2] * mat2.E[2][1]) + (mat1.E[1][3] * mat2.E[3][1]),
    112         .E[1][2] = (mat1.E[1][0] * mat2.E[0][2]) + (mat1.E[1][1] * mat2.E[1][2]) + (mat1.E[1][2] * mat2.E[2][2]) + (mat1.E[1][3] * mat2.E[3][2]),
    113         .E[1][3] = (mat1.E[1][0] * mat2.E[0][3]) + (mat1.E[1][1] * mat2.E[1][3]) + (mat1.E[1][2] * mat2.E[2][3]) + (mat1.E[1][3] * mat2.E[3][3]),
    114 
    115         .E[2][0] = (mat1.E[2][0] * mat2.E[0][0]) + (mat1.E[2][1] * mat2.E[1][0]) + (mat1.E[2][2] * mat2.E[2][0]) + (mat1.E[2][3] * mat2.E[3][0]),
    116         .E[2][1] = (mat1.E[2][0] * mat2.E[0][1]) + (mat1.E[2][1] * mat2.E[1][1]) + (mat1.E[2][2] * mat2.E[2][1]) + (mat1.E[2][3] * mat2.E[3][1]),
    117         .E[2][2] = (mat1.E[2][0] * mat2.E[0][2]) + (mat1.E[2][1] * mat2.E[1][2]) + (mat1.E[2][2] * mat2.E[2][2]) + (mat1.E[2][3] * mat2.E[3][2]),
    118         .E[2][3] = (mat1.E[2][0] * mat2.E[0][3]) + (mat1.E[2][1] * mat2.E[1][3]) + (mat1.E[2][2] * mat2.E[2][3]) + (mat1.E[2][3] * mat2.E[3][3]),
    119 
    120         .E[3][0] = (mat1.E[3][0] * mat2.E[0][0]) + (mat1.E[3][1] * mat2.E[1][0]) + (mat1.E[3][2] * mat2.E[2][0]) + (mat1.E[3][3] * mat2.E[3][0]),
    121         .E[3][1] = (mat1.E[3][0] * mat2.E[0][1]) + (mat1.E[3][1] * mat2.E[1][1]) + (mat1.E[3][2] * mat2.E[2][1]) + (mat1.E[3][3] * mat2.E[3][1]),
    122         .E[3][2] = (mat1.E[3][0] * mat2.E[0][2]) + (mat1.E[3][1] * mat2.E[1][2]) + (mat1.E[3][2] * mat2.E[2][2]) + (mat1.E[3][3] * mat2.E[3][2]),
    123         .E[3][3] = (mat1.E[3][0] * mat2.E[0][3]) + (mat1.E[3][1] * mat2.E[1][3]) + (mat1.E[3][2] * mat2.E[2][3]) + (mat1.E[3][3] * mat2.E[3][3]),
    124     };
    125     return r;
    126 }
    127 
    128 internal v4 math_m4v4_m(m4 m, v4 v)
    129 {
    130     v4 r = {
    131         .x = (m.E[0][0] * v.x) + (m.E[0][1] * v.y) + (m.E[0][2] * v.z) + (m.E[0][3] * v.w),
    132         .y = (m.E[1][0] * v.x) + (m.E[1][1] * v.y) + (m.E[1][2] * v.z) + (m.E[1][3] * v.w),
    133         .z = (m.E[2][0] * v.x) + (m.E[2][1] * v.y) + (m.E[2][2] * v.z) + (m.E[2][3] * v.w),
    134         .w = (m.E[3][0] * v.x) + (m.E[3][1] * v.y) + (m.E[3][2] * v.z) + (m.E[3][3] * v.w),
    135     };
    136     return r;
    137 }
    138 
    139 internal bool math_approx(f32 n, f32 compare, f32 epsilon)
    140 {
    141     return fabsf(n - compare) <= epsilon;
    142 }
    143 
    144 internal bool math_v2_is_nonzero(v2 v)
    145 {
    146     // TODO: Figure out whether this epsilon is universally appropriate.
    147     f32 epsilon = 0.000001;
    148     return (
    149         !math_approx(v.x, 0.0f, epsilon)
    150         || !math_approx(v.y, 0.0f, epsilon)
    151     );
    152 }
    153 
    154 internal v2 math_v2_a(v2 vec1, v2 vec2)
    155 {
    156     v2 r = {
    157         .x = vec1.x + vec2.x,
    158         .y = vec1.y + vec2.y,
    159     };
    160     return r;
    161 }
    162 
    163 internal v2 math_v2_negate(v2 v)
    164 {
    165     v2 r = {-v.x, -v.y};
    166     return r;
    167 }
    168 
    169 internal v2 math_v2_s(v2 vec1, v2 vec2)
    170 {
    171     return math_v2_a(vec1, math_v2_negate(vec2));
    172 }
    173 
    174 internal v2 math_v2f_m(v2 v, f32 s)
    175 {
    176     v2 r = {v.x * s, v.y * s};
    177     return r;
    178 }
    179 
    180 internal f32 math_v2_length(v2 v)
    181 {
    182     // TODO: Use inner product of v with itself
    183     return sqrtf((v.x * v.x) + (v.y * v.y));
    184 }
    185 
    186 // TODO: handle degenerate case where l is near 0.0f
    187 // TODO: scale by 1.0f / l
    188 internal v2 math_v2_normalize(v2 v)
    189 {
    190     f32 l = math_v2_length(v);
    191     v2 r = {v.x / l, v.y / l};
    192     return r;
    193 }
    194 
    195 internal f32 math_v2_dot(v2 vec1, v2 vec2)
    196 {
    197     // dot product, a.k.a. inner product or scalar product
    198     f32 dot_product = (vec1.x * vec2.x) + (vec1.y * vec2.y);
    199     // NOTE: this value happens to be:
    200     //     |vec1| * |vec2| * Cos(theta)
    201     // where theta is the angle between vec1 and vec2. This is useful in all
    202     // sorts of ways: What if theta is zero? What if vec1 or vec2 is a unit
    203     // vector?
    204     return dot_product;
    205 }
    206 
    207 internal v2i math_v2i_a(v2i vec1, v2i vec2)
    208 {
    209     v2i r = {vec1.x + vec2.x, vec1.y + vec2.y};
    210     return r;
    211 }
    212 
    213 internal bool math_v2i_eq(v2i vec1, v2i vec2)
    214 {
    215     bool result = (vec1.x == vec2.x && vec1.y == vec2.y);
    216     return result;
    217 }
    218 
    219 internal bool math_v2u_eq(v2u vec1, v2u vec2)
    220 {
    221     bool result = (vec1.x == vec2.x && vec1.y == vec2.y);
    222     return result;
    223 }
    224 
    225 internal v3 math_v3_cross(v3 vec1, v3 vec2)
    226 {
    227     // cross product, a.k.a. outer product or vector product
    228     v3 r = {
    229         .x = (vec1.y * vec2.z) - (vec1.z * vec2.y),
    230         .y = (vec1.z * vec2.x) - (vec1.x * vec2.z),
    231         .z = (vec1.x * vec2.y) - (vec1.y * vec2.x),
    232     };
    233     return r;
    234 }
    235 
    236 internal f32 math_v3_length(v3 v)
    237 {
    238     // TODO: Use inner product of v with itself
    239     // NOTE: math_v3_dot(v, v) returns:
    240     //     |v| * |v| * Cos(theta)
    241     // We know that theta, the angle between v and itself, is always 0, so we
    242     // know that Cos(theta) is always 1. We can then simplify the above
    243     // expression to be:
    244     //     (|v|)^2
    245     // so sqrtf(math_v3_dot(v, v)) is the length of v
    246     return sqrtf((v.x * v.x) + (v.y * v.y) + (v.z * v.z));
    247 }
    248 
    249 internal v3 math_v3f_m(v3 v, f32 s)
    250 {
    251     v3 r = {.x = v.x * s, .y = v.y * s, .z = v.z * s};
    252     return r;
    253 }
    254 
    255 internal v3 math_v3_negate(v3 v)
    256 {
    257     v3 r = {.x = -v.x, .y = -v.y, .z = -v.z};
    258     return r;
    259 }
    260 
    261 // TODO: handle degenerate case where l is near 0.0f
    262 // TODO: scale by 1.0f / l
    263 internal v3 math_v3_normalize(v3 v)
    264 {
    265     f32 l = math_v3_length(v);
    266     v3 r = {v.x / l, v.y / l, v.z / l};
    267     return r;
    268 }
    269 
    270 internal v3 math_v3_a(v3 vec1, v3 vec2)
    271 {
    272     v3 r = {
    273         .x = vec1.x + vec2.x,
    274         .y = vec1.y + vec2.y,
    275         .z = vec1.z + vec2.z
    276     };
    277     return r;
    278 }
    279 
    280 internal v3 math_v3_s(v3 vec1, v3 vec2)
    281 {
    282     return math_v3_a(vec1, math_v3_negate(vec2));
    283 }
    284 
    285 internal v3 math_v3_from_rgb(i32 rgb)
    286 {
    287     v3 result = {
    288         .x = ((rgb >> 16) & 0xFF) / 255.0f,
    289         .y = ((rgb >> 8) & 0xFF) / 255.0f,
    290         .z = ((rgb >> 0) & 0xFF) / 255.0f,
    291     };
    292     return result;
    293 }
    294 
    295 internal bool math_v4v4_eq(v4 vec1, v4 vec2)
    296 {
    297     for (u8 i = 0; i < 4; ++i)
    298     {
    299         if (vec1.E[i] != vec2.E[i])
    300         {
    301             return false;
    302         }
    303     }
    304     return true;
    305 }
    306 
    307 internal void math_clamp(f32 *f, f32 min, f32 max)
    308 {
    309     if (*f < min)
    310     {
    311         *f = min;
    312     }
    313     else if (*f > max)
    314     {
    315         *f = max;
    316     }
    317 }
    318 
    319 internal f32 math_deg(f32 rad)
    320 {
    321     return rad * (180.0f / M_PI);
    322 }
    323 
    324 internal f32 math_lerpf(f32 f, f32 min, f32 max)
    325 {
    326     assert(f >= 0.0f && f <= 1.0f);
    327     return (1.0f - f) * min + f * max;
    328 }
    329 
    330 internal f32 math_rad(f32 deg)
    331 {
    332     return deg * (M_PI / 180.0f);
    333 }
    334 
    335 internal m4 math_rotate_x(m4 m, f32 rad)
    336 {
    337     f32 c = cosf(rad);
    338     f32 s = sinf(rad);
    339 
    340     m4 r = math_m4_init_id();
    341 
    342     r.E[1][1] = c;
    343     r.E[1][2] = -s;
    344 
    345     r.E[2][1] = s;
    346     r.E[2][2] = c;
    347 
    348     return math_m4m4_m(m, r);
    349 }
    350 
    351 internal m4 math_rotate_y(m4 m, f32 rad)
    352 {
    353     f32 c = cosf(rad);
    354     f32 s = sinf(rad);
    355 
    356     m4 r = math_m4_init_id();
    357 
    358     r.E[0][0] = c;
    359     r.E[0][2] = s;
    360 
    361     r.E[2][0] = -s;
    362     r.E[2][2] = c;
    363 
    364     return math_m4m4_m(m, r);
    365 }
    366 
    367 internal m4 math_rotate_z(m4 m, f32 rad)
    368 {
    369     f32 c = cosf(rad);
    370     f32 s = sinf(rad);
    371 
    372     m4 r = math_m4_init_id();
    373 
    374     r.E[0][0] = c;
    375     r.E[0][1] = -s;
    376 
    377     r.E[1][0] = s;
    378     r.E[1][1] = c;
    379 
    380     return math_m4m4_m(m, r);
    381 }
    382 
    383 internal m4 math_rotate(m4 m, f32 rad, v3 axis)
    384 {
    385     axis = math_v3_normalize(axis);
    386 
    387     f32 c = cosf(rad);
    388     f32 s = sinf(rad);
    389 
    390     m4 r = math_m4_init_id();
    391 
    392     r.E[0][0] = c + (powf(axis.x, 2.0f) * (1 - c));
    393     r.E[0][1] = (axis.x * axis.y * (1 - c)) - (axis.z * s);
    394     r.E[0][2] = (axis.x * axis.z * (1 - c)) + (axis.y * s);
    395 
    396     r.E[1][0] = (axis.y * axis.x * (1 - c)) + (axis.z * s);
    397     r.E[1][1] = c + (powf(axis.y, 2.0f) * (1 - c));
    398     r.E[1][2] = (axis.y * axis.z * (1 - c)) - (axis.x * s);
    399 
    400     r.E[2][0] = (axis.z * axis.x * (1 - c)) - (axis.y * s);
    401     r.E[2][1] = (axis.z * axis.y * (1 - c)) + (axis.x * s);
    402     r.E[2][2] = c + (powf(axis.z, 2.0f) * (1 - c));
    403 
    404     return math_m4m4_m(m, r);
    405 }
    406 
    407 internal m4 math_scale(m4 m, v3 v)
    408 {
    409     m4 r = math_m4_init_id();
    410     r.E[0][0] = v.x;
    411     r.E[1][1] = v.y;
    412     r.E[2][2] = v.z;
    413     return math_m4m4_m(m, r);
    414 }
    415 
    416 internal m4 math_translate(m4 m, v3 v)
    417 {
    418     m4 r = math_m4_init_id();
    419     r.E[0][3] = v.x;
    420     r.E[1][3] = v.y;
    421     r.E[2][3] = v.z;
    422     return math_m4m4_m(m, r);
    423 }
    424 
    425 internal m4 math_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
    426 {
    427     // TODO: This assert fails when you minimise the window in Windows.
    428     assert(left != right);
    429     assert(bottom != top);
    430     assert(near != far);
    431 
    432     m4 r = math_m4_init_id();
    433 
    434     r.E[0][0] = 2.0f / (right - left);
    435     r.E[0][1] = 0.0f;
    436     r.E[0][2] = 0.0f;
    437     r.E[0][3] = -(right + left) / (right - left);
    438 
    439     r.E[1][0] = 0.0f;
    440     r.E[1][1] = 2.0f / (top - bottom);
    441     r.E[1][2] = 0.0f;
    442     r.E[1][3] = -(top + bottom) / (top - bottom);
    443 
    444     r.E[2][0] = 0.0f;
    445     r.E[2][1] = 0.0f;
    446     r.E[2][2] = -2.0f / (far - near);
    447     r.E[2][3] = -(far + near) / (far - near);
    448 
    449     r.E[3][0] = 0.0f;
    450     r.E[3][1] = 0.0f;
    451     r.E[3][2] = 0.0f;
    452     r.E[3][3] = 1.0f;
    453 
    454     return r;
    455 }
    456 
    457 internal m4 math_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
    458 {
    459     assert(left != right);
    460     assert(bottom != top);
    461     assert(near != far);
    462 
    463     m4 r = math_m4_init_id();
    464 
    465     r.E[0][0] = (2.0f * near) / (right - left);
    466     r.E[0][1] = 0.0f;
    467     r.E[0][2] = (right + left) / (right - left);
    468     r.E[0][3] = 0.0f;
    469 
    470     r.E[1][0] = 0.0f;
    471     r.E[1][1] = (2.0f * near) / (top - bottom);
    472     r.E[1][2] = (top + bottom) / (top - bottom);
    473     r.E[1][3] = 0.0f;
    474 
    475     r.E[2][0] = 0.0f;
    476     r.E[2][1] = 0.0f;
    477     r.E[2][2] = -(far + near) / (far - near);
    478     r.E[2][3] = (-2.0f * far * near) / (far - near);
    479 
    480     r.E[3][0] = 0.0f;
    481     r.E[3][1] = 0.0f;
    482     r.E[3][2] = -1.0f;
    483     r.E[3][3] = 0.0f;
    484 
    485     return r;
    486 }
    487 
    488 internal m4 math_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far)
    489 {
    490     f32 half_height = tanf(fovy / 2.0f) * near;
    491     f32 half_width = half_height * aspect;
    492     f32 left = -half_width;
    493     f32 right = half_width;
    494     f32 bottom = -half_height;
    495     f32 top = half_height;
    496 
    497     return math_projection_perspective(left, right, bottom, top, near, far);
    498 }
    499 
    500 internal m4 math_camera_look_at(v3 camera_pos, v3 camera_target, v3 up)
    501 {
    502     v3 camera_direction = math_v3_normalize(math_v3_s(camera_pos, camera_target));
    503     v3 camera_right = math_v3_normalize(math_v3_cross(up, camera_direction));
    504     v3 camera_up = math_v3_cross(camera_direction, camera_right);
    505 
    506     m4 look = math_m4_init_id();
    507     look.E[0][0] = camera_right.x;
    508     look.E[0][1] = camera_right.y;
    509     look.E[0][2] = camera_right.z;
    510 
    511     look.E[1][0] = camera_up.x;
    512     look.E[1][1] = camera_up.y;
    513     look.E[1][2] = camera_up.z;
    514 
    515     look.E[2][0] = camera_direction.x;
    516     look.E[2][1] = camera_direction.y;
    517     look.E[2][2] = camera_direction.z;
    518 
    519     return math_m4m4_m(look, math_translate(math_m4_init_id(), math_v3_negate(camera_pos)));
    520 }
    521 
    522 internal bool math_intersect_aabb_aabb(rect r1, rect r2)
    523 {
    524     f32 total_w = (r1.max.x - r1.min.x) + (r2.max.x - r2.min.x);
    525     f32 total_h = (r1.max.y - r1.min.y) + (r2.max.y - r2.min.y);
    526     bool aabbs_do_indeed_intersect = (
    527         fabsf(r1.max.x - r2.min.x) <= total_w
    528         && fabsf(r1.max.y - r2.min.y) <= total_h
    529         && fabsf(r2.max.x - r1.min.x) <= total_w
    530         && fabsf(r2.max.y - r1.min.y) <= total_h
    531     );
    532     return aabbs_do_indeed_intersect;
    533 }
    534 
    535 internal rect math_minkowski_sum_rect(rect r1, v2 r2_dimensions)
    536 {
    537     v2 r2_half = {
    538         .width = 0.5f * r2_dimensions.width,
    539         .height = 0.5f * r2_dimensions.height,
    540     };
    541     rect sum = {
    542         .min = {r1.min.x - r2_half.width, r1.min.y - r2_half.height},
    543         .max = {r1.max.x + r2_half.width, r1.max.y + r2_half.height},
    544     };
    545     return sum;
    546 }
    547 
    548 internal rect math_minkowski_diff_rect(rect r1, v2 r2_dimensions)
    549 {
    550     v2 negated_r2_dim = math_v2_negate(r2_dimensions);
    551     rect difference = math_minkowski_sum_rect(r1, negated_r2_dim);
    552     return difference;
    553 }
    554 
    555 internal rect math_rect_from_center_dim(v2 center, v2 dimensions)
    556 {
    557     v2 half_d = {dimensions.x * 0.5f, dimensions.y * 0.5f};
    558     rect result = {
    559         .min = {center.x - half_d.x, center.y - half_d.y},
    560         .max = {center.x + half_d.x, center.y + half_d.y},
    561     };
    562     return result;
    563 }
    564 
    565 enum MathRectEdge
    566 {
    567     RECT_EDGE_BOTTOM,
    568     RECT_EDGE_TOP,
    569     RECT_EDGE_LEFT,
    570     RECT_EDGE_RIGHT,
    571     MAX_RECT_EDGE,
    572 };
    573 
    574 internal segment math_rect_get_edge(rect r, enum MathRectEdge e)
    575 {
    576     v2 nw = {r.min.x, r.max.y};
    577     v2 ne = r.max;
    578     v2 sw = r.min;
    579     v2 se = {r.max.x, r.min.y};
    580 
    581     segment edge = {0};
    582     switch(e)
    583     {
    584         case RECT_EDGE_BOTTOM:
    585             edge = (segment) {sw, se};
    586             break;
    587         case RECT_EDGE_TOP:
    588             edge = (segment) {nw, ne};
    589             break;
    590         case RECT_EDGE_LEFT:
    591             edge = (segment) {sw, nw};
    592             break;
    593         case RECT_EDGE_RIGHT:
    594             edge = (segment) {se, ne};
    595             break;
    596         default:
    597             assert(false);
    598             break;
    599     }
    600     return edge;
    601 }
    602 
    603 internal v2 math_rect_get_normal(enum MathRectEdge e)
    604 {
    605     v2 normal = {0};
    606     switch(e)
    607     {
    608         case RECT_EDGE_BOTTOM:
    609             normal = (v2) {0.0f, -1.0f};
    610             break;
    611         case RECT_EDGE_TOP:
    612             normal = (v2) {0.0f, 1.0f};
    613             break;
    614         case RECT_EDGE_LEFT:
    615             normal = (v2) {-1.0f, 0.0f};
    616             break;
    617         case RECT_EDGE_RIGHT:
    618             normal = (v2) {1.0f, 0.0f};
    619             break;
    620         default:
    621             assert(false);
    622             break;
    623     }
    624     return normal;
    625 }
    626 
    627 internal bool math_in_interval_open(f32 n, f32 min, f32 max)
    628 {
    629     assert(min <= max);
    630     bool in_open_interval = false;
    631     in_open_interval = (n >= min && n <= max);
    632     return in_open_interval;
    633 }
    634 
    635 internal bool math_v2_lt(v2 v, v2 compare)
    636 {
    637     bool is_lt = false;
    638     is_lt = (v.x < compare.x && v.y < compare.y);
    639     return is_lt;
    640 }
    641 
    642 internal bool math_v2_le(v2 v, v2 compare)
    643 {
    644     bool is_le = false;
    645     is_le = (v.x <= compare.x && v.y <= compare.y);
    646     return is_le;
    647 }
    648 
    649 internal bool math_v2_ge(v2 v, v2 compare)
    650 {
    651     bool is_ge = false;
    652     is_ge = (v.x >= compare.x && v.y >= compare.y);
    653     return is_ge;
    654 }
    655 
    656 internal i32 math_log2(u32 x)
    657 {
    658     i32 result = sizeof(u32) * CHAR_BIT - intr_clz(x) - 1;
    659     return result;
    660 }
    661 
    662 #define math_is_pow_2(n) ((n) & ((n) - 1)) == 0
    663 
    664 internal void math_v2_print(v2 v)
    665 {
    666     printf("( %f, %f )\n", v.x, v.y);
    667 }
    668 
    669 internal void math_v2u_print(v2u v)
    670 {
    671     printf("( %u, %u )\n", v.x, v.y);
    672 }
    673 
    674 internal void math_v2i_print(v2i v)
    675 {
    676     printf("( %i, %i )\n", v.x, v.y);
    677 }
    678 
    679 internal void math_v3_print(v3 v)
    680 {
    681     printf("( %f, %f, %f )\n", v.x, v.y, v.z);
    682 }
    683 
    684 internal void math_v4_print(v4 v)
    685 {
    686     printf("( %f, %f, %f, %f )\n", v.x, v.y, v.z, v.w);
    687 }
    688 
    689 internal void math_rect_print(rect r)
    690 {
    691     printf("rect:\n");
    692     printf(" min: ");
    693     math_v2_print(r.min);
    694     printf(" max: ");
    695     math_v2_print(r.max);
    696 }
    697 
    698 internal void math_m4_print(m4 m)
    699 {
    700     printf("[\n");
    701     printf("  %f, %f, %f, %f\n", m.E[0][0], m.E[0][1], m.E[0][2], m.E[0][3]);
    702     printf("  %f, %f, %f, %f\n", m.E[1][0], m.E[1][1], m.E[1][2], m.E[1][3]);
    703     printf("  %f, %f, %f, %f\n", m.E[2][0], m.E[2][1], m.E[2][2], m.E[2][3]);
    704     printf("  %f, %f, %f, %f\n", m.E[3][0], m.E[3][1], m.E[3][2], m.E[3][3]);
    705     printf("]\n");
    706 }
    707 
    708 #define math_print(x) _Generic((x),\
    709     v2: math_v2_print,             \
    710     v2u: math_v2u_print,           \
    711     v2i: math_v2i_print,           \
    712     v3: math_v3_print,             \
    713     v4: math_v4_print,             \
    714     rect: math_rect_print,         \
    715     m4: math_m4_print              \
    716     )(x)