If I have an array that looks like this:
int map[21][28] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
How can I create a smaller array consisting of the values inside that array..?
A bit like this:
int zoomedMap[7][7] =
{
2, 2, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1
};
I don't know if this helps, but:
I know the exact dimensions of both arrays
I want it to be possible to move the smaller arrays position, preferably by just changing an X/Y value
In what is hopefully obvious, your sub-array is literally nothing more than a "block" of data (with some interesting partitioning) within your main array. Assuming you are checking your boundaries and will NOT allow an out of bounds condition to happen, you can simulate your sub-array using pointer math and some basic info about your main array.
The block will always be some [row][column] offset from the [0][0] location, so we need those values (row and column).
The block has fixed width such that column+width does not exceed your main array width. We need that width.
The block has fixed height such that row+height does not exceed your main array height. We need that height.
I hope it obvious we need the base address that is the main array (in your case map).
This is probably best demonstrated by example. The following is NOT some end-all solution. It will likely not even address 1/10th of the need you're going to have. Rather, it is intended to offer you an idea on how you can do this using only a pointer, some offsets, some sizes, and a little arithmetic to get what you're looking for. There is nothing stopping you from exceeding limits that may be harmful (just like a plain array), so be cautious.
// internal rerefential to a submatrix in a larger fixed matrix.
template<typename T>
class Sub2D
{
public:
template<size_t R, size_t C>
Sub2D(T(&ar)[R][C], int top, int left, int height, int width)
: parent(ar[0])
, row(top)
, col(left)
, max_row(R)
, max_col(C)
{
if ((row+width) >= R || (col+height) >= C)
throw std::out_of_range("");
}
// retrieve our subrow offset into the main 2D array
T* operator [](size_t n)
{
// enable at your desire, but as Alex pointed out, all
// the standard containers let you shoot yourself in the
// foot with this operator. why not this one too =P
//if (row+n >= max_row)
// throw std::out_of_range("");
return parent + ((row+n)*max_col + col);
}
private:
T* parent;
size_t row, col;
size_t max_row, max_col;
};
Used like this, assuming your array in your question is the one we're basing:
int main()
{
// take the submatrix & [6][7] that is 7x7 in dimension.
Sub2D<int> sub(map, 6,7, 7,7);
for (size_t i=0;i<7;++i)
{
for (size_t j=0;j<7;++j)
cout << sub[i][j] << ' ';
cout << endl;
}
cout << endl;
// update an element at location [1][1] of our sub-matrix.
sub[1][1] = 9;
// reprint the *entire* main array. it better have updated.
for (size_t i=0;i<sizeof(map)/sizeof(map[0]);++i)
{
for (size_t j=0;j<sizeof(map[0])/sizeof(map[0][0]);++j)
cout << map[i][j] << ' ';
cout << endl;
}
cout << endl;
return 0;
}
Produces the following output.
2 2 1 1 1 1 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 2 2 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 9 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Note the 9 now sitting right where we expect it.
There is obviously a LOT more you can add to such a template class, including better range checking, moving your pointer, snap-shotting into other memory buffers, etc, but the point is for basic get-me-this sub matrix a pointer and a few offsets are hard to beat, especially for performance.
You could always do it statically with loops:
const int pos_x = 6, pos_y = 7, size_x = 7, size_y = 7;
int newMap[size_x][size_y] = {0};
for( int i = pos_x; i != pos_x+size_x; ++i ) {
for( int j = pos_y; j != pos_y+size_y; ++j) {
newMap[i-pos_x][j-pos_y] = map[i][j];
}
}
Although this isn't very safe, and difficult to get working dynamically.
You might want to either write a matrix-wrapper class with the functions you desire; or find one that already exists and extend it (I'd start looking into the STL or Boost for an existing matrix class)
Have a read of Boost's uBLAS module. It provides Matrix and MatrixRange classes, which are exactly what you are looking for.
In essence, the zoomedMap should just be an intelligent pointer into the larger matrix, which understands how to dereference and get the correct result.
Depending on your requirements, this may suffice:
int (*zoomedMap)[28] = reinterpret_cast<int (*)[28]>(&map[X][Y]);
This gives you an identifier you can use as if it were a two-dimensional array: zoomedMap[i][j]. You can easily move the zoomed map within the larger array by assigning a new address to it.
It depends on your C++ implementation allowing a pointer to float to be cast to a pointer to an array of float, provided all references remain within the original array. This is common.
It leaves zoomedMap pointing to the same memory as map. Thus, you cannot change one without changing the other. If you want to do that, you would need to make a copy.
Related
I have a .cpp file that must include Console.h. In the file I'm trying to create a map (used later on for a game).
Error C2086: 'int nMapArray[15][20]: redefinition
#include "Console.h"
#include <Windows.h>
#include <stdint.h>
// Map dimensions
#define MAP_WIDTH 20
#define MAP_HEIGHT 15
// Tile Types
#define TILE_FLOOR 0
#define TILE_WALL 1
// Map declaration
int nMapArray[ MAP_HEIGHT ][ MAP_WIDTH ];
// Map Layout
int nMapArray[ MAP_HEIGHT ][ MAP_WIDTH ]=
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
I know that I'm only supposed to declare nMapArray once but I'm not sure which one to discard. If I discard int nMapArray[ MAP_HEIGHT ][ MAP_WIDTH ]; then it will generate two errors:
LNK2019: unresolved external symbol
and
LNK1120: unresolved externals
Did a bit of googling but I still can't find the fix so help would be appreciated.
EDIT:
Ok so following the advice of many to get rid of the first inisialisation. From here I get two errors:
error LNK2019: unresolved external symbol "public: virtual class IConsole & __thiscall Win32Console::Color(unsigned short)" (?Color#Win32Console##UAEAAVIConsole##G#Z) referenced in function "void __cdecl DrawTile(int,int)" (?DrawTile##YAXHH#Z)
and
error LNK1120: 1 unresolved externals
Full code:
#include "Console.h"
#include <Windows.h>
#include <stdint.h>
// Map dimensions
#define MAP_WIDTH 20
#define MAP_HEIGHT 15
// Tile Types
#define TILE_FLOOR 0
#define TILE_WALL 1
// Map Layout
int nMapArray[ MAP_HEIGHT ][ MAP_WIDTH ]=
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
void DrawMap( void );
bool IsPassable( int x, int y );
void DrawTile( int x, int y );
int main( void )
{
console.SetTitle( "Article Two Demo" );
// Declare the player's position
int nPlayerX = 4;
int nPlayerY = 4;
// Main game loop
while( true )
{
// Draw the map
DrawMap();
// Draw the player to the screen
console.Color( RED );
console.Position( nPlayerX, nPlayerY );
console << '#';
// Input phase - Wait for the player to do something
KEYPRESS sKeyPress = console.WaitForKeypress();
// Process the input
switch( sKeyPress.eCode )
{
// Move up
case CONSOLE_KEY_UP:
// Can we move to the tile above?
if( IsPassable(nPlayerX, nPlayerY-1) )
{
// Move up
nPlayerY--;
}
break;
// Move left
case CONSOLE_KEY_LEFT:
// Can we move to the tile to the left of the player?
if( IsPassable(nPlayerX-1, nPlayerY) )
{
// Move left
nPlayerX--;
}
break;
// Move right
case CONSOLE_KEY_RIGHT:
// Can we move to the tile to the right of the player
if( IsPassable(nPlayerX+1, nPlayerY ) )
{
// Move right
nPlayerX++;
}
break;
// Move down
case CONSOLE_KEY_DOWN:
// Can we move to the tile below the player?
if( IsPassable(nPlayerX, nPlayerY+1) )
{
// Move down
nPlayerY++;
}
break;
// Escape key
case CONSOLE_KEY_ESCAPE:
// Quit the program
return 0;
// Ignore any other keys
default:
break;
}
}
// If execution gets here, the program is done
return 0;
}
// IsPassable Function ///////////////////////////////////////////////////////////////////
//
// This function analyzes the coordinates of the map array specified and returns
// true if the coordinate is passable (able for the player to occupy), false if not.
//
bool IsPassable( int x, int y )
{
// Before we do anything, make darn sure that the coordinates are valid
if( x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT )
return false;
// Store the value of the tile specified
int nTileValue = nMapArray[y][x];
// Return true if it's passable
if( nTileValue == TILE_FLOOR)
return true;
return false;
}
// DrawMap Function //////////////////////////////////////////////////////////////////////
//
// This function draws the entire map to the screen.
//
void DrawMap( void )
{
for( int y = 0; y < MAP_HEIGHT; y++ )
{
for( int x = 0; x < MAP_WIDTH; x++ )
{
DrawTile(x, y);
}
}
}
// DrawTile Function /////////////////////////////////////////////////////////////////////
//
// Draws a map tile for the map coordinates specified.
//
void DrawTile( int x, int y )
{
console.Position( x, y );
switch( nMapArray[y][x] )
{
case TILE_FLOOR:
console.Color( GREY );
console << '.';
break;
case TILE_WALL:
console.Color( GREY );
console << '#';
break;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
In Console.h I haven't put the layout in (because I'm not entirely sure how to do that).
Get rid of the first one, the one without the initialisation.
That solves the compiler issue, the linker errors are another matter. The reason they appear is simply because the compilation phase is working once the double declaration is fixed.
Then I suggest you post another question with more details on the linker problems.
I'm currently writing an ASCII game engine for the Windows console, and one of the classes I'm working with is an Image class, whose data members include width, height, and pointers to dynamically allocated arrays of unsigned chars that represent character and colour data for the image.
Originally I was constructing all of the images with data from structs defined elsewhere in the program, but that proved quite inefficient for anything past my first couple practice images so I tried to create a constructor that would read the required data from a text file instead. The format of the text file is:
width
height
[chars]
[colours]
where chars and colours are just arrays separated by whitespace of width * height size (full image file pasted below if you're interested). However, when I call this constructor, something goes wrong - in debug mode in Eclipse, nothing looks strange at all and it in fact looks like it's copying over the character and colour arrays just fine, but executing the program doesn't draw the image to the screen when it's asked to.
The original version of the constructor looks like this:
Image::Image(const int w, const int h, const CHAR *chrs, const COL *cols) :width(w), height(h) {
chars = new CHAR[w * h];
colours = new COL[w * h];
for (int i = 0; i < w * h; ++i) {
chars[i] = chrs[i];
colours[i] = cols[i];
}
}
and was called like so:
Image *sun_image = new Image(Sun.width, Sun.height, Sun.chars, Sun.colours);
with the Sun struct defined like this:
Sun_s Sun =
{
SUNW,
SUNH,
{
255,255,255,255,255,255,255,177,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,177,255,255,255,177,255,255,255,177,255,255,255,
255,255,255,255,177,255,255,255,255,255,177,255,255,255,255,
255,255,255,255,255,255,177,177,177,255,255,255,255,255,255,
255,255,255,255,255,177,178,219,178,177,255,255,255,255,255,
255,255,255,255,177,178,219,219,219,178,177,255,255,255,255,
177,255,177,255,177,219, 94,219, 94,219,177,255,177,255,177,
255,255,255,255,177,178,219,126,219,178,177,255,255,255,255,
255,255,255,255,255,177,178,219,178,177,255,255,255,255,255,
255,255,255,255,255,255,177,177,177,255,255,255,255,255,255,
255,255,255,255,177,255,255,255,255,255,177,255,255,255,255,
255,255,255,177,255,255,255,177,255,255,255,177,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,177,255,255,255,255,255,255,255,
},
{
0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 62, 0, 0, 0, 62, 0, 0, 0, 62, 0, 0, 0,
0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 62, 62, 62, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 62, 62, 14, 62, 62, 0, 0, 0, 0, 0,
0, 0, 0, 0, 62, 62, 14, 14, 14, 62, 62, 0, 0, 0, 0,
62, 0, 62, 0, 62, 14,224, 14,224, 14, 62, 0, 62, 0, 62,
0, 0, 0, 0, 62, 62, 14,224, 14, 62, 62, 0, 0, 0, 0,
0, 0, 0, 0, 0, 62, 62, 14, 62, 62, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 62, 62, 62, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0,
0, 0, 0, 62, 0, 0, 0, 62, 0, 0, 0, 62, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0,
}
};
The attempted new version of the constructor looks like this:
Image::Image(const char *filename) {
std::ifstream inputFile;
inputFile.open(filename, std::ios::in);
if (inputFile.is_open()) {
inputFile >> width;
inputFile >> height;
chars = new CHAR[width * height];
colours = new CHAR[width * height];
int temp, i;
for (i = 0; i < height * width; ++i) {
inputFile >> temp;
chars[i] = (CHAR)temp;
}
for (i = 0; i < width * height; ++i) {
inputFile >> temp;
colours[i] = (CHAR)temp;
}
} else {
width = 0;
height = 0;
chars = new CHAR[width * height];
colours = new CHAR[width * height];
}
inputFile.close();
}
with a call that looks like:
Image *sun_image = new Image("./artassets/sun.txt");
and a sun.txt that looks like:
15
15
255 255 255 255 255 255 255 177 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 177 255 255 255 177 255 255 255 177 255 255 255
255 255 255 255 177 255 255 255 255 255 177 255 255 255 255
255 255 255 255 255 255 177 177 177 255 255 255 255 255 255
255 255 255 255 255 177 178 219 178 177 255 255 255 255 255
255 255 255 255 177 178 219 219 219 178 177 255 255 255 255
177 255 177 255 177 219 94 219 94 219 177 255 177 255 177
255 255 255 255 177 178 219 126 219 178 177 255 255 255 255
255 255 255 255 255 177 178 219 178 177 255 255 255 255 255
255 255 255 255 255 255 177 177 177 255 255 255 255 255 255
255 255 255 255 177 255 255 255 255 255 177 255 255 255 255
255 255 255 177 255 255 255 177 255 255 255 177 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 177 255 255 255 255 255 255 255
0 0 0 0 0 0 0 62 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 62 0 0 0 62 0 0 0 62 0 0 0
0 0 0 0 62 0 0 0 0 0 62 0 0 0 0
0 0 0 0 0 0 62 62 62 0 0 0 0 0 0
0 0 0 0 0 62 62 14 62 62 0 0 0 0 0
0 0 0 0 62 62 14 14 14 62 62 0 0 0 0
62 0 62 0 62 14 224 14 224 14 62 0 62 0 62
0 0 0 0 62 62 14 224 14 62 62 0 0 0 0
0 0 0 0 0 62 62 14 62 62 0 0 0 0 0
0 0 0 0 0 0 62 62 62 0 0 0 0 0 0
0 0 0 0 62 0 0 0 0 0 62 0 0 0 0
0 0 0 62 0 0 0 62 0 0 0 62 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 62 0 0 0 0 0 0 0
Can anyone point out what I'm doing wrong? My two constructors don't look all that different to me...
EDIT: Forgot to mention, CHAR and COL are both just defines for unsigned char.
I use the following function to return a weight matrix of values between 0 and 1 depending on how close to the center a particular matrix position is. Also after a threshold the values are all 1 (it is like plateau with the points closer to center having value 1 and points away from center after a threshold of distance linearly fall from 1 to 0 at the edges)?
cv::Mat2f getWeightsMatrix(int N, int M, float r){
cv::Mat2f weights = cv::Mat2f(N,M);
int i,j;
for(i=0;i<N;i++){
for(j=0;j<M;j++){
if(i<=floor(N*(1-r)/2)){
if(j<=floor(M*(1-r)/2)){
weights[i][j]=((float)(i/N-j/M)/(1-r));
}
else{
weights[i][j]=(2*(float)(i/N)/(1-r));
}
}
else if (i>=floor(N*(1+r)/2)){
if(j>=floor(M*(1+r)/2)){
weights[i][j]=(((float)((N-i)/N))-((float)((M-j)/M)))/(1-r);
}
else{
weights[i][j]=(2*(float)((N-i)/N)/(1-r));
}
}
else{
if(j<=floor(M*(1-r)/2)){
weights[i][j]=(2*(float)(j/M)/(1-r));
}
else if(j>=floor(M*(1+r)/2)){
weights[i][j]=(2*(float)((M-j)/M)/(1-r));
}
else{
weights[i][j]=1;
}
}
}
}
cout << weights << endl;
return weights;
}
Now my problem is that I am having some casting issues and only values 0 and 1 are being returned (no floats). Also my matrix size displayed by the cout is 20x10 when I call the function with N=10, M=10 and r=0.5.
Please helP!
EDIT: This is the output
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Your matrix consists only if integers because when you do basic operations on integers, the results gets rounded automatically.
For example: when you write
weights[i][j]=((float)(i/N-j/M)/(1-r));
The result of i/N is rounded to a integer, j/M is also rounded to an integer, and finally, the division by (1-r) is also rounded. Your (float) cast is a good idea but it's applied too late.
You can do several things:
Cast inside elementary operations, for instance, float(i)/N instead of i/N
Use float numbers instead of integers: write 1.0 instead of 1
Use floats inside your loops and statements
For example:
for(float i = 0; i < N-0.5; i++) {
for(float j = 0; j < M-0.5; j++) {
if(i <= floor(N*(1.0-r)/2.0)) {
// ...
It's important you understand that because of floating point precision, a test such as i < N might or might not pass when float i = N. This is why I did a little trick by substracting 0.5 from your loop bounds N and M.
Since you already define the thread groups to be executed when dispaching a compute shader, what do the numbers inside the shader file signify? example: [numthreads(1, 1, 1)]
Did some more digging, by official definition when dispatching you define thread groups while the numthreads statement defines the threads so basically they can be taken as extra dimensions. For example when i dispatch 2,2,1 to a function with 3,1,1 it spawns
enter code here
0, 0, 0 - 0, 0, 0
0, 0, 0 - 1, 0, 0
0, 0, 0 - 2, 0, 0
1, 0, 0 - 0, 0, 0
1, 0, 0 - 1, 0, 0
1, 0, 0 - 2, 0, 0
0, 1, 0 - 0, 0, 0
0, 1, 0 - 1, 0, 0
0, 1, 0 - 2, 0, 0
1, 1, 0 - 0, 0, 0
1, 1, 0 - 1, 0, 0
1, 1, 0 - 2, 0, 0
so 2*2*1*3*1*1 = 12 threads total
I have a bunch of blocks that needs to be drawn into a grid. Now displaying them unscaled everything is fine but when I try to scale them down to fit withing a window I get "scale-artifacts" because I use a normal scale-ratio formula and floats.
Is there a way to avoid these problems ?
Common example data:
Original length: 200000
Scaled down to a 25x25 pixel grid (it's this small for development and debugging)
The scaled down max length: 625 (25 * 25)
Scale-ratio: (625 / 200000) = 0,003125
Example data 1 - overlapping, scaled blocks overwrite each other
Start of block => end of block: [start, end)
1: 2100 => 2800
2: 2800 => 3600
3: 3600 => 4500
4: 4500 => 5500
Jumping over showing the output of this example because I think example 2 and 3 will get the point across. Left it in for completeness.
Example data 2 - incorrect space between 2 and 3
Start of block => end of block: [start, end)
1: 960 => 1440
2: 1440 => 1920
3: 1920 => 2400
1: 960 => 1440, length: 480, scaled length: 1.5:
2: 1440 => 1920, length: 480, scaled length: 1.5:
3: 1920 => 2400, length: 480, scaled length: 1.5:
pixel start, end, length
1: 3, 0, 1
2: 4, 0, 1
3: 6, 0, 1
Displayed grid:
[ 0, 0, 0, 1, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
...
Example data 3 - 1 moved a step back incorrectly
Start of block => end of block: [start, end)
1: 896 => 1344
2: 1344 => 1792
3: 1792 => 2240
1: 896 => 1344, length: 448, scaled length: 1.4:
2: 1344 => 1792, length: 448, scaled length: 1.4:
3: 1792 => 2240, length: 448, scaled length: 1.4:
pixel start, end, length
1: 2, 0, 1
2: 4, 0, 1
3: 5, 0, 1
Displayed grid:
[ 0, 0, 1, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
...
What example data 2 and 3 should have looked like:
[ 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
...
Remember the block values are [start, end)
Preemptive strike (down-voters / trollers) Remember: I'm not psychic or a mind-reader. If you want to give negative do it in a constrictive way or it is useless (i will not learn anything) and will just pollute the thread.
Update
#include <iostream>
#include <math.h>
#include <limits.h>
#include <assert.h>
#include <vector>
#include <array>
#include <utility> // pair
#include <algorithm> // for_each
using namespace std;
const size_t width_size = 25; // 25 pixels
const size_t height_size = 25; // 25 pixels
const size_t grid_length = width_size * height_size; // width * height
array<size_t, grid_length> grid;
const size_t original_length = 200000;
typedef pair<unsigned long, unsigned long> block;
vector<block> test_values;
void show_grid()
{
for (size_t y = 0; y < height_size; ++y) {
const size_t start_pos_for_current_heigth = y * width_size;
const size_t end_pos_for_current_heigth = start_pos_for_current_heigth + width_size;
cout << "[ ";
for (size_t i = start_pos_for_current_heigth; i < end_pos_for_current_heigth; ++i) {
if (i + 1 < end_pos_for_current_heigth)
cout << grid[i] << ", ";
else
cout << grid[i];
};
cout << " ]" << endl;
}
}
void scale_and_add(const float scale)
{
size_t test_value_id = 1;
for_each(test_values.cbegin(), test_values.cend(), [&](const block &p) {
const float s_f = p.first * scale;
const unsigned long s = round(s_f);
const float e_f = p.second * scale;
const unsigned long e = round(e_f);
const unsigned long block_length = p.second - p.first;
const float block_length_scaled = block_length * scale;
assert(s <= grid_length);
assert(e <= grid_length);
cout << test_value_id << ":" << endl;
cout << " " << p.first << " => " << p.second << " length: " << block_length << endl;
cout << " " << s << " (" << s_f << ") => " << e << " (" << e_f << ") length: " << (e - s) << " (" << block_length_scaled << ")" << " (scaled)" << endl;
for (size_t i = s; i < e; ++i) {
if (grid[i] != 0) {
cout << "overlapp detected !" << endl;
}
grid[i] = test_value_id;
}
++test_value_id;
});
}
void reset_common()
{
grid.fill(0);
test_values.clear();
}
int main()
{
const float scale = ((float)grid_length / (float)original_length);
cout << "scale: " << scale << " length per pixel: " << ((float)original_length / (float)grid_length) << endl;
// Example data 1
/* cout << "Example data 1" << endl;
test_values.push_back(make_pair(2100, 2800));
test_values.push_back(make_pair(2800, 3600));
test_values.push_back(make_pair(3600, 4500));
test_values.push_back(make_pair(4500, 5500));
scale_and_add(scale);
show_grid();
reset_common();
// Example data 2
cout << "Example data 2" << endl;
test_values.push_back(make_pair(960, 1440));
test_values.push_back(make_pair(1440, 1920));
test_values.push_back(make_pair(1920, 2400));
scale_and_add(scale);
show_grid();
reset_common();
// Example data 3
cout << endl << "Example data 3" << endl;
test_values.push_back(make_pair(896, 1344));
test_values.push_back(make_pair(1344, 1792));
test_values.push_back(make_pair(1792, 2240));
scale_and_add(scale);
show_grid();
reset_common();*/
// Generated data - to quickly find the problem
cout << "Generated data" << endl;
auto to_op = [&](const size_t v) {
return v * (original_length / grid_length) * 1.3; // 1.4 and 1.5 are also good values to show the problem
};
size_t pos = 0;
size_t psize = 1; // Note this value (length) and check it with the displayed one, you'll be surprised !
for (size_t g = 0; g < 10; ++g) {
test_values.push_back(make_pair(to_op(pos), to_op(pos + psize)));
pos += psize;
}
scale_and_add(scale);
show_grid();
return 0;
}
Output:
scale: 0.003125 length per pixel: 320
Generated data
1:
0 => 416 length: 416
0 (0) => 1 (1.3) length: 1 (1.3) (scaled)
2:
416 => 832 length: 416
1 (1.3) => 3 (2.6) length: 2 (1.3) (scaled)
3:
832 => 1248 length: 416
3 (2.6) => 4 (3.9) length: 1 (1.3) (scaled)
4:
1248 => 1664 length: 416
4 (3.9) => 5 (5.2) length: 1 (1.3) (scaled)
5:
1664 => 2080 length: 416
5 (5.2) => 7 (6.5) length: 2 (1.3) (scaled)
6:
2080 => 2496 length: 416
7 (6.5) => 8 (7.8) length: 1 (1.3) (scaled)
7:
2496 => 2912 length: 416
8 (7.8) => 9 (9.1) length: 1 (1.3) (scaled)
8:
2912 => 3328 length: 416
9 (9.1) => 10 (10.4) length: 1 (1.3) (scaled)
9:
3328 => 3744 length: 416
10 (10.4) => 12 (11.7) length: 2 (1.3) (scaled)
10:
3744 => 4160 length: 416
12 (11.7) => 13 (13) length: 1 (1.3) (scaled)
[ 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
This code example demonstrates my problem more clearly.
Interesting fact: mingw-g++, which i used to write this example, shows slightly different values. I usually use visual studio 2010 but couldn't this time because I'm not at home.
I am not sure if I am getting the problem statement but I will take a stab at it. You have ranges of information that you are displaying contigously a range is usually [a,b). Everything is fine when a and b directly represent the pixels that you are trying to draw, but you are having problems when you want to scale the whole thing.
Not dealing with multiple rows of pixels, if you have two ranges R1=[a,b) and R2=[b,c) unscaled you just draw from a to b-1 and from b to c-1 and your ranges are drawn so what is the problem in the scaled case drawing from (int)(a*scale) to ((int)(b*scale)-1) and then from (int)(b*scale) to ((int)(c*scale)-1), you can use any float to int conversion, rounding, floor or ceiling and you should be ok.
The next problem area would be if your scale ranges amount to less than 1 pixel, in this case you might need to detect if the size of the scaled range is 0 and carry a correction factor (in pixels) that is added at the end of the calculation.
Pseudocode
DrawRanges(List<Range> ranges, float scale)
int carry = 0;
foreach(Range range in ranges)
{
int newStart = ceiling(range.start*scale);
int newEnd = ceiling(range.end*scale)-1;
if (newStart <= newEnd)
{
newEnd = newStart;
++carry;
}
DrawRange(newStart+carry,newEnd+carry);
}
This will eventually fail if you have more Ranges than blocks in your scaled down grid, you would have to figure out how to drop ranges completely. In draw rang you map your index to an actual block coordinate.
Does this solve your problem ?
Yeah +1 to get you back up, the question is OK.
I don't know why people thing it is so fun to downvote without even leaving a comment.
Well, to the question :-)
Usually when drawing you have this overlapping issues and in 3D computer graphics with scanline renderers (DirectX & OpenGL for ex) they usually Skip exactly one pixel (say all on the right and down side).
Maybe this can help you out.
It is possible too that when the division is perfect, you don't have the artefacts so you must maybe deal with that (ie. if the value is a 'perfect integer', for example 185.000000 then don't remove the last pixel).
HTH