I implemented A* and JPS(Jump Point Search) using VS2008.
Then i tried comparing time cost of these code.
On debug mode, (my) JPS is faster than A* about 2.0~50 times .
But on release mode, JPS is faster than A* about 0.6~3.0 times.
Especially, almost cases of test on release mode, JPS slower than A*.
Why results are so different?
In the paper( "Online Graph Pruning for Pathfinding on Grid Maps", 2011 ),
JPS is faster than A* about 20~30 times.
If i want to get a similar results in the paper, what should i do?
I just call map1.A_star() and map2.JPS() in main.cpp.
and I used prioiry_queue(STL) for A* and JPS.
↓ pathfinding.cpp
#include "util.h"
using namespace std;
int DIR_X[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };
int DIR_Y[8] = { -1, -1, 0, 1, 1, 1, 0, -1 };
// diagonal index {1, 3, 5, 7}
template<class T>
void vector_clear(vector<T>& vecObj)
{
vector<T> tempObj;
tempObj.swap(vecObj);
}
bool operator<(const Node& a, const Node& b)
{
return a.getPriority() > b.getPriority();
}
void read_scenario(char* path, char(*scenarios)[256], int& total) {
ifstream scen_file(path);
char buffer[256];
int num = 0;
scen_file.getline(buffer, 256);
while (!scen_file.eof()) {
scen_file.getline(buffer, 256);
num++;
int index1;
int index2;
int count = 0;
for (int i = 0; i<256; i++) {
if (buffer[i] == ' ')
count++;
if (buffer[i] == ' ' && count == 4)
index1 = i;
if (buffer[i] == ' ' && count == 8)
index2 = i;
}
for (int i = index1 + 1; i <= index2 - 1; i++) {
scenarios[num][i - (index1 + 1)] = buffer[i];
}
scenarios[num][index2] = NULL;
}
std::cout << num << " 개의 시나리오가 있습니다." << endl;
total = num;
scen_file.close();
}
Map::Map(int* START_GOAL, char* IN_PATH, char* OUT_PATH, string MODE) {
sx = START_GOAL[0];
sy = START_GOAL[1];
gx = START_GOAL[2];
gy = START_GOAL[3];
mode = MODE;
in_path = IN_PATH;
out_path = OUT_PATH;
ifstream map_file(in_path);
if (!map_file.is_open()) {
std::cout << "there is no map_file" << endl;
}
char buffer[128];
char ch[4];
char cw[4];
map_file.getline(buffer, 128);
map_file.getline(buffer, 128);
for (int i = 7; i < strlen(buffer); i++) {
ch[i - 7] = buffer[i];
}
h = atoi(ch);
std::cout << "height = " << h << endl;;
map_file.getline(buffer, 128);
for (int i = 6; i < strlen(buffer); i++) {
cw[i - 6] = buffer[i];
}
w = atoi(cw);
std::cout << "width = " << w << endl;;
map_file.getline(buffer, 128);
std::cout << "Start at (" << sx << " " << sy << ")" << endl;
std::cout << "Goal is (" << gx << " " << gy << ")" << endl;
std::cout << endl;
in_map = new char*[h];
direction_map = new int*[h];
visit_map = new bool*[h];
out_map = new char*[h];
parent_map = new int*[h];
open_node_map = new double*[h];
for (int j = 0; j<h; j++) {
in_map[j] = new char[w];
direction_map[j] = new int[w];
visit_map[j] = new bool[w];
out_map[j] = new char[w];
parent_map[j] = new int[w];
open_node_map[j] = new double[w];
for (int i = 0; i <= w; i++) {
char tmp;
map_file.get(tmp);
if (w == i)
continue;
in_map[j][i] = tmp;
direction_map[j][i] = -1;
visit_map[j][i] = false;
out_map[j][i] = tmp;
open_node_map[j][i] = 0.0;
}
}
map_file.close();
}
Map::~Map() {
for (int i = 0; i<h; i++) {
delete[] parent_map[i];
delete[] in_map[i];
delete[] direction_map[i];
delete[] visit_map[i];
delete[] out_map[i];
delete[] open_node_map[i];
}
delete[] parent_map;
delete[] in_map;
delete[] direction_map;
delete[] visit_map;
delete[] out_map;
delete[] open_node_map;
}
int Map::getGx() const { return gx; }
int Map::getGy() const { return gy; }
int Map::getSx() const { return sx; }
int Map::getSy() const { return sy; }
int Map::getHeight() const { return h; }
int Map::getWidth() const { return w; }
double Map::getOptimalLength() const { return optimal_length; }
char Map::getInMapData(int x, int y) { return in_map[y][x]; }
int Map::getDirectionData(int x, int y) { return direction_map[y][x]; }
bool Map::getVisitMapData(int x, int y) { return visit_map[y][x]; }
int Map::getParentMapData(int x, int y) { return parent_map[y][x]; }
double Map::getOpen_NodeData(int x, int y) { return open_node_map[y][x]; }
char Map::getOutMapData(int x, int y) const { return out_map[y][x]; }
void Map::setVisitMap(int x, int y, bool data) { visit_map[y][x] = data; }
void Map::setDirectionMap(int x, int y, int data) { direction_map[y][x] = data; }
void Map::setOutMap(int x, int y, char data) { out_map[y][x] = data; }
void Map::setParentMap(int x, int y, int data) { parent_map[y][x] = data; }
void Map::setOpen_NodeMap(int x, int y, double data) { open_node_map[y][x] = data; }
void Map::initialize() {}
void Map::draw_map() {
ofstream out_file(out_path);
for (int j = 0; j<h; j++) {
for (int i = 0; i<w; i++) {
if (j == sy && i == sx)
out_map[j][i] = 'S';
if (j == gy && i == gx)
out_map[j][i] = 'G';
out_file << out_map[j][i];
}
out_file << "\r\n";
}
out_file.close();
}
void Map::A_star() {
priority_queue<Node> search_q[2];
Node startPoint(sx, sy, gx, gy, -1, 0, mode);
int pqi = 0;
search_q[pqi].push(startPoint);
Map::setOpen_NodeMap(sx, sy, startPoint.getPriority());
while (!search_q[pqi].empty()) {
int cx = search_q[pqi].top().getX(); // current x, y
int cy = search_q[pqi].top().getY();
double passedLength_c = search_q[pqi].top().getPassedLength();
Map::setVisitMap(cx, cy, true);
Map::setOpen_NodeMap(cx, cy, search_q[pqi].top().getPriority());
search_q[pqi].pop();
if (cx == gx && cy == gy) {
double shortestLength = 0;
while (1) {
if ((cx == sx) && (cy == sy)) break;
int tmp_x, tmp_y, tmp_dir;
tmp_x = cx;
tmp_y = cy;
tmp_dir = getDirectionData(tmp_x, tmp_y);
cx -= DIR_X[tmp_dir];
cy -= DIR_Y[tmp_dir];
setOutMap(cx, cy, '#');
if (tmp_dir % 2 == 1)
shortestLength += sqrt(2.0);
else
shortestLength += 1.0;
}
cout << "A_star find!" << endl;
cout << "Path Length = " << shortestLength << endl;
optimal_length = shortestLength;
while (!search_q[pqi].empty()) {
search_q[pqi].pop();
}
return;
}
for (int dir = 0; dir<8; dir++) {
// next_node
int nx = cx + DIR_X[dir];
int ny = cy + DIR_Y[dir];
if (!(nx >(w - 1) || nx < 0 || ny >(h - 1) || ny < 0 || getInMapData(nx, ny) == '#' || getVisitMapData(nx, ny) == true)) {
Node next_node(nx, ny, gx, gy, passedLength_c, dir, mode, 1);
if (Map::getOpen_NodeData(nx, ny) == 0) {
Map::setOutMap(nx, ny, 'I');
Map::setOpen_NodeMap(nx, ny, next_node.getPriority());
search_q[pqi].push(next_node);
Map::setDirectionMap(nx, ny, dir);
}
else if (Map::getOpen_NodeData(nx, ny) > next_node.getPriority()) {
Map::setOpen_NodeMap(nx, ny, next_node.getPriority());
Map::setDirectionMap(nx, ny, dir);
search_q[pqi].push(next_node);
/*
while (!(search_q[pqi].top().getX() == nx && search_q[pqi].top().getY() == ny))
search_q[1 - pqi].push(search_q[pqi].top());
search_q[pqi].pop();
}
search_q[pqi].pop();
if (search_q[pqi].size() > search_q[1 - pqi].size()) {
pqi = 1 - pqi;
}
while (!search_q[pqi].empty()) {
search_q[1 - pqi].push(search_q[pqi].top());
search_q[pqi].pop();
}
pqi = 1 - pqi;
search_q[pqi].push(next_node);
*/
}
}
}
}
}
void Map::JPS() {
priority_queue <Node> JumpPoints;
Node startPoint(sx, sy, gx, gy, 0, -1, mode, 1);
startPoint.calculateDistanceToGoal();
startPoint.updatePriority();
JumpPoints.push(startPoint);
while (!JumpPoints.empty()) {
int x = JumpPoints.top().getX();
int y = JumpPoints.top().getY();
if (x == gx && y == gy) {
cout << "JPS find!!!" << endl;
double shortestLength = 0;
while (!(x == Map::getSx() && y == Map::getSy())) {
int fix_x = x;
int fix_y = y;
int tmp_dir = getDirectionData(fix_x, fix_y);
int px = Map::getParentMapData(fix_x, fix_y) % 512;
int py = Map::getParentMapData(fix_x, fix_y) / 512;
//while(!(Map::getOutMapData(x, y) == 'J')){
while (!(px == x && py == y)) {
if (Map::getParentMapData(fix_x, fix_y) == (y * Map::getWidth() + x)) break;
//if(!(Map::getOutMapData(x, y) == 'J')){
if (!(Map::getOutMapData(x, y) == 'J')) {
setOutMap(x, y, '#');
}
x -= DIR_X[tmp_dir];
y -= DIR_Y[tmp_dir];
if (tmp_dir % 2 == 1)
shortestLength += sqrt(2.0);
else
shortestLength += 1.0;
}
}
optimal_length = shortestLength;
cout << "Path Length = " << shortestLength << endl;
//cout<<"Path Length = "<< passedLength_c << endl;
return;
}
else
Map::identifySuccessors(JumpPoints);
}
while (!JumpPoints.empty()) {
JumpPoints.pop();
}
cout << "not found" << endl;
}
/*
Node Map::jump(Node const node, int dir, int& off) {
int nx = node.getX() + DIR_X[dir];
int ny = node.getY() + DIR_Y[dir];
if (nx > (w - 1) || nx < 0 || ny >(h - 1) || ny < 0) {
// Map::setOutMap(nx, ny, 'B');
Node NULL_node(-100, -100, 0, 0, 0, 0, "OCTILE", 1);
return NULL_node;
}
char n_MapData = Map::getOutMapData(nx, ny);
if (n_MapData == '#') {
Node NULL_node(-100, -100, 0, 0, 0, 0, "OCTILE", 1);
return NULL_node;
}
Node n_node(nx, ny, gx, gy, node.getPassedLength(), dir);
if (n_MapData == 'I')
Map::setOutMap(nx, ny, 'X');
if (nx == gx && ny == gy) {
off = 1;
return n_node;
}
int forced_neighbours_bits = Map::forced_neighbours(nx, ny, dir);
if (forced_neighbours_bits > 0) {
//Map::setOutMap(nx, ny, 'F');
return n_node;
}
if (dir % 2 == 1) {
// Algorithm 2 function jump 8th line)
if (Map::jump(n_node, (dir + 7) % 8, off).getX() != -100)
return n_node;
if (Map::jump(n_node, (dir + 1) % 8, off).getX() != -100)
return n_node;
}
if (n_MapData != 'S' && n_MapData != 'I' && n_MapData != 'G' && n_MapData != '#' && n_MapData != 'J')
Map::setOutMap(nx, ny, 'I');
//draw_map();
return Map::jump(n_node, dir, off);
}
*/
int Map::jump(int index, int dir, int& off) {
int x = index % w;
int y = index / w;
int nx = x + DIR_X[dir];
int ny = y + DIR_Y[dir];
int n_index = ny * w + nx;
if (nx > (w - 1) || nx < 0 || ny >(h - 1) || ny < 0) {
// Map::setOutMap(nx, ny, 'B');
return -1;
}
char n_MapData = Map::getOutMapData(nx, ny);
if (n_MapData == '#') {
return -1;
}
if (n_MapData == 'I')
Map::setOutMap(nx, ny, 'X');
if (nx == gx && ny == gy) {
off = 1;
return n_index;
}
int forced_neighbours_bits = Map::forced_neighbours(nx, ny, dir);
if (forced_neighbours_bits > 0) {
//Map::setOutMap(nx, ny, 'F');
return n_index;
}
if (dir % 2 == 1) {
// Algorithm 2 function jump 8th line)
if (Map::jump(n_index, (dir + 7) % 8, off) != -1)
return n_index;
if (Map::jump(n_index, (dir + 1) % 8, off) != -1)
return n_index;
}
if (n_MapData != 'S' && n_MapData != 'I' && n_MapData != 'G' && n_MapData != '#' && n_MapData != 'J')
Map::setOutMap(nx, ny, 'I');
//draw_map();
return Map::jump(n_index, dir, off);
}
void Map::identifySuccessors(priority_queue <Node>& successors) {
int x = successors.top().getX();
int y = successors.top().getY();
if (x == gx && y == gy)
return;
int index = y * Map::getWidth() + x;
int dir = successors.top().getDirection();
double passedLength = successors.top().getPassedLength();
Node start(x, y, gx, gy, passedLength, dir, mode, 1);
start.updatePassedLength();
start.calculateDistanceToGoal();
start.updatePriority();
successors.pop();
vector<int> candidate_dir;
if (dir == -1) {
for (int i = 0; i<8; i++) {
int dx = x + DIR_X[i];
int dy = y + DIR_Y[i];
if (!(dx < 0 || dx >(w - 1) || dy < 0 || dy >(h - 1) || Map::getOutMapData(dx, dy) == '#'))
candidate_dir.push_back(i);
}
}
else {
int bits = Map::forced_neighbours(x, y, dir);
for (int i = 0; i<8; i++) {
if (bits & (1 << i))
candidate_dir.push_back(i);
}
if (dir % 2 == 1) {
int dx = x + DIR_X[(dir + 1) % 8];
int dy = y + DIR_Y[(dir + 1) % 8];
if (!(dx < 0 || dx >(w - 1) || dy < 0 || dy >(h - 1) || Map::getOutMapData(dx, dy) == '#'))
candidate_dir.push_back((dir + 1) % 8);
dx = x + DIR_X[(dir + 7) % 8];
dy = y + DIR_Y[(dir + 7) % 8];
if (!(dx < 0 || dx >(w - 1) || dy < 0 || dy >(h - 1) || Map::getOutMapData(dx, dy) == '#'))
candidate_dir.push_back((dir + 7) % 8);
dx = x + DIR_X[dir];
dy = y + DIR_Y[dir];
if (!(dx < 0 || dx >(w - 1) || dy < 0 || dy >(h - 1) || Map::getOutMapData(dx, dy) == '#'))
candidate_dir.push_back(dir);
}
else {
int dx = x + DIR_X[dir];
int dy = y + DIR_Y[dir];
if (!(dx < 0 || dx >(w - 1) || dy < 0 || dy >(h - 1) || Map::getOutMapData(dx, dy) == '#'))
candidate_dir.push_back(dir);
}
}
for (int i = 0; i<candidate_dir.size(); i++) {
int nx, ny, n_index;
int n_dir = candidate_dir[i];
nx = x + DIR_X[n_dir];
ny = y + DIR_Y[n_dir];
int jx, jy;
double j_passedLength, s_dist = 0.0, d_dist = 0.0;
int off = 0;
int j_index = Map::jump(index, n_dir, off);
if (j_index == -1)
continue;
jx = j_index % w;
jy = j_index / w;
j_passedLength = passedLength + sqrt((x - jx)*(x - jx) + (y - jy)*(y - jy));
Node j_node(jx, jy, gx, gy, j_passedLength, n_dir, mode, 1);
j_node.setPassedLength(j_passedLength);
j_node.calculateDistanceToGoal();
j_node.updatePriority();
if (Map::getOpen_NodeData(jx, jy) == 0) {
Map::setOutMap(jx, jy, 'J');
Map::setParentMap(jx, jy, y * Map::getWidth() + x);
Map::setDirectionMap(jx, jy, n_dir);
Map::setOpen_NodeMap(jx, jy, j_node.getPriority());
successors.push(j_node);
}
else if (Map::getOpen_NodeData(jx, jy) > j_node.getPriority()) {
Map::setOpen_NodeMap(jx, jy, j_node.getPriority());
Map::setDirectionMap(jx, jy, n_dir);
Map::setParentMap(jx, jy, y * Map::getWidth() + x);
}
}
candidate_dir.clear();
return;
cout << "not found" << endl;
}
/*
*/
bool Map::is_obstacle(int x, int y, int dir) {
int nx = x + DIR_X[dir];
int ny = y + DIR_Y[dir];
if (nx < 0 || nx >(w - 1) || ny < 0 || ny >(h - 1))
return false;
if (Map::getInMapData(nx, ny) == '#')
return true;
else
return false;
}
int Map::forced_neighbours(int x, int y, int dir) {
int bits = 0;
if (dir == -1)
return -1;
if (dir % 2 == 0) {
// straight
int ndir1 = (dir + 2) % 8;
int ndir2 = (dir + 6) % 8;
if (Map::is_obstacle(x, y, ndir1)) {
if (!Map::is_obstacle(x, y, (dir + 1) % 8))
bits = bits | 1 << ((dir + 1) % 8);
}
if (Map::is_obstacle(x, y, ndir2))
if (!Map::is_obstacle(x, y, (dir + 7) % 8))
bits = bits | 1 << ((dir + 7) % 8);
}
else {
int ndir1 = (dir + 3) % 8;
int ndir2 = (dir + 5) % 8;
if (Map::is_obstacle(x, y, ndir1))
if (!Map::is_obstacle(x, y, (dir + 1) % 8))
bits = bits | 1 << ((dir + 1) % 8);
if (Map::is_obstacle(x, y, ndir2))
if (!Map::is_obstacle(x, y, (dir + 7) % 8))
bits = bits | 1 << ((dir + 7) % 8);
}
return bits;
}
Node::Node(int const x, int const y, int const gx, int const gy, double const passedLength, int const direction, string const mode, int const k) {
this->x = x; this->y = y;
this->direction = direction;
this->passedLength = passedLength;
this->gx = gx; this->gy = gy;
this->mode = mode;
if (k == 1) {
updatePassedLength();
calculateDistanceToGoal();
updatePriority();
}
}
Node::~Node() {}
int Node::getX() const { return x; }
int Node::getY() const { return y; }
int Node::getDirection() const { return direction; }
double Node::getPriority() const { return priority; }
double Node::getPassedLength() const { return passedLength; }
double Node::getDistanceToGoal() const { return distanceToGoal; }
void Node::calculateDistanceToGoal() {
double xd = abs(x - gx);
double yd = abs(y - gy);
if (mode.compare("MANHATTAN") == 0)
distanceToGoal = abs(xd) + abs(yd);
else if (mode.compare("EUCLIDIAN") == 0)
distanceToGoal = sqrt((xd*xd) + (yd*yd));
else if (mode.compare("CHEBYSHEV") == 0)
distanceToGoal = max(abs(xd), abs(yd));
else if (mode.compare("OCTILE") == 0)
distanceToGoal = max(xd, yd) + (sqrt(2.0) - 1) + min(xd, yd);
else
cout << "plz input mode" << endl;
}
void Node::updatePassedLength() {
if (direction == -1);
else if (direction % 2 == 1)
passedLength += (sqrt(2.0));
else
passedLength += (1);
}
void Node::updatePriority() {
priority = passedLength + distanceToGoal;
}
void Node::setPassedLength(double data) {
passedLength = data;
}
util.h
#include <iostream>
#include <string>
#include <queue>
#include <math.h>
#include <fstream>
#include <istream>
#include <cstdio>
#include <vector>
using namespace std;
class Node {
private:
int x;
int y;
int gx;
int gy;
int direction; // direction from past node
double passedLength;
double distanceToGoal;
double priority;
string mode; // mode -> "MANHATTAN", "EUCLIDIAN", CHEBYSHEV", "OCTILE"
public:
Node(int const x, int const y, int const gx, int const gy, double const passedLength, int const direction = 0, string const mode = "MANHATTAN" , int const k = 0 );
~Node();
int getX() const;
int getY() const;
int getDirection() const;
double getPriority() const;
double getPassedLength() const;
double getDistanceToGoal() const;
void setPassedLength(double data);
void calculateDistanceToGoal();
void updatePassedLength();
void updatePriority();
};
class Map {
public:
int gx, gy, sx, sy, w, h;
char* in_path, *out_path;
string mode;
char** in_map;
int** direction_map;
bool** visit_map;
char** out_map;
int** parent_map;
double** open_node_map;
double optimal_length;
public:
Map(int* START_GOAL, char* IN_PATH, char* OUT_PATH, string MODE);
~Map();
int getGx() const;
int getGy() const;
int getSx() const;
int getSy() const;
char getInMapData(int x, int y);
int getDirectionData(int x, int y);
bool getVisitMapData(int x, int y);
int getParentMapData(int x, int y);
double getOpen_NodeData(int x, int y);
int getHeight() const;
int getWidth() const;
double getOptimalLength() const;
char getOutMapData(int x, int y) const;
void setVisitMap(int x, int y, bool data);
void setDirectionMap(int x, int y, int data);
void setOutMap(int x, int y, char data);
void setParentMap(int x, int y, int data);
void setOpen_NodeMap(int x, int y, double data);
void initialize();
void draw_map();
void A_star();
void JPS();
//int identifySuccessors(int x, int y);
void identifySuccessors(priority_queue <Node>& successors);
//int jump(int node_index, int dir, double& s_distance, priority_queue <Node>& successors, double& d_distance, int& trig, int& fx_fy);
//Node Map::jump(Node node, int dir, int& off);
int Map::jump(int index, int dir, int& off);
bool is_obstacle(int x, int n, int dir);
int Map::forced_neighbours(int x, int y, int dir);
};
void read_scenario(char* path, char(*scenarios)[256], int& total);
Without an individual review of each line of code, it appears that the speed difference is due to your programming style.
To take just the first two examples: vector_clear is a poor reimplementation of std::vector::clear and operator<(Node a, Node b) makes unnecessary copies of both nodes. And glancing at the rest of the code, these do not appear to be exceptions.
Measuring the speed of a debug executable is pointless. The compiler settings used for debugging do not take speed of the resulting executable into account. And you've further complicated it by using a debug version of new. Only the speed in release mode is reasonable, and then only if you have good code to start with.
Related
I have a vector<Octree*> children variable where each point in the octree is an RGB value (made of 3 components).When I do
int G1= children[i]->point->G;
I get the address as an int not the value . How can I de-reference the pointer in this case?
this is the function where I use it:
vector Octree::find(int R,int G, int B)
{
vector<RGBvalues> result;
int midR = (topLeftFront->R
+ bottomRightBack->R)
/ 2;
int midG = (topLeftFront->G
+ bottomRightBack->G)
/ 2;
int midB = (topLeftFront->B
+ bottomRightBack->B)
/ 2;
int pos = -1;
// Deciding the position
// where to move
if (R <= midR) {
if (G <= midG) {
if (B <= midB)
pos = TopLeftFront;
else
pos = TopLeftBottom;
}
else {
if (B <= midB)
pos = BottomLeftFront;
else
pos = BottomLeftBack;
}
}
else {
if (G <= midG) {
if (B <= midB)
pos = TopRightFront;
else
pos = TopRightBottom;
}
else {
if (B <= midB)
pos = BottomRightFront;
else
pos = BottomRightBack;
}
}
// If an internal node is encountered
if (children[pos]->point == nullptr) {
return children[pos]->find(R,G,B);
}
// If an empty node is encountered
else if (children[pos]->point->R == -1) {
return vector<RGBvalues>();
}
else {
// If node is found with
// the given value
if (R == children[pos]->point->R
&& G == children[pos]->point->G
&& B == children[pos]->point->B)
{ for(int i= 0;i<8;i++ )
{ int R1 =children[pos]->point->R;
int G1= children[i]->point->G;
int B1= children[i]->point->B;
RGBvalues rez;
rez.R= R1;
rez.G= G1;
rez.B= B1;
result.push_back(rez);
}
return result ;
}
return vector<RGBvalues>();
}
}
When I tried debugging, in the last if condition I get the correct R value when writing if (R == children[pos]->point->R) but not when I do children[i]->point->R
THIS IS THE Octree class:
#define TopLeftFront 0
#define TopRightFront 1
#define BottomRightFront 2
#define BottomLeftFront 3
#define TopLeftBottom 4
#define TopRightBottom 5
#define BottomRightBack 6
#define BottomLeftBack 7
class Octree{
RGBvalues* point;
RGBvalues *topLeftFront, *bottomRightBack;
vector<Octree*> children;
public:
Octree();
Octree(int x, int y, int z);
Octree(int x1, int y1, int z1, int x2, int y2, int z2);
void insert(int x, int y , int z);
vector<RGBvalues> find(int x, int y, int z);
bool findValue(int x, int y, int z);
};
AND THE RGBvalues struct:
struct RGBvalues{
int R;
int G;
int B;
RGBvalues()
: R(-1), G(-1), B(-1)
{
}
RGBvalues(int a, int b, int c)
: R(a), G(b), B(c)
{
}
};
HERE IS THE REST OF THE CODE that creates and inserts in an octree, if you find it helpful:
Octree::Octree()
{
// To declare empty node
point = new RGBvalues();
}
// Constructor with three arguments
Octree:: Octree(int x, int y, int z)
{
// To declare point node
point = new RGBvalues(x, y, z);
}
// Constructor with six arguments
Octree::Octree(int x1, int y1, int z1, int x2, int y2, int z2)
{
point = nullptr;
topLeftFront
= new RGBvalues(x1, y1, z1);
bottomRightBack
= new RGBvalues(x2, y2, z2);
// Assigning null to the children
children.assign(8, nullptr);
for (int i = TopLeftFront;
i <= BottomLeftBack;
++i)
children[i] = new Octree();
}
// Function to insert a point in the octree
void Octree::insert(int R, int G, int B)
{
if (findValue(R,G,B)) {
return;
}
// Binary search to insert the point
int midR = (topLeftFront->R
+ bottomRightBack->R)
/ 2;
int midG = (topLeftFront->G
+ bottomRightBack->G)
/ 2;
int midB = (topLeftFront->B
+ bottomRightBack->B)
/ 2;
int pos = -1;
// Checking the octant of
// the point
if (R <= midR) {
if (G <= midG) {
if (B <= midB)
pos = TopLeftFront;
else
pos = TopLeftBottom;
}
else {
if (B <= midB)
pos = BottomLeftFront;
else
pos = BottomLeftBack;
}
}
else {
if (G <= midG) {
if (B <= midB)
pos = TopRightFront;
else
pos = TopRightBottom;
}
else {
if (B <= midB)
pos = BottomRightFront;
else
pos = BottomRightBack;
}
}
// If an internal node is encountered
if (children[pos]->point == nullptr) {
children[pos]->insert(R,G,B);
return;
}
// If an empty node is encountered
else if (children[pos]->point->R == -1) {
delete children[pos];
children[pos] = new Octree(R, G, B);
return;
}
else {
int R_ = children[pos]->point->R,
G_ = children[pos]->point->G,
B_ = children[pos]->point->B;
delete children[pos];
children[pos] = nullptr;
if (pos == TopLeftFront) {
children[pos] = new Octree(topLeftFront->R,
topLeftFront->G,
topLeftFront->B,
midR,
midG,
midB);
}
else if (pos == TopRightFront) {
children[pos] = new Octree(midR + 1,
topLeftFront->G,
topLeftFront->B,
bottomRightBack->R,
midG,
midB);
}
else if (pos == BottomRightFront) {
children[pos] = new Octree(midR + 1,
midG + 1,
topLeftFront->B,
bottomRightBack->R,
bottomRightBack->G,
midB);
}
else if (pos == BottomLeftFront) {
children[pos] = new Octree(topLeftFront->R,
midG + 1,
topLeftFront->B,
midR,
bottomRightBack->G,
midB);
}
else if (pos == TopLeftBottom) {
children[pos] = new Octree(topLeftFront->R,
topLeftFront->G,
midB + 1,
midR,
midG,
bottomRightBack->B);
}
else if (pos == TopRightBottom) {
children[pos] = new Octree(midR + 1,
topLeftFront->G,
midB + 1,
bottomRightBack->R,
midG,
bottomRightBack->B);
}
else if (pos == BottomRightBack) {
children[pos] = new Octree(midR + 1,
midG + 1,
midB + 1,
bottomRightBack->R,
bottomRightBack->G,
bottomRightBack->B);
}
else if (pos == BottomLeftBack) {
children[pos] = new Octree(topLeftFront->R,
midG + 1,
midB + 1,
midR,
bottomRightBack->G,
bottomRightBack->B);
}
children[pos]->insert(R_,G_, B_);
children[pos]->insert(R,G,B);
}
}
In the find() function I want an RGBvalues vector of the brothers of the node I was looking for ( including him) ,if you have other suggestions on how to do that, it is very welcomed.
Everything works fine except the segmentation fault I get at that if().
I think a problem is that in the children vector there can be null pointers as well so I can't push-back() that.
Much thanks for the help in additionally.
I'm trying to build a BVH Tree with Surface Area Heuristic, but everytime I compile my code it gives me random errors like:
"Access violation reading location"
"Run-Time Check Failure #2 - Stack around the variable 'x' was
corrupted."
"Stack overflow "
The errors happen in the BVH::buildSAH() function.
And I have tried to find a solution for the whole day, meaningless. Could it be something from the std::partition function or from sending variables with pointers to a recursion?
I'm reading from the book "Physically Based Rendering: From Theory to Implementation
By Matt Pharr, Greg Humphreys"
It works for 2 primitives in the area, but thats trivial...
If you would like to clone: https://github.com/vkaytsanov/MortonCode-BVH-KD
My BVH.hpp:
#include <vector>
#include <cassert>
#include <algorithm>
#include "memory.hpp"
#include "Screen.hpp"
#include "Point3D.hpp"
#include "BoundBox.hpp"
#pragma once
enum Axis{
X, Y, Z
};
struct MortonPrimitive{
int primitiveIndex;
uint32_t mortonCode;
};
struct BVHPrimitiveInfo {
BVHPrimitiveInfo() {}
BVHPrimitiveInfo(int primitiveNumber, const BoundBox& box) : primitiveNumber(primitiveNumber), box(box),
centroid(Point3D(box.pMin.x* 0.5f + box.pMax.x * 0.5f, box.pMin.y* 0.5f + box.pMax.y * 0.5f, box.pMin.z* 0.5f + box.pMax.z * 0.5f)) {}
int primitiveNumber;
BoundBox box;
Point3D centroid;
};
struct BVHNode {
void InitLeaf(int first, int n, const BoundBox& b) {
firstPrimOffset = first;
nPrimitives = n;
box = b;
children[0] = children[1] = nullptr;
}
void InitInterior(int axis, BVHNode* c0, BVHNode* c1) {
assert(c0 != NULL || c1 != NULL);
children[0] = c0;
children[1] = c1;
this->box = Union(c0->box, c1->box);
splitAxis = axis;
nPrimitives = 0;
}
BoundBox box;
BVHNode* children[2];
int splitAxis, firstPrimOffset, nPrimitives;
};
struct LinearBVHNode {
BoundBox bounds;
union {
int primitivesOffset; // leaf
int secondChildOffset; // interior
};
uint16_t nPrimitives; // 0 -> interior node
uint8_t axis; // interior node: xyz
uint8_t pad[1]; // ensure 32 byte total size
};
struct BVHLittleTree {
int startIndex;
int numPrimitives;
BVHNode* nodes;
};
struct BVH {
BVH(std::vector<std::shared_ptr<Primitive>> p) : primitives(std::move(p)) {
std::vector<BVHPrimitiveInfo> BVHPrimitives;
BVHPrimitives.reserve(primitives.size());
for (int i = 0; i < primitives.size(); i++) {
BVHPrimitives.push_back({ i, primitives[i]->box });
}
MemoryArena arena(1024 * 1024);
int totalNodes = 0;
std::vector<std::shared_ptr<Primitive>> orderedPrimitives;
orderedPrimitives.reserve(primitives.size());
BVHNode* root;
root = HLBVHBuild(arena, BVHPrimitives, &totalNodes, orderedPrimitives);
primitives.swap(orderedPrimitives);
BVHPrimitives.resize(0);
printf("BVH created with %d nodes for %d "
"primitives (%.4f MB), arena allocated %.2f MB\n",
(int)totalNodes, (int)primitives.size(),
float(totalNodes * sizeof(LinearBVHNode)) /
(1024.f * 1024.f),
float(arena.TotalAllocated()) /
(1024.f * 1024.f));
assert(root != NULL);
nodes = AllocAligned<LinearBVHNode>(totalNodes);
int offset = 0;
flattenBVHTree(root, &offset);
}
~BVH() { FreeAligned(nodes); }
BVHNode* build(std::vector<MortonPrimitive>&, std::vector<Primitive>&);
BVHNode* HLBVHBuild(MemoryArena& arena, const std::vector<BVHPrimitiveInfo>& BVHPrimitives, int* totalNodes, std::vector<std::shared_ptr<Primitive>>& orderedPrims);
BVHNode* emit(BVHNode*& nodes, const std::vector<BVHPrimitiveInfo>& BVHPrimitives, MortonPrimitive* mortonPrimitives, std::vector<std::shared_ptr<Primitive>>&, int, int*, int*, int);
BVHNode* buildSAH(MemoryArena& arena, std::vector<BVHNode*>& treeRoots, int start, int end, int* total) const;
int flattenBVHTree(BVHNode*, int*);
std::vector<std::shared_ptr<Primitive>> primitives;
LinearBVHNode* nodes = nullptr;
int maxPrimsInNode = 1;
};
inline uint32_t LeftShift3(uint32_t x) {
if (x == (1 << 10)) --x;
x = (x | (x << 16)) & 0b00000011000000000000000011111111;
x = (x | (x << 8)) & 0b00000011000000001111000000001111;
x = (x | (x << 4)) & 0b00000011000011000011000011000011;
x = (x | (x << 2)) & 0b00001001001001001001001001001001;
return x;
}
uint32_t EncodeMorton3(const Point3D& p) {
return (LeftShift3(p.z) << 2) |
(LeftShift3(p.y) << 1) |
(LeftShift3(p.x) << 0);
}
short bitValue(uint32_t& number, uint32_t& mask) {
return number & mask ? 1 : 0;
}
static void radixSort(std::vector<MortonPrimitive>* v)
{
std::vector<MortonPrimitive> tempVector(v->size());
const int bitsPerPass = 6;
const int nBits = 30;
static_assert((nBits % bitsPerPass) == 0,
"Radix sort bitsPerPass must evenly divide nBits");
const int nPasses = nBits / bitsPerPass;
for (int pass = 0; pass < nPasses; ++pass) {
// Perform one pass of radix sort, sorting _bitsPerPass_ bits
int lowBit = pass * bitsPerPass;
// Set in and out vector pointers for radix sort pass
std::vector<MortonPrimitive>& in = (pass & 1) ? tempVector : *v;
std::vector<MortonPrimitive>& out = (pass & 1) ? *v : tempVector;
// Count number of zero bits in array for current radix sort bit
const int nBuckets = 1 << bitsPerPass;
int bucketCount[nBuckets] = { 0 };
const int bitMask = (1 << bitsPerPass) - 1;
for (const MortonPrimitive& mp : in) {
int bucket = (mp.mortonCode >> lowBit) & bitMask;
++bucketCount[bucket];
}
// Compute starting index in output array for each bucket
int outIndex[nBuckets];
outIndex[0] = 0;
for (int i = 1; i < nBuckets; ++i)
outIndex[i] = outIndex[i - 1] + bucketCount[i - 1];
// Store sorted values in output array
for (const MortonPrimitive& mp : in) {
int bucket = (mp.mortonCode >> lowBit) & bitMask;
out[outIndex[bucket]++] = mp;
}
}
// Copy final result from _tempVector_, if needed
if (nPasses & 1) std::swap(*v, tempVector);
}
//BVHNode* BVH::build(std::vector<MortonPrimitive>& mortonPrimitives, std::vector<Primitive>& prims) {
//
//
//}
struct BucketInfo {
int count = 0;
BoundBox bounds;
};
BVHNode* BVH::HLBVHBuild(MemoryArena& arena, const std::vector<BVHPrimitiveInfo>& BVHPrimitives, int* totalNodes, std::vector<std::shared_ptr<Primitive>>& orderedPrims) {
BoundBox box;
for (const BVHPrimitiveInfo& pi : BVHPrimitives) {
box = box.Union(box, pi.centroid); // maybe it should be UNION #TODO
}
std::vector<MortonPrimitive> mortonPrims(BVHPrimitives.size());
for (int i = 0; i < BVHPrimitives.size(); i++) {
const int mortonBits = 10;
const int mortonScale = 1 << mortonBits;
mortonPrims[i].primitiveIndex = BVHPrimitives[i].primitiveNumber;
Point3D p = box.offset(BVHPrimitives[i].centroid);
p.x = p.x * mortonScale;
p.y = p.y * mortonScale;
p.z = p.z * mortonScale;
mortonPrims[i].mortonCode = EncodeMorton3(p);
}
radixSort(&mortonPrims);
//for (MortonPrimitive mp : mortonPrims) {
// std::cout << mp.primitiveIndex << " " << mp.mortonCode << std::endl;
//}
std::vector<BVHLittleTree> treesToBuild;
uint32_t mask = 0b00111111111111000000000000000000; // first 12 bits describe the position of the primitive
for (int start = 0, end = 1; end <= (int)mortonPrims.size(); end++) {
if (end == mortonPrims.size() || ((mortonPrims[start].mortonCode & mask) != (mortonPrims[end].mortonCode & mask))) {
int n = end - start;
int maxNodes = 2 * n;
BVHNode* nodes = arena.Alloc<BVHNode>(maxNodes, false);
treesToBuild.push_back({ start, n, nodes });
start = end;
}
}
int orderedPrimsOffset = 0;
orderedPrims.resize(primitives.size());
int nodesCreated = 0;
int firstBitIndex = 29 - 12;
for (int i = 0; i < treesToBuild.size(); i++) {
treesToBuild[i].nodes = BVH::emit(treesToBuild[i].nodes, BVHPrimitives, &mortonPrims[treesToBuild[i].startIndex], orderedPrims, treesToBuild[i].numPrimitives, &nodesCreated, &orderedPrimsOffset, firstBitIndex);
*totalNodes += nodesCreated;
}
totalNodes += nodesCreated;
std::vector<BVHNode*> finishedTrees;
finishedTrees.reserve(treesToBuild.size());
for (BVHLittleTree& tr : treesToBuild) {
finishedTrees.emplace_back(tr.nodes);
}
return buildSAH(arena, finishedTrees, 0, finishedTrees.size(), totalNodes);
}
BVHNode* BVH::emit(BVHNode*& nodes, const std::vector<BVHPrimitiveInfo>& BVHPrimitive, MortonPrimitive* mortonPrimitives, std::vector<std::shared_ptr<Primitive>>& orderedPrimitives, int primitivesCount, int* totalNodes, int* orderedPrimsOffset, int bitIndex) {
if (bitIndex == -1 || primitivesCount < maxPrimsInNode) {
(*totalNodes)++;
BVHNode* tmp = nodes++;
BoundBox box;
int firstPrimOffset = *orderedPrimsOffset;
for (int i = 0; i < primitivesCount; i++) {
int index = mortonPrimitives[i].primitiveIndex;
orderedPrimitives[firstPrimOffset + i] = primitives[index];
box = box.Union(box, BVHPrimitive[index].box);
}
tmp->InitLeaf(0, primitivesCount, box);
return tmp;
}
else {
int mask = 1 << bitIndex;
if ((mortonPrimitives[0].mortonCode & mask) == (mortonPrimitives[primitivesCount - 1].mortonCode & mask)){ // Next tree if nothing to split for this bit
return emit(nodes, BVHPrimitive, mortonPrimitives, orderedPrimitives, primitivesCount, totalNodes, orderedPrimsOffset, bitIndex - 1);
}
int start = 0;
int end = primitivesCount - 1;
while (start + 1 != end) {
int mid = (end - start) / 2 + start; // (start-end)/2
if ((mortonPrimitives[start].mortonCode & mask) == (mortonPrimitives[mid].mortonCode & mask)) {
start = mid;
}
else {
end = mid;
}
}
int split = end;
(*totalNodes)++;
BVHNode* tmp = nodes++;
BVHNode* lbvh[2];
lbvh[0] = emit(nodes, BVHPrimitive, mortonPrimitives, orderedPrimitives, split, totalNodes, orderedPrimsOffset, bitIndex-1);
lbvh[1] = emit(nodes, BVHPrimitive, &mortonPrimitives[split], orderedPrimitives, primitivesCount - split, totalNodes, orderedPrimsOffset, bitIndex - 1);
int axis = bitIndex % 3;
tmp->InitInterior(axis, lbvh[0], lbvh[1]);
return tmp;
}
}
BVHNode* BVH::buildSAH(MemoryArena& arena, std::vector<BVHNode*>& treeRoots, int start, int end, int* total) const {
int nodesCount = end - start;
if (nodesCount == 1) {
return treeRoots[start];
}
assert(nodesCount > 1);
(*total)++;
BVHNode* node = arena.Alloc<BVHNode>();
BoundBox box;
for (int i = start; i < end; i++) {
box = Union(box, treeRoots[i]->box);
}
BoundBox centroidBox;
for (int i = start; i < end; i++) {
Point3D centroid = Point3D((treeRoots[i]->box.pMin.x + treeRoots[i]->box.pMax.x) * 0.5f, (treeRoots[i]->box.pMin.y + treeRoots[i]->box.pMax.y) * 0.5f, (treeRoots[i]->box.pMin.z + treeRoots[i]->box.pMax.z) * 0.5f);
centroidBox = Union(centroidBox, centroid);
}
const int dimension = centroidBox.MaximumExtent();
const int nBuckets = 12;
struct Buckets {
int count = 0;
BoundBox box;
};
Buckets buckets[nBuckets];
for (int i = start; i < end; i++) {
float centroid = (treeRoots[i]->box.pMin[dimension] * 0.5f + treeRoots[i]->box.pMax[dimension] * 0.5f) ;
int b = nBuckets * ((centroid - centroidBox.pMin[dimension]) / (centroidBox.pMax[dimension] - centroidBox.pMin[dimension]));
if (b == nBuckets) b = nBuckets - 1;
//assert(b < nBuckets);
buckets[b].count++;
buckets[b].box = Union(buckets[b].box, treeRoots[i]->box);
}
float cost[nBuckets - 1];
for (int i = 0; i < nBuckets - 1; i++) {
BoundBox b0, b1;
int count0 = 0, count1 = 0;
for (int j = 0; j <= i; j++) {
b0 = Union(b0, buckets[j].box);
count0 += buckets[j].count;
}
for (int j = i+1; j < nBuckets; j++) {
b1 = Union(b1, buckets[j].box);
count1 += buckets[j].count;
}
cost[i] = (.125f + (count0 * b0.surfaceArea() + count1 * b1.surfaceArea())) / box.surfaceArea();
}
double minCost = cost[0];
int minCostSplitBucket = 0;
for (int i = 1; i < nBuckets - 1; ++i) {
if (cost[i] < minCost) {
minCost = cost[i];
minCostSplitBucket = i;
}
}
BVHNode** pmid = std::partition(&treeRoots[start], &treeRoots[end - 1] + 1, [=](const BVHNode* node) {
double centroid = (node->box.pMin[dimension]*0.5f + node->box.pMax[dimension] * 0.5f) ;
int b = nBuckets * ((centroid - centroidBox.pMin[dimension]) / (centroidBox.pMax[dimension] - centroidBox.pMin[dimension]));
if (b == nBuckets) b = nBuckets - 1;
return b <= minCostSplitBucket;
});
assert(pmid != NULL);
//std::cout << pmid << " " << &treeRoots[0];
int mid = pmid - &treeRoots[0];
//std::cout << start << " " << mid << std::endl;
//std::cout << mid << " " << end << std::endl;
std::cout << dimension << std::endl;
//assert(dimension < 3);
node->InitInterior(dimension, this->buildSAH(arena, treeRoots, start, mid, total), this->buildSAH(arena, treeRoots, mid, end, total));
return node;
}
int BVH::flattenBVHTree(BVHNode* node, int* offset) {
LinearBVHNode* linearNode = &nodes[*offset];
linearNode->bounds = node->box;
int myOffset = (*offset)++;
if (node->nPrimitives > 0) {
linearNode->primitivesOffset = node->firstPrimOffset;
linearNode->nPrimitives = node->nPrimitives;
}
else {
// Create interior flattened BVH node
linearNode->axis = node->splitAxis;
linearNode->nPrimitives = 0;
flattenBVHTree(node->children[0], offset);
linearNode->secondChildOffset = flattenBVHTree(node->children[1], offset);
}
return myOffset;
}
My Point3D.hpp
#include <cstdint>
#pragma once
struct Point3D {
float x;
float y;
float z;
Point3D(uint32_t, uint32_t, uint32_t);
Point3D();
int operator[](int);
int operator[](int) const;
Point3D operator+(int);
Point3D operator-(int);
Point3D operator-(Point3D&);
};
Point3D::Point3D() {
x = 0;
y = 0;
z = 0;
}
Point3D::Point3D(uint32_t x, uint32_t y, uint32_t z) {
this->x = x;
this->y = y;
this->z = z;
}
bool operator<(Point3D a, Point3D b) {
uint32_t xSquare = a.x * a.x;
uint32_t ySquare = a.y * a.y;
uint32_t zSquare = a.z * a.z;
uint32_t x2Square = b.x * b.x;
uint32_t y2Square = b.y * b.y;
uint32_t z2Square = b.z * b.z;
int64_t sum = std::sqrt(xSquare + ySquare + z2Square) - std::sqrt(x2Square + y2Square + z2Square);
return sum < 0 ||
sum == 0 && xSquare < x2Square ||
sum == 0 && xSquare == x2Square && ySquare < y2Square ||
sum == 0 && xSquare == x2Square && ySquare == y2Square && zSquare < z2Square;
}
bool operator>(Point3D a, Point3D b) {
uint32_t xSquare = a.x * a.x;
uint32_t ySquare = a.y * a.y;
uint32_t zSquare = a.z * a.z;
uint32_t x2Square = b.x * b.x;
uint32_t y2Square = b.y * b.y;
uint32_t z2Square = b.z * b.z;
int32_t sum = std::sqrt(xSquare + ySquare + z2Square) - std::sqrt(x2Square + y2Square + z2Square);
return sum > 0 ||
sum == 0 && xSquare > x2Square ||
sum == 0 && xSquare == x2Square && ySquare > y2Square ||
sum == 0 && xSquare == x2Square && ySquare == y2Square && zSquare > z2Square;
}
int Point3D::operator[](int i) {
if (i == 0) return x;
if (i == 1) return y;
return z;
}
Point3D Point3D::operator+(int i) {
this->x += i;
this->y += i;
this->z += i;
return *this;
}
Point3D Point3D::operator-(const int i) {
this->x -= i;
this->y -= i;
this->z -= i;
return *this;
}
Point3D Point3D::operator-(Point3D& p) {
this->x -= p.x;
this->y -= p.y;
this->z -= p.z;
return *this;
}
int Point3D::operator[](const int i) const {
if (i == 0) return x;
if (i == 1) return y;
return z;
}
My BoundBox.hpp
#include "Point3D.hpp"
#include "Vector3D.hpp"
#pragma once
struct BoundBox {
Point3D pMin;
Point3D pMax;
BoundBox(Point3D);
BoundBox(Point3D, Point3D);
BoundBox();
void setBounds(BoundBox);
void Union(BoundBox);
BoundBox Union(BoundBox&, Point3D&);
BoundBox Union(BoundBox, BoundBox);
BoundBox unite(BoundBox, BoundBox);
BoundBox unite(BoundBox);
const Point3D offset(const Point3D&);
Point3D diagonal();
const int MaximumExtent();
float surfaceArea();
};
BoundBox::BoundBox() {
float minNum = 0;
pMin = Point3D(800, 600, 300);
pMax = Point3D(minNum, minNum, minNum);
}
BoundBox::BoundBox(Point3D p){
pMin = p;
pMax = p;
}
BoundBox::BoundBox(Point3D p1, Point3D p2) {
pMin = Point3D(std::min(p1.x, p2.x), std::min(p1.y, p2.y), std::min(p1.z, p2.z));
pMax = Point3D(std::max(p1.x, p2.x), std::max(p1.y, p2.y), std::max(p1.z, p2.z));
}
BoundBox BoundBox::Union(BoundBox& box, Point3D& p) {
BoundBox newBox;
newBox.pMin = Point3D(std::min(box.pMin.x, p.x), std::min(box.pMin.y, p.y), std::min(box.pMin.z, p.z));
newBox.pMax = Point3D(std::max(box.pMax.x, p.x), std::max(box.pMax.y, p.y), std::max(box.pMax.z, p.z));
return newBox;
}
BoundBox BoundBox::Union(BoundBox box1, BoundBox box2) {
BoundBox newBox;
newBox.pMin = std::min(box1.pMin, box2.pMin);
newBox.pMax = std::max(box1.pMax, box2.pMax);
return newBox;
}
BoundBox Union(BoundBox box1, BoundBox box2) {
BoundBox newBox;
newBox.pMin = std::min(box1.pMin, box2.pMin);
newBox.pMax = std::max(box1.pMax, box2.pMax);
return newBox;
}
BoundBox BoundBox::unite(BoundBox b1, BoundBox b2) {
bool x = (b1.pMax.x >= b2.pMin.x) && (b1.pMin.x <= b2.pMax.x);
bool y = (b1.pMax.y >= b2.pMin.y) && (b1.pMin.y <= b2.pMax.y);
bool z = (b1.pMax.z >= b2.pMin.z) && (b1.pMin.z <= b2.pMax.z);
if (x && y && z) {
return Union(b1, b2);
}
}
BoundBox BoundBox::unite(BoundBox b2) {
bool x = (this->pMax.x >= b2.pMin.x) && (this->pMin.x <= b2.pMax.x);
bool y = (this->pMax.y >= b2.pMin.y) && (this->pMin.y <= b2.pMax.y);
bool z = (this->pMax.z >= b2.pMin.z) && (this->pMin.z <= b2.pMax.z);
if (x && y && z) {
return Union(*this, b2);
}
else return *this;
}
const int BoundBox::MaximumExtent() {
Point3D d = Point3D(this->pMax.x - this->pMin.x, this->pMax.y - this->pMin.y, this->pMax.z - this->pMin.z); // diagonal
if (d.x > d.y && d.x > d.z) {
return 0;
}
else if (d.y > d.z) {
return 1;
}
else {
return 2;
}
}
float BoundBox::surfaceArea() {
Point3D d = Point3D(this->pMax.x - this->pMin.x, this->pMax.y - this->pMin.y, this->pMax.z - this->pMin.z); // diagonal
return 2 * (d.x * d.y + d.x * d.z + d.y * d.z);
}
const Point3D BoundBox::offset(const Point3D& p) {
Point3D o = Point3D(p.x - pMin.x, p.y - pMin.y, p.z - pMin.z);
if (pMax.x > pMin.x) o.x /= pMax.x - pMin.x;
if (pMax.y > pMin.y) o.y /= pMax.y - pMin.y;
if (pMax.z > pMin.z) o.z /= pMax.z - pMin.z;
return o;
}
My memory.hpp
#include <list>
#include <cstddef>
#include <algorithm>
#include <malloc.h>
#include <stdlib.h>
#pragma once
#define ARENA_ALLOC(arena, Type) new ((arena).Alloc(sizeof(Type))) Type
void* AllocAligned(size_t size);
template <typename T>
T* AllocAligned(size_t count) {
return (T*)AllocAligned(count * sizeof(T));
}
void FreeAligned(void*);
class
#ifdef PBRT_HAVE_ALIGNAS
alignas(PBRT_L1_CACHE_LINE_SIZE)
#endif // PBRT_HAVE_ALIGNAS
MemoryArena {
public:
// MemoryArena Public Methods
MemoryArena(size_t blockSize = 262144) : blockSize(blockSize) {}
~MemoryArena() {
FreeAligned(currentBlock);
for (auto& block : usedBlocks) FreeAligned(block.second);
for (auto& block : availableBlocks) FreeAligned(block.second);
}
void* Alloc(size_t nBytes) {
// Round up _nBytes_ to minimum machine alignment
#if __GNUC__ == 4 && __GNUC_MINOR__ < 9
// gcc bug: max_align_t wasn't in std:: until 4.9.0
const int align = alignof(::max_align_t);
#elif !defined(PBRT_HAVE_ALIGNOF)
const int align = 16;
#else
const int align = alignof(std::max_align_t);
#endif
#ifdef PBRT_HAVE_CONSTEXPR
static_assert(IsPowerOf2(align), "Minimum alignment not a power of two");
#endif
nBytes = (nBytes + align - 1) & ~(align - 1);
if (currentBlockPos + nBytes > currentAllocSize) {
// Add current block to _usedBlocks_ list
if (currentBlock) {
usedBlocks.push_back(
std::make_pair(currentAllocSize, currentBlock));
currentBlock = nullptr;
currentAllocSize = 0;
}
// Get new block of memory for _MemoryArena_
// Try to get memory block from _availableBlocks_
for (auto iter = availableBlocks.begin();
iter != availableBlocks.end(); ++iter) {
if (iter->first >= nBytes) {
currentAllocSize = iter->first;
currentBlock = iter->second;
availableBlocks.erase(iter);
break;
}
}
if (!currentBlock) {
currentAllocSize = std::max(nBytes, blockSize);
currentBlock = AllocAligned<uint8_t>(currentAllocSize);
}
currentBlockPos = 0;
}
void* ret = currentBlock + currentBlockPos;
currentBlockPos += nBytes;
return ret;
}
template <typename T>
T* Alloc(size_t n = 1, bool runConstructor = true) {
T* ret = (T*)Alloc(n * sizeof(T));
if (runConstructor)
for (size_t i = 0; i < n; ++i) new (&ret[i]) T();
return ret;
}
void Reset() {
currentBlockPos = 0;
availableBlocks.splice(availableBlocks.begin(), usedBlocks);
}
size_t TotalAllocated() const {
size_t total = currentAllocSize;
for (const auto& alloc : usedBlocks) total += alloc.first;
for (const auto& alloc : availableBlocks) total += alloc.first;
return total;
}
private:
MemoryArena(const MemoryArena&) = delete;
MemoryArena & operator=(const MemoryArena&) = delete;
// MemoryArena Private Data
const size_t blockSize;
size_t currentBlockPos = 0, currentAllocSize = 0;
uint8_t * currentBlock = nullptr;
std::list<std::pair<size_t, uint8_t*>> usedBlocks, availableBlocks;
};
template <typename T, int logBlockSize>
class BlockedArray {
public:
// BlockedArray Public Methods
BlockedArray(int uRes, int vRes, const T* d = nullptr)
: uRes(uRes), vRes(vRes), uBlocks(RoundUp(uRes) >> logBlockSize) {
int nAlloc = RoundUp(uRes) * RoundUp(vRes);
data = AllocAligned<T>(nAlloc);
for (int i = 0; i < nAlloc; ++i) new (&data[i]) T();
if (d)
for (int v = 0; v < vRes; ++v)
for (int u = 0; u < uRes; ++u) (*this)(u, v) = d[v * uRes + u];
}
const int BlockSize() const { return 1 << logBlockSize; }
int RoundUp(int x) const {
return (x + BlockSize() - 1) & ~(BlockSize() - 1);
}
int uSize() const { return uRes; }
int vSize() const { return vRes; }
~BlockedArray() {
for (int i = 0; i < uRes * vRes; ++i) data[i].~T();
FreeAligned(data);
}
int Block(int a) const { return a >> logBlockSize; }
int Offset(int a) const { return (a & (BlockSize() - 1)); }
T& operator()(int u, int v) {
int bu = Block(u), bv = Block(v);
int ou = Offset(u), ov = Offset(v);
int offset = BlockSize() * BlockSize() * (uBlocks * bv + bu);
offset += BlockSize() * ov + ou;
return data[offset];
}
const T & operator()(int u, int v) const {
int bu = Block(u), bv = Block(v);
int ou = Offset(u), ov = Offset(v);
int offset = BlockSize() * BlockSize() * (uBlocks * bv + bu);
offset += BlockSize() * ov + ou;
return data[offset];
}
void GetLinearArray(T * a) const {
for (int v = 0; v < vRes; ++v)
for (int u = 0; u < uRes; ++u) * a++ = (*this)(u, v);
}
private:
// BlockedArray Private Data
T * data;
const int uRes, vRes, uBlocks;
};
void* AllocAligned(size_t size) {
return _aligned_malloc(size, 32);
}
void FreeAligned(void* ptr) {
if (!ptr) return;
_aligned_free(ptr);
}
and My Source.cpp
#include <iostream>
#include <vector>
#include <chrono>
#include "Point3D.hpp"
#include "Screen.hpp"
#include "BVH.hpp"
#define N 150
int main(){
auto startTime = std::chrono::high_resolution_clock::now();
Screen* screen = new Screen(800, 600, 300);
screen->generatePoints(N);
//for (MortonPrimitive m : mortonPrims) {
// std::cout << m.mortonCode << std::endl;
//}
std::vector<std::shared_ptr<Primitive>> primitives;
primitives.reserve(N);
for (int i = 0; i < N; i++) {
primitives.emplace_back(screen->castPointToPrimitive(i));
}
BVH test(primitives);
auto endTime = std::chrono::high_resolution_clock::now();
std::cout << "Time spent: " << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() << "ms\n";
getchar();
delete screen;
}
Probably it would be wise to first cleanup your github. This mean update stuff to the recent c++ standard. It seems that you can use c++17 so use it. Also please look at some names. For example 'nodes' is used as member variable as well as parameter name, this is confusion. Please also initialize relevant (all) member variables.
Now it seems that the code in buildSAH override memory. It seems that it it can write over the end of buckets array.
I'm currently writing a BLOB detection algorithm. When I run it I get the error
Unhandled exception at 0x00007FF70476CDA7 in blobdetector.exe: 0xC0000005:
Access violation reading location 0x0000007BDDA0347D.
This is the part of the code where it breaks.
The debugger stops at the last line before the if(val == 255)
void connectivity(BlobBurnMat temp, size_t y, size_t x){
uchar* ptr = (uchar*)temp.getTemp().data;
size_t min_y = temp.getTemp().rows, min_x = temp.getTemp().cols, max_y = 0, max_x = 0;
for (int i = 0; i < 4; i++){
int kernel[4][2] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
int temp_y = y + kernel[i][0];
int temp_x = x + kernel[i][1];
uchar val = ptr[temp.getTemp().step * temp_y + temp_x]; //breaks here
if (val == 255) {
temp.setValZero(x, y);
x = temp_x;
y = temp_y;
if (temp_x > max_x)
max_x = temp_x;
if(temp_x < min_x)
min_x = temp_x;
if (temp_y > max_y)
max_y = temp_y;
if (temp_y < min_y)
min_y = temp_y;
int pass_x = ((max_x - min_x) / 2);
int pass_y((max_y - min_y) / 2);
setCoordinates(pass_x, pass_y);
connectivity(temp, y, x);
}
}
}
Here is the rest of the code. The above connectivity() method is in the BLOB class.
#include "opencv2/opencv.hpp"
#include<stdio.h>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
using namespace std;
class BlobBurnMat{
//Class used to store a copy of the source image where pixels get burned in BLOB finding.
private:
Mat temp;
public:
void initiate(Mat temp){
this -> temp = temp;
}
Mat getTemp(){
return temp;
}
void setValZero(int x, int y){
temp.at<uchar>(y, x) = 0;
}
};
class BLOB{
//BLOB class to store coordinates of the BLOBs before they are added to the vector-array-list-thingamajig
private:
int x, y;
public:
void setCoordinates(int x, int y){
this->x = x;
this->y = y;
}
int getX(){
return x;
}
int getY(){
return y;
}
void connectivity(BlobBurnMat temp){
connectivity(temp, x, y);
}
void connectivity(BlobBurnMat temp, int y, int x){
uchar* ptr = (uchar*)temp.getTemp().data;
int min_y = temp.getTemp().rows, min_x = temp.getTemp().cols, max_y = 0, max_x = 0;
for (int i = 0; i < 4; i++){
int kernel[4][2] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
int temp_y = y + kernel[i][0];
int temp_x = x + kernel[i][1];
uchar val = ptr[temp.getTemp().step * temp_y + temp_x];
if (val == 255) {
temp.setValZero(x, y);
x = temp_x;
y = temp_y;
if (temp_x > max_x)
max_x = temp_x;
if(temp_x < min_x)
min_x = temp_x;
if (temp_y > max_y)
max_y = temp_y;
if (temp_y < min_y)
min_y = temp_y;
int pass_x = ((max_x - min_x) / 2);
int pass_y((max_y - min_y) / 2);
setCoordinates(pass_x, pass_y);
connectivity(temp, y, x);
}
}
}
};
vector<Point2i> getBlobCoordinates(Mat src){
BlobBurnMat *temp = new BlobBurnMat();
temp->initiate(src.clone());
vector<Point2i> blobCoordinates; //Vector holding all BLOB coordinates
Point2i blobCoords; //Coordinates of a single BLOB
//Go through the binary matrix looking for white pixels.
for (size_t y = 0; y < src.rows; y++) {
for (size_t x = 0; x < src.cols; x++) {
uchar* ptr = (uchar*)temp->getTemp().data;
uchar val = ptr[temp->getTemp().step * y + x];
if (val == 255){
BLOB *blob = new BLOB();
blob->setCoordinates(x, y);
blob->connectivity(*temp);
blobCoords.x = blob->getX();
blobCoords.y = blob->getY();
blobCoordinates.push_back(blobCoords); //add a new element to the vector.
}
}
}
return blobCoordinates;
}
int main(int, char){
Mat src;
String path = "C:/Users/Runagar/Desktop/segment.jpg";
src = imread(path, 0);
if (src.data && !src.empty()){
imshow("blobie", src);
}
else cout << "You fucked up ";
vector <Point2i> blobCoordinates = getBlobCoordinates(src);
for (int k = 0; k < blobCoordinates.size(); k++)
cout << blobCoordinates[k];
waitKey(0);
return 0;
}
Thanks in advance!
When i == 2 and x == 0, temp_x == -1 so you read 1 byte before ptr outside the memory allocaded.
You have to handle the case when x == 0, y == 0, x == src.cols and y == src.rows.
Mat getTemp() returns a copy of your Mat instance.
uchar* ptr = (uchar*)temp.getTemp().data; retrieves a pointer from a temporary Mat instance. This instance is then immediately deleted, and you're left with a pointer to (presumably) properly deleted data.
Try Mat & getTemp() instead.
So I'm trying to make a Tetris game and I've come across something odd that I'm unsure about.
I have an array called bottom which stores the value of the lowest block - so, if there is no block in the first column, "bottom" will be 20.
If there's a square block occupying that first column, bottom would be 18. The weird thing is, when I set a breakpoint in my code to try to view the values for bottom, it says there is only one value in the array. In addition, my board, which is a 25 by 10 array, has the same problem, it only displays one dimension.
It seems the problem has to do with some kind of pointer issue, because it says (int (*)[10]) and (int *), where I think it should be a (int [25][10]) and (int [10]). I tried looking up array pointers and references, but the main thing I found was how to make an array of pointers and I'm not really quite sure how to word my searches.
If someone might know what's going wrong please let me know!
main.cpp
#include <chrono>
#include "makeboard.h"
int main() {
//declares and defines board
int board[24][10];
for (int y = 0; y < 24; y++) {
for (int x = 0; x < 10; x++) {
board[y][x] = 0;
}
}
makeboard(board);
}
tiles.h
#ifndef tiles_h
#define tiles_h
class O {
int board[24][10];
int x, y;
public:
void set_O (int[24][10], int, int);
};
void O::set_O (int board[24][10], int y, int x) {
board[y][x] = 1;
board[y][x+1] = 1;
board[y-1][x] = 1;
board[y-1][x+1] = 1;
}
class I {
int board[24][10];
int x, y, d;
public:
void set_I (int[24][10], int, int, int);
};
void I::set_I (int board[24][10], int d, int y, int x) {
if (d == 1 || d == 3) {
board[y-3][x] = 1;
board[y-2][x] = 1;
board[y-1][x] = 1;
board[y][x] = 1;
}
if (d == 2 || d == 4) {
board[y][x-1] = 1;
board[y][x] = 1;
board[y][x+1] = 1;
board[y][x+2] = 1;
}
}
class S {
int board[24][10];
int x, y, d;
public:
void set_S (int[24][10], int, int, int);
};
void S::set_S (int board[24][10], int d, int y, int x) {
if (d == 1 || d == 3) {
board[y-1][x] = 1;
board[y-1][x+1] = 1;
board[y][x] = 1;
board[y][x-1] = 1;
}
if (d == 2 || d == 4) {
board[y-2][x] = 1;
board[y-1][x] = 1;
board[y-1][x+1] = 1;
board[y][x+1] = 1;
}
}
class Z {
int board[24][10];
int x, y, d;
public:
void set_Z (int[24][10], int, int, int);
};
void Z::set_Z (int board[24][10], int d, int y, int x) {
if (d == 1 || d == 3) {
board[y][x] = 1;
board[y][x-1] = 1;
board[y+1][x] = 1;
board[y+1][x+1] = 1;
}
if (d == 2 || d == 4) {
board[y-1][x+1] = 1;
board[y][x+1] = 1;
board[y][x] = 1;
board[y+1][x] = 1;
}
}
class T {
int board[24][10];
int d, x, y;
public:
void set_T (int[24][10], int, int, int);
};
void T::set_T (int board[24][10], int d, int y, int x) {
if (d == 1 && (board[y+1][x-1] != 1 || board[y+1][x] != 1 || board[y+1][x+1] != 1)) {
board[y-1][x] = 1;
board[y][x-1] = 1;
board[y][x] = 1;
board[y][x+1] = 1;
}
if (d == 2 && (board[y+2][x] != 1 || board[y+1][x+1] != 1)) {
board[y-1][x] = 1;
board[y][x] = 1;
board[y][x+1] = 1;
board[y+1][x] = 1;
}
if (d == 3 && (board[y+1][x-1] != 1 || board[y+2][x] != 1 || board[y+1][x+1] != 1)) {
board[y][x-1] = 1;
board[y][x] = 1;
board[y][x+1] = 1;
board[y+1][x] = 1;
}
if (d == 4 && (board[y+1][x-1] != 1 || board[y+2][x] != 1)) {
board[y-1][x] = 1;
board[y][x-1] = 1;
board[y][x] = 1;
board[y+1][x] = 1;
}
}
class J {
int board[24][10];
int d, x, y;
public:
void set_J (int[24][10], int, int, int);
};
void J::set_J (int board[24][10], int d, int y, int x) {
if (d == 1) {
board[y-1][x-1] = 1;
board[y-1][x] = 1;
board[y-1][x+1] = 1;
board[y][x+1] = 1;
}
if (d == 2) {
board[y-2][x] = 1;
board[y-1][x] = 1;
board[y][x] = 1;
board[y][x-1] = 1;
}
if (d == 3) {
board[y][x-1] = 1;
board[y][x] = 1;
board[y][x+1] = 1;
board[y-1][x-1] = 1;
}
if (d == 4) {
board[y-2][x] = 1;
board[y-2][x+1] = 1;
board[y-1][x] = 1;
board[y][x] = 1;
}
}
class L {
int board[24][10];
int d, x, y;
public:
void set_L (int[24][10], int, int, int);
};
void L::set_L (int board[24][10], int d, int y, int x) {
if (d == 1) {
board[y-1][x-1] = 1;
board[y-1][x] = 1;
board[y-1][x+1] = 1;
board[y][x-1] = 1;
}
if (d == 2) {
board[y-2][x] = 1;
board[y-1][x] = 1;
board[y][x] = 1;
board[y][x-1] = 1;
}
if (d == 3) {
board[y-1][x-1] = 1;
board[y-1][x] = 1;
board[y-1][x+1] = 1;
board[y][x+1] = 1;
}
if (d == 4) {
board[y-2][x] = 1;
board[y-1][x] = 1;
board[y][x] = 1;
board[y][x+1] = 1;
}
}
#endif
makeboard.cpp
#include <iostream>
#include <limits>
#include <thread>
#include "makeboard.h"
#include "clearscreen.h"
#include "isBottom.h"
#include "isPressed.h"
#include "tiles.h"
using namespace std;
string icon[3] = { " ", " o ", " o " };
void makeboard(int board[24][10]) {
time_t srand( time(NULL) );
int block = srand % 7 ;
block = 3;
//declares pieces
O o;
I i;
S s;
Z z;
T t;
J j;
L l;
//declares and defines initial bottom
int bottom[10];
for (int i = 0; i < 10; i++) bottom[i] = 23;
//declares and defines initial block position
int y = 3;
int x = 4;
int d = 1;
while (!isBottom(board, block, y, x, d, bottom)) {
if (isPressed(0) && x > 0) {
x--;
}
if (isPressed(2) && x < 10) {
x++;
}
if (isPressed(1)) {
d += 1;
if (d == 4) {
d = 1;
}
}
//moves tile down
y++;
//clears screen
clearscreen();
//clears non set pieces
for (int i = 0; i < 24; i++) {
for (int j = 0; j < 10; j++) {
if (board[i][j] == 1) {
board[i][j] = 0;
}
}
}
//adds blocks to board
switch (block) {
case 1:
o.set_O(board, y, x);
break;
case 2:
i.set_I(board, d, y, x);
break;
case 3:
s.set_S(board, d, y, x);
break;
case 4:
z.set_Z(board, d, y, x);
break;
case 5:
t.set_T(board, d, y, x);
break;
case 6:
j.set_J(board, d, y, x);
break;
case 7:
l.set_L(board, d, y, x);
break;
}
//builds board
cout << "╔══════════════════════════════╗" << endl;
for (int i = 4; i < 24; i++) {
cout << "║";
for (int j = 0; j < 10; j++) {
cout << icon[board[i][j]] ;
}
cout << "║" << endl;
}
cout << "╚══════════════════════════════╝" << endl;
cout << " 0 1 2 3 4 5 6 7 8 9 " << endl;
//resets initial tile position
if (isBottom(board, block, y, x, d, bottom)) {
y = 2;
//block = srand % 7;
}
//ends game
if (isBottom(board, block, 3, x, d, bottom)) {
cout << "You lose!";
return;
}
//delay
this_thread::sleep_for (chrono::milliseconds(100));
}
return;
}
clearscreen.cpp
#include <unistd.h>
#include <term.h>
#include <stdlib.h>
#include "clearscreen.h"
void clearscreen()
{
if (!cur_term)
{
void *a;
int result;
setupterm( NULL, STDOUT_FILENO, &result );
a = malloc(sizeof(int) *result);
free (a);
if (result <= 0) free (a); return;
}
putp( tigetstr( "clear" ) );
}
isBottom.cpp
#include "isBottom.h"
bool isBottom(int board[24][10], int block, int y, int x, int d, int bottom[10]) {
switch (block) {
case 1:
if (y == bottom[x] || y == bottom[x+1]) {
board[y][x] = 2;
board[y][x+1] = 2;
board[y-1][x] = 2;
board[y-1][x+1] = 2;
bottom[x] -= 2;
bottom[x+1] -= 2;
return true;
}
return false;
break;
case 2:
if (d == 1 || d == 3) {
if (y == bottom[x]) {
board[y-3][x] = 2;
board[y-2][x] = 2;
board[y-1][x] = 2;
board[y][x] = 2;
bottom[x] -= 4;
return true;
}
return false;
break;
}
if (d == 2 || d == 4) {
if (y == bottom[x-1] || y == bottom[x] || y == bottom[x+1] || y == bottom[x+2]) {
board[y][x-1] = 2;
board[y][x] = 2;
board[y][x+1] = 2;
board[y][x+2] = 2;
bottom[x-1]--;
bottom[x]--;
bottom[x+1]--;
bottom[x+2]--;
return true;
}
return false;
break;
}
case 3:
if (d == 1 || d == 3) {
if (y == bottom[x-1] || y == bottom[x] || y == bottom[x+1]) {
board[y-1][x] = 2;
board[y-1][x+1] = 2;
board[y][x] = 2;
board[y][x-1] = 2;
bottom[x-1] = 23 - y;
bottom[x] -= 2;
bottom[x+1] -= 2;
return true;
break;
}
return false;
break;
}
if (d == 2 || d == 4) {
if (y == bottom[x-1] || y == bottom[x]) {
board[y-2][x] = 2;
board[y-1][x] = 2;
board[y-1][x+1] = 2;
board[y][x+1] = 2;
bottom[x-1]--;
bottom[x] -= 1;
return true;
break;
}
return false;
break;
}
/*
case 3:
s.set_S(board, d, y, x);
break;
case 4:
z.set_Z(board, d, y, x);
break;
case 5:
t.set_T(board, d, y, x);
break;
case 6:
j.set_J(board, d, y, x);
break;
case 7:
l.set_L(board, d, y, x);
break;
*/
}
return true;
}
isPressed.cpp
#include <Carbon/Carbon.h>
#include "isPressed.h"
bool isPressed( unsigned short inKeyCode )
{
unsigned char keyMap[16];
GetKeys((BigEndianUInt32*) &keyMap);
return (0 != ((keyMap[ inKeyCode >> 3] >> (inKeyCode & 7)) & 1));
}
It depends on the scope of your array. For example:
int GetBottom(int* bottom);
int GetBottom2(const int (&bottom)[20]);
int main()
{
int localArray1d[20] = {};
int localArray2d[10][25] = {};
// putting a breakpoint here will allow you to see the full dimensions of the array because this function KNOWS what the object is (e.g. a 1d and 2d array respectively)
int lastBrick = GetBottom(localArray1d);
// When the array is passed to GetBottom, it's passed just as a pointer. Although it IS an array, the function GetBottom doesn't know that. We could just as simply pass it a single int*
int n = 0;
GetBottom(&n); // here we are only passing a single int pointer. GetBottom has no idea that your object is an array, it only knows it has an int*
lastBrick = GetBottom2(localArray1d);
// GetBottom2 only takes an array of 20 elements, so inspecting the object in that function allows you to see all the elements.
return 0;
}
int GetBottom(int* bottom)
{
// Having a breakpoint here will not allow you to see all the elements in an array since this function doesn't even know bottom IS an array.
}
int GetBottom2(const int (&bottom)[20])
{
// A breakpoint here will allow you to fully inspect bottom.
}
It's a little tricky when you refer to arrays the way you do, but an array like int array[5] degrades to int* array when you branch outside the scope in which it is defined. It's because arrays are r-values and need to degrade into a reference or pointer l-value (which lacks that info about how many elements there are) to pass them around. The gotcha part here is that you can still write a function which accepts int parameter[5] and the compiler will accept it, but will silently treat it like int* parameter. The same goes for the debugger.
So depending on your debugger, there's different ways to look at all the elements through a pointer anyway. For example, with this code:
int* ptr = some_array;
... in MSVC, I can only see the first element pointed to by ptr in the watch window. However, if I know that some_array has 10 elements, I can type ptr,10 in the watch window and it'll show me all 10 elements.
Also, again this is debugger-specific, but some debuggers are conveniently programmed to show the contents of standard containers no matter what in a beautifully-readable format. So if you can use contaners like std::vector, it'll make your debugging life easier if you're using such a debugger.
I am getting these two errors while compiling but i dont understand what i didnt do right.
main.cpp|107|error: 'sqrt' was not declared in this scope|
main.cpp|107|error: 'pow' was not declared in this scope|
#include <iostream>
#include <Windows.h>
using namespace std;
struct Player
{
int x, y;
Player()
{
x = -1;
y = -1;
}
};
struct Ghost
{
int x, y, direction;
Ghost()
{
x = -1;
y = -1;
direction = 1;
}
};
const char SYMBOL_EMPTY = ' ';
const char SYMBOL_PLAYER = '#';
const char SYMBOL_GHOST = 'G';
const char SYMBOL_WALL = '#';
const int MapDx = 10;
const int MapDy = 20;
const int GameSpeed = 100;
const int LEFT = 1;
const int RIGHT = 2;
const int UP = 3;
const int DOWN = 4;
int direction = RIGHT;
char map[10][20] =
{
"###################",
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
"###################"
};
bool isValidPos(int x, int y)
{
return (x >= 0 && x < MapDx && y >= 0 && y < MapDy);
}
bool movePlayer(Player &player, int x, int y)
{
if (!isValidPos(x, y))
{
return false;
}
char ch = map[x][y];
if(ch != SYMBOL_EMPTY)
{
return false;
}
if (isValidPos(player.x, player.y))
{
map[player.x][player.y] = SYMBOL_EMPTY;
}
player.x = x;
player.y = y;
map[player.x][player.y] = SYMBOL_PLAYER;
return true;
}
bool moveGhost(Ghost &ghost, int x, int y)
{
if (!isValidPos(x, y))
{
return false;
}
char ch = map[x][y];
if (ch != SYMBOL_EMPTY)
{
return false;
}
if (isValidPos(ghost.x, ghost.y))
{
map[ghost.x][ghost.y] = SYMBOL_EMPTY;
}
ghost.x = x;
ghost.y = y;
map[ghost.x][ghost.y] = SYMBOL_GHOST;
return true;
}
void GhostAI(Ghost &ghost, Player &player)
{
double a = sqrt((pow((double) (ghost.x - 1) - player.x, 2)) + pow((double) ghost.y - player.y, 2)); //UP
double b = sqrt((pow((double) (ghost.x + 1) - player.x, 2)) + pow((double) ghost.y - player.y, 2)); //DOWN
double c = sqrt((pow((double) (ghost.y - 1) - player.x, 2)) + pow((double) ghost.x - player.y, 2)); //RIGHT
double d = sqrt((pow((double) (ghost.y + 1) - player.x, 2)) + pow((double) ghost.x - player.y, 2)); //LEFT
if(a < b && a <= c && a <= d && ghost.direction != DOWN) ghost.direction = UP;
else if(b <= c && b <= d && ghost.direction != UP) ghost.direction = DOWN;
else if(c < d && ghost.direction != LEFT) ghost.direction = RIGHT;
else if(ghost.direction != RIGHT) ghost.direction = LEFT;
}
void showMap()
{
for (int x = 0; x < MapDx; x++)
{
cout << map[x] << endl;
}
}
void showPlayer(Player &player)
{
cout << "\nPlayerX: " << player.x << endl;
cout << "PlayerY: " << player.y << endl;
}
void gameLoop()
{
Player player;
Ghost ghosts[3];
movePlayer(player, 1, 2);
moveGhost(ghosts[0], 5, 2);
moveGhost(ghosts[1], 5, 5);
moveGhost(ghosts[2], 5, 8);
while (true)
{
system("cls");
showMap();
showPlayer(player);
if (GetAsyncKeyState(VK_UP))
{
direction = UP;
}
else if (GetAsyncKeyState(VK_DOWN))
{
direction = DOWN;
}
else if (GetAsyncKeyState(VK_LEFT))
{
direction = LEFT;
}
else if (GetAsyncKeyState(VK_RIGHT))
{
direction = RIGHT;
}
switch (direction)
{
case UP:
movePlayer(player, player.x-1, player.y);
break;
case DOWN:
movePlayer(player, player.x+1, player.y);
break;
case LEFT:
movePlayer(player, player.x, player.y-1);
break;
case RIGHT:
movePlayer(player, player.x, player.y+1);
break;
}
for (int ghost = 0; ghost < 3; ghost++)
{
GhostAI(ghosts[ghost], player);
switch (ghosts[ghost].direction)
{
case UP:
moveGhost(ghosts[ghost], ghosts[ghost].x-1, ghosts[ghost].y);
break;
case DOWN:
moveGhost(ghosts[ghost], ghosts[ghost].x+1, ghosts[ghost].y);
break;
case LEFT:
moveGhost(ghosts[ghost], ghosts[ghost].x, ghosts[ghost].y-1);
break;
case RIGHT:
moveGhost(ghosts[ghost], ghosts[ghost].x, ghosts[ghost].y+1);
break;
}
}
Sleep(GameSpeed);
}
}
int main()
{
gameLoop();
return 0;
}
You need to
#include <cmath>
It's the header that declares those functions.
sqrt and pow functions are declared in the Math Library. Make sure to include them in your program at gloal scope like this.
#include <cmath>