4D Arrays?

Started by
14 comments, last by Stroppy Katamari 11 years, 4 months ago
Yes, thats why you "forget" to mention the 2D projection, but understanding projections and realizing this image is really a double projection, at least gives me a better "feeling" of what the 4D object is, even though any "visualisation" has to be in 3D at most.

For a 2D projection of a 3D projection to feel 3D, you either need shading, animation, or an easily recognizable object. All of these are extra hints of the third dimension. Just a straight static 2D projection of a 3D object gives little hints of the objects 3Dness, and makes it seem flat.

In the same way, shading, and animations, can give you more hints of the objects 4Dness in the 3D projection, and give you a better understanding of it, even if you still can't visualize it in your head.
Advertisement
I found myself using 4D arrays before. What I did was pass a 'stride' vector around. So say u have a 40x30x20x10 4D array, the 4D stride vector would be: { 1, 40, 40*30, 40*30*20 }. Sometimes you want to pad to align rows or what-have-you and that can easily be adjusted for in the stride. You can even use the stride to convert from indices to byte offsets, if you're working with raw data. Then to convert a 4D index and a stride to a 1D offset you simple do the dot product, something like dot(index,stride). Obviously this could easily be extrapolated to N dimensions, under the hood there's nothing special or tricky going on, but by pairing a stride vector with all the arrays it makes it very easy to work with.
Well here it is. My 4D array resize algorithm:

[source lang="cpp"]#pragma once

#include <iostream>
using namespace std;

template <class Datatype>

class Array4D
{

public:

//Constructor
Array4D(int p_time, int p_depth, int p_height, int p_width)
{
m_array = new Datatype[ p_time * p_depth * p_height * p_width];
m_time = p_time;
m_depth = p_depth;
m_height = p_height;
m_width = p_width;
}

//Destructor
~Array4D()
{
if(m_array != 0)
{
delete[] m_array;
}

m_array = 0;
}

//Get: Retrieves the value from passed in location
Datatype& Get(int p_t, int p_z, int p_y, int p_x)
{
return m_array[ ( (p_t * m_depth * m_height * m_width) + (p_z * m_height * m_width) + (p_y * m_width) + (p_x) ) ];
}


//Width: Returns the width of the array
int Width()
{
return m_width;
}

//Height: Returns the height of the array
int Height()
{
return m_height;
}

//Depth: Returns the depth of the array
int Depth()
{
return m_depth;
}

//Time: Returns the time of the array
int Time()
{
return m_time;

}

//Resize: Resizes the array
void Resize(int p_time, int p_depth, int p_height, int p_width)
{
//Declare a pointer to a new array and allocate enouh memory
Datatype *newarray = new [p_time * p_depth * p_height * p_width];

//Determine the minimum of all four dimensions
int minx = (p_width < m_width ? p_width : m_width);
int miny = (p_height < m_height ? p_height : m_height);
int minz = (p_depth < m_depth ? p_depth : m_depth);
int mint = (p_time < m_time ? p_time : m_time);

//Declare four dimensional coordinates
int x;
int y;
int x;
int t;

//Declare temporary variables
int t1;
int t2;
int t3;
int t4;
int t5;
int t6;

//Now loop through each cell and paste the values from the old array into the new array
for(t = 0; t < mint; t++)
{
t1 = t * p_depth * m_height * m_width
t2 = t * m_depth * m_height * m_width

for(z = 0; z < minz; z++)
{
t3 = z * p_width * m_height;
t4 = z * m_width * m_height;

for(y = 0; y < miny; y++)
{
t5 = y * p_width;
t6 = y * m_width;

for(x = 0; x < minx ; x++)
{
newarray[t1 + t3 + t5 + x] = m_array[t2 + t4 + t6 + x];
}
}
}
}

//Deallocate the old array
if(m_array != 0)
{
delete[] m_array;
}
m_array = newarray;

//set the new dimesions
m_width = p_width;
m_height = p_height;
m_depth = p_depth;
m_time = p_time;
}


//Size: Returns the total size of the array
int Size()
{
return m_time * m_depth * m_height * m_width;
}

private:

//Private variables
Datatype *m_array;
int m_width;
int m_height;
int m_depth;
int m_time;
};



[/source]

and heres an example implementation file to test it:

[source lang="cpp"]#include <iostream>
#include "Array4D.h"
using namespace std;



int main()
{
//Create two different 4-Dimensional arrays
Array4D<int> iarray(5, 5, 5, 5);
Array4D<float> farray(7, 7, 7, 7);

//Create some variables to show output
int i;
int f;

//Insert 10 into an array
iarray.Get(2, 1, 3, 0) = 10;
i = iarray.Get(2, 1, 3, 0);
cout << "The value at time 2, depth 1, height 3, and width 0 is: " << i << endl;

farray.Get(2, 1, 3, 0) = 25.0f;
f = farray.Get(2, 1, 3, 0);
cout << "The value at time 2, depth 1, height 3, and width 0 is: " << f << endl;

//Display the size of the arrays
cout << "The size of iarray is: " << iarray.Size() << endl;
cout << "The size of farray is: " << farray.Size() << endl;


cin.get();
return 0;
}[/source]

Why aren't you just using

int array[3][4][5][6];
//or
std::vector< std::vector< std::vector< std::vector< /*your Type*/ > > > > array;


Using a naked C++ array is usually bad for this and even if you have to if you use only one array you end up with a uniform grid like array only, jagged arrays wouldn't be possible you would need to use a "type****" for that and the just looks ugly.

Your first example and second example aren't equivalent.

While I agree with using an std::vector, I disagree with stacking them four deep.
A vector of a vector of a vector of a vector is not the same as a 4D array, in the same way that a vector of a vector is not the same as a 2D array, since each vector within the first vector aren't constrained to the same size (a so called 'jagged array' is an array of an array, not a 2D array).
If you have access to C++11, a std::array would constrain it to the same size.

When I want a multi-dimensional array, I use a single std::vector, and resize it to (width * height) or (width * height * depth), and index into it as Olof Hedman shows.
@ISDCaptain01: Nicely done! Your code looks very cleanly written too, which I approve of. biggrin.png

A few notes:

  • Typically, people put the dimensions in the order (x,y,z,t) (width, height, depth, time). You seem to have them backward - which is fine, but unusual.
  • You also declare alot of your temporary variables before their use in your Resize() function. This, while not quite punishable by death, is often considered bad practice. wink.png Variables shouldn't be declared until the last moment they are needed, unless you have a reason for doing otherwise. And if you do declare them in advance, they should at least be initialized. Where you declare temporary x,y,z,t in your Resize() function you accidentally declare 'x' twice instead of 'z'.
  • Your 'Get()' function doesn't check to make sure the parameters are within range - that may be intentional though. The standard library typically provides two accessing methods - the subscript operator [], which doesn't check for range (used for extra speed when you are iterating and are confident you don't go over), and a 'at()' function that does check range.

When I want a multi-dimensional array, I use a single std::vector, and resize it to (width * height) or (width * height * depth), and index into it as Olof Hedman shows.
Yep. Also, when you do that, a small indexing function can be very helpful. Easy to get variations of "x + y*width + "z*height*width + w*height*width*depth" wrong in individual accesses, much clearer when you have "array[idx(0, 0, arg, time)]" and so on.

This topic is closed to new replies.

Advertisement