Goal: I am trying to create a ray tracer in C. I just added in a light source that should give each of my three spheres a shading effect based on where the light is. If the light is to the left of all of them, a shadow should be cased on the right.
Problem: When changing the light intensities and position of the light, all the spheres are changed uniformly. The spheres will be more or less lit equally and there is no variation of lighting on individual pixels on the sphere.
My debugging attempts: I have tried looking through the variable outputs by printing out a lot of different info and I think the source comes from my variable
diffuse_light_intensity
which does not change much (through all the iterations on the screen the value changes twice when it should be changing quite often due to the angles of the light on the surface changing quite a bit)
My Code: (my theory is the problem lies in scene_intersect() or cast_ray())
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <limits.h>
typedef struct {
float position[3];
float intensity;
} Light;
typedef struct {
float diffuse_color[3];
} Material;
typedef struct {
float center[3];
float radius;
Material material;
} Sphere;
int arrSub(const float arr1[], const float arr2[], float subArr[], int length) {
/*
Requires 3 equally sized arrays (denoted as length),
arr1 - arr2 will result in the third array subArr
*/
for (int i = 0; i < length; i++) {
subArr[i] = arr1[i] - arr2[i];
}
return 0;
}
int arrAdd(const float arr1[], const float arr2[], float addArr[], int length) {
/*
Requires 3 equally sized arrays (denoted as length),
arr1 + arr2 will result in the third array subArr
*/
for (int i = 0; i < length; i++) {
addArr[i] = arr1[i] + arr2[i];
}
return 0;
}
int arrScalarMult(const float arr1[], float scalar, float newArr[], int length) {
/*
Requires 3 equally sized arrays (denoted as length),
arr1 - arr2 will result in the third array subArr
*/
for (int i = 0; i < length; i++) {
newArr[i] = arr1[i] * scalar;
}
return 0;
}
float dotProduct(const float arr1[], const float arr2[], int length) {
/*
Returns the dot product of two equal sized arrays
(treated as vectors)
a (dot) b = a1b1 + a2b2 + ... anbn
*/
float result = 0;
for (int i = 0; i < length; i++) {
result += arr1[i] * arr2[i];
}
return result;
}
int normalize(float arr[], int len) {
//Normalize a vector (array)
float sumSqr;
float norm;
for (int i = 0; i < len; i++) {
sumSqr += arr[i] * arr[i];
}
norm = sqrt(sumSqr);
for (int i = 0; i < len; i++) {
arr[i] = arr[i] / norm;
}
return 0;
}
bool ray_intersect(const float origin[], const float dir[], float t0, Sphere s) {
/*
Ray-Sphere Intersection
Vectors:
origin (the zero vector)
dir (direction vector)
L (vector from origin to center of sphere)
Scalars:
tca
d2
thc
t0
t1
*/
float L[3] = {0,0,0}; //The zero vector
arrSub(s.center, origin, L, 3); //L is now the vector from origin to the sphere's center
float tca = dotProduct(L, dir, 3); //Projection of L onto dir
float d2 = dotProduct(L, L, 3) - tca*tca;
if (d2 > s.radius * s.radius) return false; //There is no intersection, so return false.
float thc = sqrtf((s.radius*s.radius - d2));
t0 = tca - thc;
float t1 = tca + thc;
if (t0 < 0) {
t0 = t1;
}
if (t0 < 0) return false;
return true;
}
bool scene_intersect(const float origin[], const float dir[], const Sphere s[], int len, float hit[], float N[], Material * ptr_m) {
float sphere_dist = INT_MAX;
for (size_t i=0; i < len; i++) {
float dist_i;
if (ray_intersect(origin, dir, dist_i, s[i]) && dist_i < sphere_dist) {
sphere_dist = dist_i;
float dirDist[3];
arrScalarMult(dir, dist_i, dirDist, 3);
arrAdd(origin, dirDist, hit, 3);
float hitMinusCenter[3];
arrSub(hit, s[i].center, hitMinusCenter, 3);
normalize(hitMinusCenter, 3);
N[0] = hitMinusCenter[0];
N[1] = hitMinusCenter[1];
N[2] = hitMinusCenter[2];
* ptr_m = s[i].material;
}
}
return sphere_dist<1000;
}
int cast_ray(const float origin[], const float dir[], const Sphere s[], const Light l[], int l_size, unsigned char colorArr[]) {
float point[3], N[3];
Material m;
Material * ptr_m = &m;
if (!scene_intersect(origin, dir, s, 3, point, N, ptr_m)) {
//background
colorArr[0] = 5; //red
colorArr[1] = 100; //green
colorArr[2] = 250; //blue
} else {
float diffuse_light_intensity = 0;
float light_dir[3];
for (size_t i = 0; i < l_size; i++) {
arrSub(l[i].position, point, light_dir, 3);
normalize(light_dir, 3);
diffuse_light_intensity += l[i].intensity * ((0.f >= dotProduct(light_dir, N, 3) ? (0.f) : (dotProduct(light_dir, N, 3))));
}
//light up pixel
colorArr[0] = m.diffuse_color[0] * diffuse_light_intensity;
colorArr[1] = m.diffuse_color[1] * diffuse_light_intensity;
colorArr[2] = m.diffuse_color[2] * diffuse_light_intensity;
}
return 0;
}
int render(const Sphere s[], const Light l[], int l_length) {
/*
Creates image in a new color each step.
*/
const int width = 1024;
const int height = 768;
FILE *fp = fopen("fourth.ppm", "wb"); // Write in binary mode
(void) fprintf(fp, "P6\n%d %d\n255\n", width, height);
float fov = 3.1415926535/2.; // Field of View
#pragma omp parallel for
for (size_t j = 0; j < height; j++) {
for (size_t i = 0; i < width; i++) {
float x = (2*(i+.5)/(float)width - 1)*tan(fov/2.)*width/(float)height;
float y = -(2*(j+.5)/(float)height - 1)*tan(fov/2.);
float dir[] = {x,y,-1};
normalize(dir, 3);
unsigned char color[3];
const float origin[] = {0,0,0};
cast_ray(origin, dir, s, l, l_length, color);
(void) fwrite(color, 1, 3, fp);
}
}
(void) fclose(fp);
return 0;
}
int main(void) {
Material red = {255,0,0};
Material pink = {150,10,150};
Material gold = {255, 195, 0};
//Populate with spheres
Sphere s[3];
Sphere originalS = {{-3,0,-16},2,gold};
Sphere bigS = {{-1.0, -1.5, -12}, 3, red};
Sphere anotherS = {{7,5,-18},2,pink};
s[0] = originalS;
s[1] = bigS;
s[2] = anotherS;
//Add light source
Light l[1];
Light test_light = {{-20,20,20}, 1.5};
l[0] = test_light;
render(s,l, 1);
printf("Run success!\n");
return 0;
}
If any clarification is needed on my code please let me know, I am quite new to both C and stackoverflow.
There's a fundamental error in ray_intersect where you're passing the t0 variable by value, and not as a pointer, and therefore in the scene_intersect function its value is always zero.
The other problem is that you don't initialize the sumSqr in the normalize function, resulting in that function returning NaN for each vector component.
With those two fixed I get something approximating shaded balls. The errors in that image are caused by failing to ensure that your output pixel values fall in the range [0, 255].
NB: both of these first errors are detected if you turn on full compiler error checking, warning you of uninitialised variables being used.
Related
Working on an edge detection function. Looking back at my code I think that I have concept / logic down. But the results aren't coming out the way it should.
typedef struct {
int Red;
int Green;
int Blue;
} GTOTALS;
// Detect edges
void edges(int height, int width, RGBTRIPLE image[height][width])
{
const int MAX = 3;
// Copy Image
RGBTRIPLE Copy[height][width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
Copy[i][j] = image[i][j];
}
}
// Gx and Gy Grids 3 x 3
int Gx[MAX][MAX] = {
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}
};
int Gy[MAX][MAX] = {
{-1, -2, -1},
{0, 0, 0},
{1, 2, 1}
};
// Loop through each pixel
for (int Rows = 0; Rows < height; Rows++)
{
for (int Cols = 0; Cols < width; Cols++)
{
// Hold RGB Values + Refresh Current Pixel RGB
int CRed = 0, CGreen = 0, CBlue = 0;
// Store Gx and Gy RGB Values
GTOTALS X;
GTOTALS Y;
// Loop through surrouding pixels
for (int S_Rows = Rows - 1, R = 0; S_Rows <= Rows + 1; S_Rows++, R++)
{
for (int S_Cols = Cols - 1, C = 0; S_Cols <= Cols + 1; S_Cols++, C++)
{
// Check Pixel Validity
if ((S_Rows >= 0) && (S_Rows < height) && (S_Cols >= 0) && (S_Cols < width))
{
// RGB Gx Total Values
X.Red += Copy[S_Rows][S_Cols].rgbtRed * Gx[R][C]; // Current Pixel Red * Gx[N][N]
X.Green += Copy[S_Rows][S_Cols].rgbtGreen * Gx[R][C]; // Current Pixel Green * Gx[N][N]
X.Blue += Copy[S_Rows][S_Cols].rgbtBlue * Gx[R][C]; // Current Pixel Blue * Gx[N][N]
// RGB Gy Total Values
Y.Red += Copy[S_Rows][S_Cols].rgbtRed * Gy[R][C]; // Current Pixel Red * Gy[N][N]
Y.Green += Copy[S_Rows][S_Cols].rgbtGreen * Gy[R][C]; // Current Pixel Green * Gy[N][N]
Y.Blue += Copy[S_Rows][S_Cols].rgbtBlue * Gy[R][C]; // Current Pixel Blue * Gy[N][N]
}
}
}
// Value = Square Root(Gx^2 + Gx^2)
CRed = round( sqrt( pow(X.Red, 2.0) + pow(Y.Red, 2.0) ) );
CGreen = round( sqrt( pow(X.Green, 2.0) + pow(Y.Green, 2.0) ) );
CBlue = round( sqrt( pow(X.Blue, 2.0) + pow(Y.Blue, 2.0) ) );
// MAX 255
Cap(&CRed);
Cap(&CGreen);
Cap(&CBlue);
// Update Target Pixel
image[Rows][Cols].rgbtRed = CRed;
image[Rows][Cols].rgbtGreen = CGreen;
image[Rows][Cols].rgbtBlue = CBlue;
}
}
return;
}
void Cap(int *Value)
{
if (*Value > 255)
{
*Value = 255;
}
}
When I run the prograM most of the RGB values turn out to be 255. I've played around with using different data types and moving around when variables are created but that doesn't seem to help. I've also tried miniature versions of the code and all seems to work as intended but not sure why when I add it together it doesn't seem to give the correct results
Here is description of Sobel filter
// from Source to Destination
int ComputeBoundaries(unsigned char S[], unsigned char D[])
{
unsigned int iX,iY; /* indices of 2D virtual array (image) = integer coordinate /
unsigned int i; / index of 1D array /
/ sobel filter */
unsigned char G, Gh, Gv;
// boundaries are in D array ( global var )
// clear D array
memset(D, iColorOfBasin1, iSize*sizeof(*D)); // for heap-allocated arrays, where N is the number of elements = FillArrayWithColor(D , iColorOfBasin1);
// printf(" find boundaries in S array using Sobel filter\n");
#pragma omp parallel for schedule(dynamic) private(i,iY,iX,Gv,Gh,G) shared(iyMax,ixMax)
for(iY=1;iY<iyMax-1;++iY){
for(iX=1;iX<ixMax-1;++iX){
Gv= S[Give_i(iX-1,iY+1)] + 2S[Give_i(iX,iY+1)] + S[Give_i(iX-1,iY+1)] - S[Give_i(iX-1,iY-1)] - 2S[Give_i(iX-1,iY)] - S[Give_i(iX+1,iY-1)];
Gh= S[Give_i(iX+1,iY+1)] + 2S[Give_i(iX+1,iY)] + S[Give_i(iX-1,iY-1)] - S[Give_i(iX+1,iY-1)] - 2S[Give_i(iX-1,iY)] - S[Give_i(iX-1,iY-1)];
G = sqrt(GhGh + GvGv);
i= Give_i(iX,iY); /* compute index of 1D array from indices of 2D array /
if (G==0) {D[i]=255;} / background /
else {D[i]=0;} / boundary */
}
}
return 0;
}
// copy from Source to Destination
int CopyBoundaries(unsigned char S[], unsigned char D[])
{
unsigned int iX,iY; /* indices of 2D virtual array (image) = integer coordinate /
unsigned int i; / index of 1D array */
//printf("copy boundaries from S array to D array \n");
for(iY=1;iY<iyMax-1;++iY)
for(iX=1;iX<ixMax-1;++iX)
{i= Give_i(iX,iY); if (S[i]==0) D[i]=0;}
return 0;
}
Here is the image and a full program
result:
I have a code where It wants to read which operation it wants to do and then calculate the numbers using the operation. I have the library code and header but the main code, I don't know what to implement within the body. I have a skeleton code however I'm drawing a blank on what else I need to do.
If this helps, the operation I'm trying to achieve is:
./statistics mean 10 20 30
and the answer should be: 20.0
Library Header:
#ifndef MY_LIBRARY_H
#define MY_LIBRARY_H
float OP_MEAN(int size, float * values);
float OP_MAX(int size, float * values);
float OP_SD(int size, float * values);
#endif
Library Code:
float OP_MEAN(int size, float * values)
{
int i;
float sum = 0.0;
for(i = 0; i < size; i++)
{
sum += values[i];
}
return sum/n;
}
float OP_MAX(int size, float * values)
{
int i;
float maximum = 0.0;
for(i = 0; i < size; i++)
{
if (values[i] > maximum)
{
maximum = values[i];
}
}
return maximum;
}
float OP_SD(int size, float * values)
{
float mean = OP_MEAN(size, values);
float sum = 0.0, un_sqrt_sd = 0.0;
int i;
for(i = 0; i < size; i++)
{
sum += pow(double(values[i] - mean), double(2));
}
un_sqrt_sd = (float) sum / (size - 1);
return (float) sqrt(double(un_sqrt_sd));
}
Main Code:
#include <stdio.h>
#include "my_lib.h"
// define the operations as enum here
enum operations{
OP_MEAN
OP_MAX
OP_SD
};
int main(int argc, char ** argv){
//parse command line arguments
enum operations op;
// 1. find the operator
if (strcmp(argv[1], "max") ...){ // check with man strcmp
op = OP_MAX;
}...
// 2. define an array with the necessary size
// 3. load numbers using my_atoi()
float (*fp)(int size, float * values); // declared function pointer variable
// use a switch to set the function pointer
switch(op_var){
case(OP_MAX):
fp = ... // set the function pointer
}
// invoke the function pointer
float result = fp(size, array)
printf("%.1f\n", result);
return 0;
}
I'm using FFTW in my C code and I have some issue.
First, I can transform an original image to two images (mag+phase) and get back the original image with the inverse transform.
However, If I want to get a mag file centered in frequency it does not work anymore: the final image has some issues.
Here some pieces of my code. Can someone help me to find the error in my code?
EDIT: I've fixed the code to follow #francis recommandation, but my issues is always here.
enum {
TYPE_CENTERED,
TYPE_REGULAR
};
static void fft_to_spectra(fits* fit, fftw_complex *frequency_repr, double *as,
double *ps, int nbdata) {
unsigned int i;
for (i = 0; i < nbdata; i++) {
double r = creal(frequency_repr[i]);
double im = cimag(frequency_repr[i]);
as[i] = hypot(r, im);
ps[i] = atan2(im, r);
}
}
static void fft_to_freq(fits* fit, fftw_complex *frequency_repr, double *as, double *ps, int nbdata) {
unsigned int i;
for (i = 0; i < nbdata; i++) {
frequency_repr[i] = as[i] * (cos(ps[i]) + I * sin(ps[i]));
}
}
void change_symmetry(unsigned int width, unsigned int height, unsigned int i, unsigned int j, unsigned int *x,
unsigned int *y) {
if (i < width / 2 && j < height / 2) {
*x = i + width / 2;
*y = j + height / 2;
}
if (i >= width / 2 && j < height / 2) {
*x = i - width / 2;
*y = j + height / 2;
}
if (i < width / 2 && j >= height / 2) {
*x = i + width / 2;
*y = j - height / 2;
}
if (i >= width / 2 && j >= height / 2) {
*x = i - width / 2;
*y = j - height / 2;
}
}
static void centered(WORD *buf, unsigned int width,
unsigned int height) {
unsigned int i, j;
WORD *temp = malloc(width * height * sizeof(WORD));
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
unsigned int x = i;
unsigned int y = j;
change_symmetry(width, height, i, j, &x, &y);
temp[j * width + i] = buf[y * width + x];
}
}
memcpy(buf, temp, sizeof(WORD) * width * height);
free(temp);
}
static void normalisation_spectra(unsigned int w, unsigned int h, double *modulus, double *phase,
WORD *abuf, WORD *pbuf) {
unsigned int i;
for (i = 0; i < h * w; i++) {
pbuf[i] = round_to_WORD(((phase[i] + M_PI) * USHRT_MAX_DOUBLE / (2 * M_PI)));
abuf[i] = round_to_WORD((modulus[i] / w / h));
}
}
static void save_dft_information_in_gfit(fits *fit) {
strcpy(gfit.dft.ord, fit->dft.type);
strcpy(gfit.dft.ord, fit->dft.ord);
}
static void FFTD(fits *fit, fits *x, fits *y, int type_order, int layer) {
WORD *xbuf = x->pdata[layer];
WORD *ybuf = y->pdata[layer];
WORD *gbuf = fit->pdata[layer];
unsigned int i;
unsigned int width = fit->rx, height = fit->ry;
int nbdata = width * height;
fftw_complex *spatial_repr = fftw_malloc(sizeof(fftw_complex) * nbdata);
if (!spatial_repr) {
return;
}
fftw_complex *frequency_repr = fftw_malloc(sizeof(fftw_complex) * nbdata);
if (!frequency_repr) {
fftw_free(spatial_repr);
return;
}
/* copying image selection into the fftw data */
#ifdef _OPENMP
#pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static) if(nbdata > 15000)
#endif
for (i = 0; i < nbdata; i++) {
spatial_repr[i] = (double) gbuf[i];
}
/* we run the Fourier Transform */
fftw_plan p = fftw_plan_dft_2d(height, width, spatial_repr, frequency_repr,
FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(p);
/* we compute modulus and phase */
double *modulus = malloc(nbdata * sizeof(double));
double *phase = malloc(nbdata * sizeof(double));
fft_to_spectra(fit, frequency_repr, modulus, phase, nbdata);
//We normalize the modulus and the phase
normalisation_spectra(width, height, modulus, phase, xbuf, ybuf);
if (type_order == TYPE_CENTERED) {
strcpy(x->dft.ord, "CENTERED");
centered(xbuf, width, height);
centered(ybuf, width, height);
}
free(modulus);
free(phase);
fftw_destroy_plan(p);
fftw_free(spatial_repr);
fftw_free(frequency_repr);
}
static void FFTI(fits *fit, fits *xfit, fits *yfit, int type_order, int layer) {
WORD *xbuf = xfit->pdata[layer];
WORD *ybuf = yfit->pdata[layer];
WORD *gbuf = fit->pdata[layer];
unsigned int i;
unsigned int width = xfit->rx;
unsigned int height = xfit->ry;
int nbdata = width * height;
double *modulus = calloc(1, nbdata * sizeof(double));
double *phase = calloc(1, nbdata * sizeof(double));
if (type_order == TYPE_CENTERED) {
centered(xbuf, width, height);
centered(ybuf, width, height);
}
for (i = 0; i < height * width; i++) {
modulus[i] = (double) xbuf[i] * (width * height);
phase[i] = (double) ybuf[i] * (2 * M_PI / USHRT_MAX_DOUBLE);
phase[i] -= M_PI;
}
fftw_complex* spatial_repr = fftw_malloc(sizeof(fftw_complex) * nbdata);
if (!spatial_repr) {
return;
}
fftw_complex* frequency_repr = fftw_malloc(sizeof(fftw_complex) * nbdata);
if (!frequency_repr) {
fftw_free(spatial_repr);
return;
}
fft_to_freq(fit, frequency_repr, modulus, phase, nbdata);
fftw_plan p = fftw_plan_dft_2d(height, width, frequency_repr, spatial_repr,
FFTW_BACKWARD, FFTW_ESTIMATE);
fftw_execute(p);
for (i = 0; i < nbdata; i++) {
double pxl = creal(spatial_repr[i]) / nbdata;
gbuf[i] = round_to_WORD(pxl);
}
free(modulus);
free(phase);
fftw_destroy_plan(p);
fftw_free(spatial_repr);
fftw_free(frequency_repr);
}
Here my images, the original one and the FFTD(centered)->FFTI result
The plan is created using the flag FFTW_MEASURE. Hence, several DFT are computed and the input array is likely overwritten. Here is the start of the description of planner flags in the documentation of FFTW:
FFTW_ESTIMATE specifies that, instead of actual measurements of different algorithms, a simple heuristic is used to pick a (probably sub-optimal) plan quickly. With this flag, the input/output arrays are not overwritten during planning.
FFTW_MEASURE tells FFTW to find an optimized plan by actually computing several FFTs and measuring their execution time. Depending on your machine, this can take some time (often a few seconds). FFTW_MEASURE is the default planning option.
Either switch to FFTW_ESTIMATE or create the plan before populating the input array:
/* we run the Fourier Transform */
fftw_plan p = fftw_plan_dft_2d(width, height, spatial_repr, frequency_repr,
FFTW_FORWARD, FFTW_MEASURE);
/* copying image selection into the fftw data */
#ifdef _OPENMP
#pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static) if(nbdata > 15000)
#endif
for (i = 0; i < nbdata; i++) {
spatial_repr[i] = (double) gbuf[i];
}
If you intend to a single image, using FFTW_ESTIMATE is the way to go. On the contrary, if you consider treating multiple images, creating the plan once using FFTW_MEASURE and storing it is a good option. Then you may use New-array Execute Functions each time a FFT is to be performed:
fftw_execute_dft(p, spatial_repr, frequency_repr);
You can test the return value of malloc() or fftw_malloc() to check if the allocations went right. If not, it returns NULL. fftw_malloc() is implemented as function *X(kernel_malloc)(size_t n) in fftw-3.3.6-pl2/kernel/kalloc.c . It calls functions like memalign() or _aligned_malloc() among others. Both these two return NULL just like malloc() in case of failure. Finally, I did not spotted a critical issue regarding memory allocation of deallocation in the piece of code you provided.
The argument double nbdata in fft_to_spectra() should likely be an integer. Valgrind might have considered it as strange...
EDIT : the change_symmetry() is to be modified for odd sizes. Something like:
void change_symmetry_forward(unsigned int width, unsigned int height, unsigned int i, unsigned int j, unsigned int *x,
unsigned int *y) {
*x = i + width / 2;
if (*x>=width){
*x=*x-width;
}
*y = j + height / 2;
if(*y>=height){
*y =*y-height;
}
}
and
void change_symmetry_backward(unsigned int width, unsigned int height, unsigned int i, unsigned int j, unsigned int *x,
unsigned int *y) {
*x = i +width- width / 2;
if (*x>=width){
*x=*x-width;
}
*y = j +height- height / 2;
if(*y>=height){
*y =*y-height;
}
}
I'm currently working on this : I generate a Paraview .vtm file that contains several .vtr files. Each .vtr file contains values, and coordinates, like this, assuming I'm working on a dimension of 8 :
<PointData Scalars="U">
<DataArray type="Float32" Name="U" format="ascii">
<!-- 8*8*8 values -->
</DataArray>
</PointData>
<Coordinates>
<DataArray type="Float32" Name="x" format="ascii">
<!-- 8 x values -->
</DataArray>
<DataArray type="Float32" Name="y" format="ascii">
<!-- 8 y values -->
</DataArray>
<DataArray type="Float32" Name="z" format="ascii">
<!-- 8 z values -->
</DataArray>
</Coordinates>
I use a quadridimensionnal array to store my values : float ****tab, with tab[s][x][y][z], where :
s is the current split step. It increments everytime I start working on the next .vtr file.
x, y, z the values.
Now is what causes me trouble : the coordinates where I have to place these points can be anything. It can be constant (following a step, like 0, 0.1, 0.2, and so on), or not.
I store the coordinates in three arrays : x[], y[], z[]. My goal is to cut the set of values into smaller cubes. Let's assume I split my values into 8 files (2^3 files), I have to retrieve the correct coordinates for 8 small cubes. And I can't find a way to do that.
I'm pretty sure my data structures choice is terrible, could someone give me some help with that ?
EDIT :
Here is the function generating my four-star array :
float**** fill_array_random4d(int split, int size)
{
float**** ret;
ret = malloc(sizeof(float***) * split);
for (int i = 0; i < split; i++)
{
ret[i] = malloc(sizeof (float**) * size);
for (int j = 0; j < size; j++)
{
ret[i][j] = malloc(sizeof (float*) * size);
for (int k = 0; k < size; k++)
{
ret[i][j][k] = malloc(sizeof (float) * size);
for (int l = 0; l < size; l++)
ret[i][j][k][l] = rand() % 100;
}
}
}
return ret;
}
It's a pretty basic stuff. Right now I'm using random values.
Here is how I create and fill my x, y, z arrays :
float *x, *y, *z;
x = malloc(sizeof (float) * size);
y = malloc(sizeof (float) * size);
z = malloc(sizeof (float) * size);
for (int i = 0; i < size * split; i++)
x[i] = step * i;
for (int i = 0; i < size * split; i++)
y[i] = step * i;
for (int i = 0; i < size * split; i++)
z[i] = step * i;
It's still very basic, and finally here is the function printing the coordinates in the file, following the vtk legacy format :
void print_Coordinates(FILE *file, float *x, float *y, float *z, int size, int split)
{
fprintf(file, " <Coordinates>\n");
for (int i = 0; i < 3; i++)
{
const char *text1 = " <DataArray type=\"Float32\" Name=\"";
const char *text2 = "\" format=\"ascii\">\n";
fprintf(file, "%s%c%s", text1, 'x' + i, text2);
for (int j = 0; j < size; j++)
{
if (i == 0)
fprintf(file, " %f\n", x[j]);
else if (i == 1)
fprintf(file, " %f\n", y[j]);
else
fprintf(file, " %f\n", z[j]);
}
fprintf(file, " </DataArray>\n");
}
fprintf(file, " </Coordinates>\n");
}
So, yeah, it doesn't do what I want at all.
Here is a screenshot of the result :
All the cubes are on top of each other. With the code I was using earlier, I had several cubes (one per file), but they were aligned on a diagonal (which is not good either).
As you have admitted, there are some problems with your data structure:
The first dimension s seems incongruent: Should the data structure include the original and the smaller cube? That's not easy to do, because the smaller cubes have other dimensions.
You have many separate data: The (random) data, the coordinates and the array dimensions. In order to represent the cube, you need to keep track of all of these. I recommend to create a structure to keep the relevant data together.
There isn't anything per se wrong with your approach to represent the three-dimensional array with a triple pointer, but the design leads to many fragmented allocations. A multi-dimensional array with constant dimensions is probably better represented as one "flat" memory block.
I suggest two structures:
typedef struct Cube Cube;
typedef struct Axis Axis;
struct Axis {
int n; /* number of values */
float *data; /* graduation values */
};
struct Cube {
Axis *x, *y, *z; /* Axes of the cube */
float *data; /* x-major data */
};
An "axis" stores the values along one of the axes. The cube itself doesn't worry about the axis-related code and just delegates it to its three member axes. A "cube" is your data object. (In the implementation below, the data representation is x-major, meaning the x loop is the outermost, the z loop is the innermost. You can chnage that by swapping the loops.)
If you have a populated cube object, you can the extract sub-cubes by creating a cube of a smaller dimension and copying the relevant data ranges from the axes and from the cube data. If you want to cover the whole cube, you can either extract and write the cubes as you go or store them in an array of cubes, e.g. Cube *small[8] for splitting in half for each direction. (This would be like your original s index, only that each cube may have its own dimension.)
An implementation of this behaviour with an (addmittedly simple) test main is below:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Cube Cube;
typedef struct Axis Axis;
struct Axis {
int n; /* number of values */
float *data; /* graduation values */
};
struct Cube {
Axis *x, *y, *z; /* Axes of the cube */
float *data; /* x-major data */
};
/*
* Create a new axis with a constant step.
*/
Axis *axis_new(int n, float start, float step)
{
Axis *axis = malloc(sizeof(*axis));
float *p;
axis->n = n;
axis->data = malloc(n * sizeof(*axis->data));
p = axis->data;
while (n--) {
*p = start;
start += step;
p++;
}
return axis;
}
/*
* Destroy and clean up axis
*/
void axis_delete(Axis *axis)
{
if (axis) {
free(axis->data);
free(axis);
}
}
/*
* Write axis in XML format to given file
*/
void axis_write(const Axis *axis, FILE *f, const char *name)
{
float *p = axis->data;
int n = axis->n;
fprintf(f, " <DataArray type=\"Float32\" "
"Name=\"%s\" format=\"ascii\">\n", name);
fprintf(f, " ");
while (n--) {
fprintf(f, " %g", *p++);
}
fprintf(f, "\n");
fprintf(f, " </DataArray>\n");
}
/*
* Create a new axis that is a sub-axis of orig.
*/
Axis *axis_slice(const Axis *orig, int start, int len)
{
Axis *axis = axis_new(len, 0, 0);
memcpy(axis->data, orig->data + start, len * sizeof(*axis->data));
return axis;
}
/*
* Create a cube of zero values for the given axes
*/
Cube *cube_new(Axis *x, Axis *y, Axis *z)
{
Cube *cube = malloc(sizeof(*cube));
int dim = x->n * y->n * z->n;
cube->x = x;
cube->y = y;
cube->z = z;
cube->data = malloc(dim * sizeof(*cube->data));
return cube;
}
/*
* Destroy and clean up cube
*/
void cube_delete(Cube *cube)
{
if (cube) {
axis_delete(cube->x);
axis_delete(cube->y);
axis_delete(cube->z);
free(cube->data);
free(cube);
}
}
float *cube_at(const Cube *cube, int x, int y, int z)
{
int pos = (x * cube->y->n + y) * cube->z->n + z;
return cube->data + pos;
}
/*
* Populate all x, y, z values according to the function func.
*/
void cube_populate(Cube *cube, float (*func)(float x, float y, float z))
{
int i, j, k;
float *p = cube->data;
for (i = 0; i < cube->x->n; i++) {
float x = cube->x->data[i];
for (j = 0; j < cube->y->n; j++) {
float y = cube->y->data[j];
for (k = 0; k < cube->z->n; k++) {
float z = cube->z->data[k];
*p++ = func(x, y, z);
}
}
}
}
/*
* Write cube to given file.
*/
void cube_write(const Cube *cube, FILE *f)
{
float *p = cube->data;
int n = cube->x->n * cube->y->n * cube->z->n;
fprintf(f, "<PointData Scalars=\"U\">\n");
fprintf(f, " <DataArray type=\"Float32\" Name=\"U\" format=\"ascii\">\n");
while (n--) {
fprintf(f, " %g", *p++);
}
fprintf(f, "\n");
fprintf(f, " </DataArray>\n");
fprintf(f, "</PointData>\n");
fprintf(f, "<Coordinates>\n");
axis_write(cube->x, f, "x");
axis_write(cube->y, f, "y");
axis_write(cube->z, f, "z");
fprintf(f, "</Coordinates>\n");
}
/*
* Create a new cube that is a sub-cube of orig.
*/
Cube *cube_slice(const Cube *orig,
int x, int dx, int y, int dy, int z, int dz)
{
Cube *cube;
float *p;
int i, j, k;
if (x + dx > orig->x->n) return NULL;
if (y + dy > orig->y->n) return NULL;
if (z + dz > orig->z->n) return NULL;
cube = cube_new(
axis_slice(orig->x, x, dx),
axis_slice(orig->y, y, dy),
axis_slice(orig->z, z, dz));
p = cube->data;
for (i = 0; i < dx; i++) {
for (j = 0; j < dy; j++) {
for (k = 0; k < dz; k++) {
*p++ = *cube_at(orig, x + i, y + j, z + k);
}
}
}
return cube;
}
/*
* Example appliaction
*/
float dist2(float x, float y, float z)
{
return x*x + y*y + z*z;
}
int main()
{
Cube *cube = cube_new(
axis_new(4, 0, 0.1),
axis_new(4, 0, 0.1),
axis_new(4, 0, 0.1));
int i, j, k;
cube_populate(cube, dist2);
for (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) {
for (k = 0; k < 2; k++) {
Cube *sub = cube_slice(cube, 2*i, 2, 2*j, 2, 2*k, 2);
cube_write(sub, stdout);
printf("--\n");
cube_delete(sub);
}
}
}
cube_delete(cube);
return 0;
}
I am trying get a mandelbrot image clearly with the sequential programming in C++, but I am getting a segmentation fault during runtime. I have no idea about the seg. fault, but my program is perfectly compiling with no errors.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int file_write(unsigned int width, unsigned int height)
{
unsigned int **color = NULL;
FILE *fractal = fopen("mandelbrot_imageSequential.ppm","w+");
if(fractal != NULL)
{
fprintf(fractal,"P6\n");
fprintf(fractal,"# %s\n", "Mandelbrot_imageSequential.ppm");
fprintf(fractal,"%d %d\n", height, width);
fprintf(fractal,"40\n");
int x = 0, y = 0;
unsigned int R = 0, G = 0, B = 0;
for(x = 0; x < width; ++x)
{
for(y = 0; y < height; ++y)
{
R = (color[y][x]*10);
G = 255-((color[y][x]*10));
B = ((color[y][x]*10)-150);
if(R == 10)
R = 11;
if(G == 10)
G = 11;
if(B == 10)
B = 11;
putc(R, fractal);
putc(G, fractal);
putc(B, fractal);
}
}
fclose(fractal);
}
return 0;
}
int method(int x, int y, int height, int width, double min_re, double max_re, double min_im, double max_im, int max_iterations)
{
double threshold = 4;
double x_factor = (max_re-min_re)/(width-1);
double y_factor = (max_im-min_im)/(height-1);
double c_im = max_im - y*y_factor;
double c_re = min_re + x*x_factor;
double Z_re = c_re, Z_im = c_im;
unsigned int col = 0;
for(unsigned n = 0; n < max_iterations; ++n)
{
double Z_re2 = Z_re*Z_re, Z_im2 = Z_im*Z_im;
if(Z_re2 + Z_im2 > threshold)
{
col = n;
break;
}
Z_im = 2 * Z_re * Z_im + c_im;
Z_re = Z_re2 - Z_im2 + c_re;
}
return col;
}
int main(int argc, char *argv[])
{
unsigned int width;
unsigned int height;
unsigned int max_iterations;
unsigned int **color = NULL;
int x,y;
double threshold;
double min_re;
double max_re;
double min_im;
double max_im;
unsigned int NUM_OF_THREADS;
if(argc != 10)
{
printf("There is an error in the input given.\n");
return 0;
}
else
{
height = atoi(argv[1]);
width = atoi(argv[2]);
max_iterations = atoi(argv[3]);
min_re = atof(argv[4]);
max_re = atof(argv[5]);
min_im = atof(argv[6]);
max_im = atof(argv[7]);
threshold = atoi(argv[8]);
NUM_OF_THREADS = atoi(argv[9]);
}
color = (unsigned int**)malloc(height*sizeof(unsigned int*));
printf("height = %d\twidth = %d\tmaximum_iterations = %d\tminimum_x-value = %.2f\tmaximum_x-value = %.2f\tminimum_y-value = %.2f\tmaximum_y-value = %.2f\tthreshold_value = %.2f\tno. of threads = %d\t\n",height,width,max_iterations,min_re,max_re,min_im,max_im,threshold,NUM_OF_THREADS);
for(x = 0; x < height; x++)
{
color[x] = (unsigned int*)malloc(width*sizeof(unsigned int));
}
time_t ts,te;
time(&ts);
method(x,y,height,width,min_re,max_re,min_im,max_im,max_iterations);
time(&te);
double diff = difftime(te,ts);
file_write(width, height);
printf("Total Time elapsed: %f\n",diff);
return 0;
}
How to correct this segmentation fault?
At least one problem is in the file_write function.
unsigned int **color = NULL;
R = (color[y][x]*10);
I assume the color should be an input parameter.
If you are on Linux machine do the following :
$ulimit -c unlimited
Then run the code. Notice a core.[pid] file is generated. fire up gdb like following
$gdb ./your_app core.[pid]
It will take you the statement where segfault occurred. issue a "backtrace" command in gdb prompt to see the call hierarchy.
Remember compiling with "-g" flag to get more verbose gdb output.
There are two major problems with your code:
You allocate memory for the color array but then use a different color inside file_write() which is initialized to NULL.
You need to pass the first color as an argument to file_write():
int main(...)
{
...
file_write(color, width, height);
printf("Total Time elapsed: %f\n",diff);
return 0;
}
And declare the other color as an argument to file_write():
int file_write(unsigned int **color, unsigned int width, unsigned int height)
{
/* unsigned int **color = NULL; // Removed */
...
You're only calling method() once and not storing anything into color. You need to call it in a loop. Something similar to:
/* Untested */
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
color[y][x] = method(x,y,height,width,min_re,max_re,min_im,max_im,max_iterations);
}
}
Then, of course, you should check the return values of malloc(), fopen(), fprintf(), fclose(), ... , and check that the input variables have reasonable values and so on.
I also noticed that you're passing width and height in different order to file_write() and method(). To avoid future headaches, I would change the method() function to method(x, y, width, height) so that the horizontal and vertical arguments are passed in the same order.