A tiny software raymarcher that attempts to render "n-dimension" manofold insertions as an image appearing to be a non-euclidean 3-dimensional space. Written for the uqcs hackathon 2020. This repo is a mirror of: https://github.com/ailrst/blackpink
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

559 lines
14 KiB

#include "vect.h"
#include <math.h>
#include <float.h>
/**
* Creates a new vec* struct with the given number of dimenions.
* All elements are set to 0.
*/
struct vec*
new_vec(int num_dimensions)
{
struct vec* new_vector = calloc(1,sizeof(struct vec));
new_vector->dimension = num_dimensions;
new_vector->elements = calloc(num_dimensions, sizeof(double));
return new_vector;
}
struct vec*
new_random_vec(int num_dimensions, double min, double max)
{
struct vec* new_vector = new_vec(num_dimensions);
for (int i = 0; i < num_dimensions; i++) {
double rand_val = (double) rand() / RAND_MAX;
new_vector->elements[i] = min + rand_val * (max - min);
}
return new_vector;
}
struct vec* do_on_vec_ip(struct vec * v, double (*func)(double)) {
for (int i = 0; i < v->dimension; i++) {
v->elements[i] = func(v->elements[i]);
}
return v;
}
struct vec*
new_vec_of(int num_dimensions, double value) {
struct vec* new_vector = new_vec(num_dimensions);
for (int i = 0; i < num_dimensions; i++) {
new_vector->elements[i] = value;
}
return new_vector;
}
/**
* Frees all memory related to the given vec.
*/
void
free_vec(struct vec* a)
{
free(a->elements);
free(a);
}
/**
* Copies the given vector into the newly malloc'd vector.
*/
struct vec*
copy_vec(struct vec* to_copy)
{
struct vec* new_vector = new_vec(to_copy->dimension);
for (int i = 0; i < to_copy->dimension; i++) {
new_vector->elements[i] = to_copy->elements[i];
}
return new_vector;
}
/**
* Takes 2 doubles and creates an vec of 2 dimensions.
*/
struct vec*
new_vec2(double x, double y)
{
struct vec* new_vector = new_vec(2);
new_vector->elements[0] = x;
new_vector->elements[1] = y;
return new_vector;
}
/**
* Takes 3 doubles and creates an vec of 3 dimensions.
*/
struct vec*
new_vec3(double x, double y, double z)
{
struct vec* new_vector = new_vec(3);
new_vector->elements[0] = x;
new_vector->elements[1] = y;
new_vector->elements[2] = z;
return new_vector;
}
/**
* Takes 4 doubles and creates an vec of 4 dimensions.
*/
struct vec*
new_vec4(double w, double x, double y, double z)
{
struct vec* new_vector = new_vec(4);
new_vector->elements[0] = w;
new_vector->elements[1] = x;
new_vector->elements[2] = y;
new_vector->elements[3] = z;
return new_vector;
}
/**
* Adds together two vecs and returns a reference to the summed vec.
*/
struct vec*
add_vec(struct vec* a, struct vec* b)
{
int smallest_dimension = a->dimension < b->dimension ? a->dimension : b->dimension;
int largest_dimension = a->dimension > b->dimension ? a->dimension : b->dimension;
// Perform addition up to where the dimensions of both vectors are equal.
struct vec* result = new_vec(a->dimension);
for (int i = 0; i < smallest_dimension; i++) {
result->elements[i] = a->elements[i] + b->elements[i];
}
// Assume the smaller array is all 0s if the dimensions aren't equal
for (int i = smallest_dimension; i < largest_dimension; i++) {
if (largest_dimension == a->dimension) {
result->elements[i] = a->elements[i];
} else if (largest_dimension == b->dimension) {
result->elements[i] = b->elements[i];
}
}
return result;
}
/**
* Add vec a to vec b and return the pointer to vec a.
*/
struct vec*
add_vec_ip(struct vec* a, struct vec* b)
{
int smallest_dimension = a->dimension < b->dimension ? a->dimension : b->dimension;
int largest_dimension = a->dimension > b->dimension? a->dimension : b->dimension;
for (int i = 0; i < smallest_dimension; i++) {
a->elements[i] = a->elements[i] + b->elements[i];
}
// Assume the smaller array is all 0s if the dimensions aren't equal
for (int i = smallest_dimension; i < largest_dimension; i++) {
if (largest_dimension == a->dimension) {
a->elements[i] = a->elements[i];
} else if (largest_dimension == b->dimension) {
a->elements[i] = b->elements[i];
}
}
return a;
}
/**
* Add vec a to vec b * scaleFactor and store in vec a. Returns a pointer to vec a.
*/
struct vec*
add_scaled_vec_ip(struct vec* a, struct vec* b, double scaleFactor) {
int smallest_dimension = a->dimension < b->dimension ? a->dimension : b->dimension;
int largest_dimension = a->dimension > b->dimension? a->dimension : b->dimension;
for (int i = 0; i < smallest_dimension; i++) {
a->elements[i] = a->elements[i] + b->elements[i] * scaleFactor;
}
// Assume the smaller array is all 0s if the dimensions aren't equal
for (int i = smallest_dimension; i < largest_dimension; i++) {
if (largest_dimension == a->dimension) {
a->elements[i] = a->elements[i];
} else if (largest_dimension == b->dimension) {
a->elements[i] = b->elements[i] * scaleFactor;
}
}
return a;
}
/**
* Subtracts vec b from vec a and returns a reference to the difference vec.
*/
struct vec*
subtract_vec(struct vec* a, struct vec* b)
{
int smallest_dimension = a->dimension < b->dimension ? a->dimension : b->dimension;
int largest_dimension = a->dimension > b->dimension? a->dimension : b->dimension;
// Perform subtraction up to where the dimensions of both vectors are equal.
struct vec* result = new_vec(a->dimension);
for (int i = 0; i < smallest_dimension; i++) {
result->elements[i] = a->elements[i] - b->elements[i];
}
// Assume the smaller array is all 0s if the dimensions aren't equal
for (int i = smallest_dimension; i < largest_dimension; i++) {
if (largest_dimension == a->dimension) {
result->elements[i] = a->elements[i];
} else if (largest_dimension == b->dimension) {
result->elements[i] = - b->elements[i];
}
}
return result;
}
/**
* Subtracts vec b from vec a and stores the result in vec a, returning a
* pointer to it as well.
*/
struct vec*
subtract_vec_ip(struct vec* a, struct vec* b)
{
int smallest_dimension = a->dimension < b->dimension ? a->dimension : b->dimension;
int largest_dimension = a->dimension > b->dimension? a->dimension : b->dimension;
// Perform subtraction up to where the dimensions of both vectors are equal.
for (int i = 0; i < smallest_dimension; i++) {
a->elements[i] = a->elements[i] - b->elements[i];
}
// Assume the smaller array is all 0s if the dimensions aren't equal
for (int i = smallest_dimension; i < largest_dimension; i++) {
if (largest_dimension == a->dimension) {
break; // the elements of a don't need to be changed
} else if (largest_dimension == b->dimension) {
a->elements[i] = - b->elements[i];
}
}
return a;
}
/**
* Calculates the magnitude of vec a.
*/
double
magnitude_vec(struct vec* a)
{
double sum_of_squares = 0;
for (int i = 0; i < a->dimension; i++) {
sum_of_squares +=a->elements[i] * a->elements[i];
}
return sqrt(sum_of_squares);
}
/**
* Normalise the given vec by dividing all components by the magnitude.
*/
struct vec*
normalise_vec(struct vec* a)
{
struct vec* result = new_vec(a->dimension);
double magnitude = magnitude_vec(a);
for (int i = 0; i < a-> dimension; i++) {
result->elements[i] = a->elements[i] / magnitude;
}
return result;
}
/**
* Normalise the given vec and store in vec a.
*/
struct vec*
normalise_vec_ip(struct vec* a)
{
double magnitude = magnitude_vec(a);
for (int i = 0; i < a-> dimension; i++) {
a->elements[i] = a->elements[i] / magnitude;
}
return a;
}
/**
* Calculate the dot product of vec a and vec b.
*/
double
dot_product_vec(struct vec* a, struct vec* b)
{
double result = 0;
for (int i = 0; i < a->dimension; i++) {
result += a->elements[i] * b->elements[i];
}
return result;
}
/**
* Multiply vec a by a scalarFactor and return a new vec with the result.
*/
struct vec*
scalar_multiply_vec(struct vec* a, double scalarFactor)
{
struct vec* result = new_vec(a->dimension);
for (int i = 0; i < a->dimension; i++) {
result->elements[i] = a->elements[i] * scalarFactor;
}
return result;
}
/**
* Multiply vec a by a scalarFactor and return vec a with the result.
*/
struct vec*
scalar_multiply_vec_ip(struct vec* a, double scalarFactor)
{
for (int i = 0; i < a->dimension; i++) {
a->elements[i] *= scalarFactor;
}
return a;
}
/**
* Calculate the distance between vec a and vec b by summing the square of
* the differences of each component.
*/
double
distance_vec(struct vec* a, struct vec* b)
{
double sum_of_differences = 0;
for (int i = 0; i < a->dimension; i++) {
sum_of_differences = pow(a->elements[i] - b->elements[i], 2);
}
return sqrt(sum_of_differences);
}
double
vec_max(const struct vec *v)
{
double max = -DBL_MAX;
for (int i = 0; i < v->dimension; i++) {
if (i > max)
max = v->elements[i];
}
return max;
}
double
vec_min(const struct vec *v)
{
double min = DBL_MAX;
for (int i = 0; i < v->dimension; i++) {
if (i < min)
min = v->elements[i];
}
return min;
}
/**
* Creates a new matrix with the given number of rows and columns
*/
struct mat2*
new_mat(int num_rows, int num_cols)
{
struct mat2* new_matrix = calloc(1, sizeof(struct mat2));
new_matrix->num_rows = num_rows;
new_matrix->num_cols = num_cols;
new_matrix->elements = calloc(num_rows, sizeof(double*));
for (int r = 0; r < num_rows; r++) {
new_matrix->elements[r] = calloc(num_cols, sizeof(double));
}
return new_matrix;
}
void
free_mat(struct mat2* to_free)
{
for (int r = 0; r < to_free->num_rows; r++) {
free(to_free->elements[r]);
}
free(to_free->elements);
free(to_free);
}
/**
* Creates a matrix from the given vectors.
* Each vector should have the same number of elements.
*/
struct mat2*
new_mat_from_vecs(int num_vectors, struct vec** vectors)
{
if (num_vectors == 0 || vectors[0]->dimension == 0) {
}
struct mat2* new_matrix = new_mat(num_vectors, num_vectors + 1);
for (int r = 0; r < new_matrix->num_rows; r++) {
for (int c = 0; c < new_matrix->num_cols; c++) {
new_matrix->elements[r][c] = vectors[r]->elements[c];
}
}
return new_matrix;
}
/**
* Returns a copy of the given matrix ignoring the given row and column.
* This is useful for the Laplace expansion where a certain cell in the matrix
* is multiplied by the matrix given by ignoring the row and column it's in.
*/
struct mat2*
get_determinant_sub_mat(int ignore_col, int ignore_row, struct mat2* matrix)
{
int new_num_rows = matrix->num_rows - 1;
int new_num_cols = matrix->num_cols - 1;
if (ignore_row == -1) {
new_num_rows = matrix->num_rows;
}
if (ignore_col == -1) {
new_num_cols = matrix->num_cols;
}
struct mat2* new_matrix = new_mat(new_num_rows, new_num_cols);
int row_ignored = 0;
int col_ignored = 0;
for (int r = 0; r < matrix->num_rows; r++) {
for (int c = 0; c < matrix->num_cols; c++) {
// Very dodgy stuff going on here
if (c == ignore_col) {
col_ignored = 1;
}
if (r == ignore_row) {
row_ignored = 1;
}
int next_col = c;
int next_row = r;
if (col_ignored == 1) {
next_col = c - 1;
}
if (row_ignored == 1) {
next_row = r - 1;
}
if (c != ignore_col && r != ignore_row) {
new_matrix->elements[next_row][next_col] = matrix->elements[r][c];
}
}
col_ignored = 0;
}
return new_matrix;
}
/**
* Finds the determinant of an n*n matrix using the Laplace expansion
* https://en.wikipedia.org/wiki/Laplace_expansion
*/
double
calc_determinant_mat2(struct mat2* matrix)
{
if (matrix->num_rows != matrix->num_cols) {
// Do something here bc this is impossible.
}
if (matrix->num_rows == 2 && matrix->num_cols == 2) {
return matrix->elements[0][0] * matrix->elements[1][1] -
matrix->elements[0][1] * matrix->elements[1][0];
}
double det = 0;
for (int c = 0; c < matrix->num_cols; c++) {
// Even is added and odd is subtracted according to Laplace expansion
struct mat2* sub_mat = get_determinant_sub_mat(c, 0, matrix);
if (c % 2 == 0) {
det += matrix->elements[0][c] * calc_determinant_mat2(sub_mat);
} else {
det -= matrix->elements[0][c] * calc_determinant_mat2(sub_mat);
}
free_mat(sub_mat);
}
return det;
}
/**
* Takes an array of n vectors and returns a vector perpendicular to all of them.
*
* Creates an array of vectors (v1, v2, ..., vn) and creates a determinant
* matrix to find the cross-product between all of them.
*
* That is for 2 vectors of dimension 3:
* |i j k |
* |v11 v12 v13|
* |v21 v22 v23|
*
* The perpendicular vector = (i, j, k, etc.) where
* i = |v12 v13|
* |v22 v23|,
*
* j = |v11 v13|
* |v21 v23|
*
* and so on.
*/
struct vec*
perpendicular_vec(int num_vectors, struct vec** vectors)
{
if (num_vectors == 0 || vectors[0] == NULL) {
// This shouldnt happen
}
if (num_vectors + 1 != vectors[0]->dimension) {
// This shouldnt happen
}
struct mat2* matrix = new_mat_from_vecs(num_vectors, vectors);
struct vec* perpendicular = new_vec(vectors[0]->dimension);
for (int i = 0; i < num_vectors + 1; i++) {
struct mat2* sub_mat = get_determinant_sub_mat(i, -1, matrix);
if (i % 2 == 0) {
perpendicular->elements[i] = calc_determinant_mat2(sub_mat);
} else {
perpendicular->elements[i] = -calc_determinant_mat2(sub_mat);
}
free_mat(sub_mat);
}
free_mat(matrix);
return perpendicular;
}