Slowdown with recursive function using vectors - c++

I'm running this function around 30 times per frame and experiencing massive slow down. This function is the cause of it, any ideas why?
bool Pathfinding::takeStep(
std::vector<std::vector<int>>* vec, int px, int py, int sx, int sy, int r)
{
if (px == sx && py == sy) return true; // destination reached
bool clear = true;
// step up
if ((*vec)[px][py - 1] == -1) // location not tested yet
{
for (int i = 0; i < r * 2; ++i)
{
int tx = px - r + i;
int ty = py - r - 1;
// Make sure test coordinates are on the grid
if (tx > 0 && ty > 0 && tx < width && ty < height)
{
// already stuck
if (grid[tx][ty] == 1) clear = false;
}
else
clear = false; // not on grid
}
if (clear)
{
// keep moving
(*vec)[px][py - 1] = (*vec)[px][py] + 1;
// add one to step count and confirm this way is clear
if (takeStep(vec, px, py - 1, sx, sy, r)) return true;
}
else
{
(*vec)[px][py - 1] = -2; // mark as already tried
}
}
return false;
}

Related

Region Growing OpenCv c/c++ with onMouse

I am encountering an issue with my code. I aim to generate three seeds at different locations in the image and observe the pixels growing. I am using the onMouse function to select the areas, however, only the last point is being recognized. What changes do I need to make for this code to function correctly?
Pont seed,seed2,seed3;
bool clicked = false;
void onmouse(int event, int x, int y, int flags, void* userdata)
{
if (event == EVENT_LBUTTONDOWN) {
seed = Point2i(y, x);
seed2 = Point2i(y, x);
seed3 = Point2i(y, x);
clicked = true;
}
}
int main() {
string path = "C:/img_4.jpg";
Mat im_gray = imread(path, IMREAD_GRAYSCALE);
Mat im_new;
int threshold = 50;
imshow("Image", im_gray);
setMouseCallback("Image", onmouse, NULL);
while (!clicked) {
waitKey(0);
}
regionGrowing_teste1(im_gray, im_new, seed, seed2, seed3, threshold);
imshow("Segmented", im_new);
waitKey(0);
return 0;
}
Mat regionGrowing_teste1(Mat& src, Mat& dst, Point2i seed1, Point2i seed2, Point2i seed3, int threshold) {
dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
vector<Point2i> queue1, queue2, queue3;
queue1.push_back(seed1);
queue2.push_back(seed2);
queue3.push_back(seed3);
int intensity1 = (int)src.at<uchar>(seed1);
int intensity2 = (int)src.at<uchar>(seed2);
int intensity3 = (int)src.at<uchar>(seed3);
int x, y;
while (!queue1.empty() || !queue2.empty() || !queue3.empty()) {
if (!queue1.empty()) {
Point2i p = queue1.back();
queue1.pop_back();
x = p.x; y = p.y;
if (x > 0 && x < src.rows - 1 && y > 0 && y < src.cols - 1) {
if ((int)dst.at<uchar>(x, y) == 0) {
int intensityNeighbor = (int)src.at<uchar>(x, y);
if (abs(intensity1 - intensityNeighbor) < threshold) {
dst.at<uchar>(x, y) = 255;
queue1.push_back(Point2i(x + 1, y));
queue1.push_back(Point2i(x - 1, y));
queue1.push_back(Point2i(x, y + 1));
queue1.push_back(Point2i(x, y - 1));
}
}
}
}
if (!queue2.empty()) {
Point2i p = queue2.back();
queue2.pop_back();
x = p.x; y = p.y;
if (x > 0 && x < src.rows - 1 && y > 0 && y < src.cols - 1) {
if ((int)dst.at<uchar>(x, y) == 0) {
int intensityNeighbor = (int)src.at<uchar>(x, y);
if (abs(intensity2 - intensityNeighbor) < threshold) {
dst.at<uchar>(x, y) = 255;
queue2.push_back(Point2i(x + 1, y));
queue2.push_back(Point2i(x - 1, y));
queue2.push_back(Point2i(x, y + 1));
queue2.push_back(Point2i(x, y - 1));
}
}
}
}
// process points in queue3
if (!queue3.empty()) {
Point2i p = queue3.back();
queue3.pop_back();
x = p.x; y = p.y;
if (x > 0 && x < src.rows - 1 && y > 0 && y < src.cols - 1) {
if ((int)dst.at<uchar>(x, y) == 0) {
int intensityNeighbor = (int)src.at<uchar>(x, y);
if (abs(intensity3 - intensityNeighbor) < threshold) {
dst.at<uchar>(x, y) = 255;
queue3.push_back(Point2i(x + 1, y));
queue3.push_back(Point2i(x - 1, y));
queue3.push_back(Point2i(x, y + 1));
queue3.push_back(Point2i(x, y - 1));
}
}
}
}
}
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
if (dst.at<uchar>(i, j) == 0) {
dst.at<uchar>(i, j) = src.at<uchar>(i, j);
}
}
}
return dst;
}
I hope to see the placement of three points and watch them grow.

Large height map interpolation

I have a vector<vector<double>> heightmap that is dynamically loaded from a CSV file of GPS data to be around 4000x4000. However, only provides 140,799 points.
It produces a greyscale map as shown bellow:
I wish to interpolate the heights between all the points to generate a height map of the area.
The below code finds all known points will look in a 10m radius of the point to find any other known points. If another point is found then it will linearly interpolate between the 2 points. Interpolated points are defined by - height and unset values are defined as -1337.
This approach is incredibly slow I am sure there are better ways to achieve this.
bool run_interp = true;
bool interp_interp = false;
int counter = 0;
while (run_interp)
{
for (auto x = 0; x < map.size(); x++)
{
for (auto y = 0; y < map.at(x).size(); y++)
{
const auto height = map.at(x).at(y);
if (height == -1337) continue;
if (!interp_interp && height < 0) continue;
//Look in a 10m radius of a known value to see if there
//Is another known value to linearly interp between
//Set height to a negative if it has been interped
const int radius = (1 / resolution) * 10;
for (auto rxi = 0; rxi < radius * 2; rxi++)
{
//since we want to expand outwards
const int rx = x + ((rxi % 2 == 0) ? rxi / 2 : -(rxi - 1) / 2);
if (rx < 0 || rx >= map.size()) continue;
for (auto ryi = 0; ryi < radius * 2; ryi++)
{
const int ry = y + ((rxi % 2 == 0) ? rxi / 2 : -(rxi - 1) / 2);
if (ry < 0 || ry >= map.at(x).size()) continue;
const auto new_height = map.at(rx).at(ry);
if (new_height == -1337) continue;
//First go around we don't want to interp
//Interps
if (!interp_interp && new_height < 0) continue;
//We have found a known point within 10m
const auto delta = new_height - height;
const auto distance = sqrt((rx- x) * (rx - x)
+ (ry - y) * (ry - y));
const auto angle = atan2(ry - y, rx - x);
const auto ratio = delta / distance;
//Backtrack from found point until we get to know point
for (auto radi = 0; radi < distance; radi++)
{
const auto new_x = static_cast<int>(x + radi * cos(angle));
const auto new_y = static_cast<int>(y + radi * sin(angle));
if (new_x < 0 || new_x >= map.size()) continue;
if (new_y < 0 || new_y >= map.at(new_x).size()) continue;
const auto interp_height = map.at(new_x).at(new_y);
//If it is a known height don't interp it
if (interp_height > 0)
continue;
counter++;
set_height(new_x, new_y, -interp_height);
}
}
}
}
std::cout << x << " " << counter << std::endl;;
}
if (interp_interp)
run_interp = false;
interp_interp = true;
}
set_height(const int x, const int y, const double height)
{
//First time data being set
if (map.at(x).at(y) == -1337)
{
map.at(x).at(y) = height;
}
else // Data set already so average it
{
//While this isn't technically correct and weights
//Later data significantly more favourablily
//It should be fine
//TODO: fix it.
map.at(x).at(y) += height;
map.at(x).at(y) /= 2;
}
}
If you put the points into a kd-tree, it will be much faster to find the closest point (O(nlogn)).
I'm not sure that will solve all your issues, but it is a start.

Performant Threaded C++ Pixel Rendering: Fastest Way?

My goal is simple: I want to create a rendering system in C++ that can draw thousands of bitmaps on screen. I have been trying to use threads to speed up the process but to no avail. In most cases, I have actually slowed down performance by using multiple threads. I am using this project as an educational exercise by not using hardware acceleration. That said, my question is this:
What is the best way to use several threads to accept a massive list of images to be drawn onto the screen and render them at break-neck speeds? I know that I won’t be able to create a system that can rival hardware accelerated graphics, but I believe that my idea is still feasible because the operation is so simple: copying pixels from one memory location to another.
My renderer design uses three core blitting operations: position, rotation, and scale of a bitmap image. I have it set up to only rotate an image when needed, and only scale an image when needed.
I have gone through several designs for this system. All of them too slow to get the job done (300 64x64 bitmaps at barely 60fps).
Here are the designs I have tried:
Immediately drawing a source bitmap on a destination bitmap for every image on screen (moderate speed).
Creating workers that accept a draw instruction and immediately begin working on it while other workers receive their instructions also (slowest).
Workers that receive packages of several instructions at a time (slower).
Saving all drawing instructions up and then parting them up in one swoop to several workers while other tasks (in theory) are being done (slowest).
Here is the bitmap class I am using to blit bitmaps onto each other:
class Bitmap
{
public:
Bitmap(int w, int h)
{
width = w;
height = h;
size = w * h;
pixels = new unsigned int[size];
}
virtual ~Bitmap()
{
if (pixels != 0)
{
delete[] pixels;
pixels = 0;
}
}
void blit(Bitmap *bmp, float x, float y, float rot, float sclx,
float scly)
{
// Position only
if (rot == 0 && sclx == 1 && scly == 1)
{
blitPos(bmp, x, y);
return;
}
// Rotate only
else if (rot != 0 && sclx == 1 && scly == 1)
{
blitRot(bmp, x, y, rot);
return;
}
// Scale only
else if (rot == 0 && (sclx != 1 || scly != 1))
{
blitScl(bmp, x, y, sclx, scly);
return;
}
/////////////////////////////////////////////////////////////////////////////
// If it is not one of those, you have to do all three... :D
/////////////////////////////////////////////////////////////////////////////
// Create a bitmap that is scaled to the new size.
Bitmap tmp((int)(bmp->width * sclx), (int)(bmp->height * scly));
// Find how much each pixel steps:
float step_x = (float)bmp->width / (float)tmp.width;
float step_y = (float)bmp->height / (float)tmp.height;
// Fill the scaled image with pixels!
float inx = 0;
int xOut = 0;
while (xOut < tmp.width)
{
float iny = 0;
int yOut = 0;
while (yOut < tmp.height)
{
unsigned int sample = bmp->pixels[
(int)(std::floor(inx) + std::floor(iny) * bmp->width)
];
tmp.drawPixel(xOut, yOut, sample);
iny += step_y;
yOut++;
}
inx += step_x;
xOut++;
}
blitRot(&tmp, x, y, rot);
}
void drawPixel(int x, int y, unsigned int color)
{
if (x > width || y > height || x < 0 || y < 0)
return;
if (color == 0x00000000)
return;
int index = x + y * width;
if (index >= 0 && index <= size)
pixels[index] = color;
}
unsigned int getPixel(int x, int y)
{
return pixels[x + y * width];
}
void clear(unsigned int color)
{
std::fill(&pixels[0], &pixels[size], color);
}
private:
void blitPos(Bitmap *bmp, float x, float y)
{
// Don't draw if coordinates are already past edges
if (x > width || y > height || y + bmp->height < 0 || x + bmp->width < 0)
return;
int from;
int to;
int destfrom;
int destto;
for (int i = 0; i < bmp->height; i++)
{
from = i * bmp->width;
to = from + bmp->width;
//////// Caps
// Bitmap is being drawn past the right edge
if (x + bmp->width > width)
{
int cap = bmp->width - ((x + bmp->width) - width);
to = from + cap;
}
// Bitmap is being drawn past the left edge
else if (x + bmp->width < bmp->width)
{
int cap = bmp->width + x;
from += (bmp->width - cap);
to = from + cap;
}
//////// Destination Maths
if (x < 0)
{
destfrom = (y + i) * width;
destto = destfrom + (bmp->width + x);
}
else
{
destfrom = x + (y + i) * width;
destto = destfrom + bmp->width;
}
// Bitmap is being drawn past either top or bottom edges
if (y + i > height - 1)
{
continue;
}
if (destfrom > size || destfrom < 0)
{
continue;
}
memcpy(&pixels[destfrom], &bmp->pixels[from], sizeof(unsigned int) * (to - from));
}
}
void blitRot(Bitmap *bmp, float x, float y, float rot)
{
float sine = std::sin(-rot);
float cosine = std::cos(-rot);
int x1 = (int)(-bmp->height * sine);
int y1 = (int)(bmp->height * cosine);
int x2 = (int)(bmp->width * cosine - bmp->height * sine);
int y2 = (int)(bmp->height * cosine + bmp->width * sine);
int x3 = (int)(bmp->width * cosine);
int y3 = (int)(bmp->width * sine);
int minx = (int)std::min(0, std::min(x1, std::min(x2, x3)));
int miny = (int)std::min(0, std::min(y1, std::min(y2, y3)));
int maxx = (int)std::max(0, std::max(x1, std::max(x2, x3)));
int maxy = (int)std::max(0, std::max(y1, std::max(y2, y3)));
int w = maxx - minx;
int h = maxy - miny;
int srcx;
int srcy;
int dest_x;
int dest_y;
unsigned int color;
for (int sy = miny; sy < maxy; sy++)
{
for (int sx = minx; sx < maxx; sx++)
{
srcx = sx * cosine + sy * sine;
srcy = sy * cosine - sx * sine;
dest_x = x + sx;
dest_y = y + sy;
if (dest_x <= width - 1 && dest_y <= height - 1
&& dest_x >= 0 && dest_y >= 0)
{
color = 0;
// Only grab a pixel if it is inside of the src image
if (srcx < bmp->width && srcy < bmp->height && srcx >= 0 &&
srcy >= 0)
color = bmp->getPixel(srcx, srcy);
// Only this pixel if it is not completely transparent:
if (color & 0xFF000000)
// Only if the pixel is somewhere between 0 and the bmp size
if (0 < srcx < bmp->width && 0 < srcy < bmp->height)
drawPixel(x + sx, y + sy, color);
}
}
}
}
void blitScl(Bitmap *bmp, float x, float y, float sclx, float scly)
{
// Create a bitmap that is scaled to the new size.
int finalwidth = (int)(bmp->width * sclx);
int finalheight = (int)(bmp->height * scly);
// Find how much each pixel steps:
float step_x = (float)bmp->width / (float)finalwidth;
float step_y = (float)bmp->height / (float)finalheight;
// Fill the scaled image with pixels!
float inx = 0;
int xOut = 0;
float iny;
int yOut;
while (xOut < finalwidth)
{
iny = 0;
yOut = 0;
while (yOut < finalheight)
{
unsigned int sample = bmp->pixels[
(int)(std::floor(inx) + std::floor(iny) * bmp->width)
];
drawPixel(xOut + x, yOut + y, sample);
iny += step_y;
yOut++;
}
inx += step_x;
xOut++;
}
}
public:
int width;
int height;
int size;
unsigned int *pixels;
};
Here is some code showing the latest method I have tried: saving up all instructions and then giving them to workers once they have all been received:
class Instruction
{
public:
Instruction() {}
Instruction(Bitmap* out, Bitmap* in, float x, float y, float rot,
float sclx, float scly)
: outbuffer(out), inbmp(in), x(x), y(y), rot(rot),
sclx(sclx), scly(scly)
{ }
~Instruction()
{
outbuffer = nullptr;
inbmp = nullptr;
}
public:
Bitmap* outbuffer;
Bitmap* inbmp;
float x, y, rot, sclx, scly;
};
Layer Class:
class Layer
{
public:
bool empty()
{
return instructions.size() > 0;
}
public:
std::vector<Instruction> instructions;
int pixel_count;
};
Worker Thread Class:
class Worker
{
public:
void start()
{
done = false;
work_thread = std::thread(&Worker::processData, this);
}
void processData()
{
while (true)
{
controller.lock();
if (done)
{
controller.unlock();
break;
}
if (!layers.empty())
{
for (int i = 0; i < layers.size(); i++)
{
for (int j = 0; j < layers[i].instructions.size(); j++)
{
Instruction* inst = &layers[i].instructions[j];
inst->outbuffer->blit(inst->inbmp, inst->x, inst->y, inst->rot, inst->sclx, inst->scly);
}
}
layers.clear();
}
controller.unlock();
}
}
void finish()
{
done = true;
}
public:
bool done;
std::thread work_thread;
std::mutex controller;
std::vector<Layer> layers;
};
Finally, the Render Manager Class:
class RenderManager
{
public:
RenderManager()
{
workers.reserve(std::thread::hardware_concurrency());
for (int i = 0; i < 1; i++)
{
workers.emplace_back();
workers.back().start();
}
}
void layer()
{
layers.push_back(current_layer);
current_layer = Layer();
}
void blit(Bitmap* out, Bitmap* in, float x, float y, float rot, float sclx, float scly)
{
current_layer.instructions.emplace_back(out, in, x, y, rot, sclx, scly);
}
void processInstructions()
{
if (layers.empty())
layer();
lockall();
int index = 0;
for (int i = 0; i < layers.size(); i++)
{
// Evenly distribute the layers in a round-robin fashion
Layer l = layers[i];
workers[index].layers.push_back(layers[i]);
index++;
if (index >= workers.size()) index = 0;
}
layers.clear();
unlockall();
}
void lockall()
{
for (int i = 0; i < workers.size(); i++)
{
workers[i].controller.lock();
}
}
void unlockall()
{
for (int i = 0; i < workers.size(); i++)
{
workers[i].controller.unlock();
}
}
void finish()
{
// Wait until every worker is done rendering
lockall();
// At this point, we know they have nothing more to draw
unlockall();
}
void endRendering()
{
for (int i = 0; i < workers.size(); i++)
{
// Send each one an exit code
workers[i].finish();
}
// Let the workers finish and then return
for (int i = 0; i < workers.size(); i++)
{
workers[i].work_thread.join();
}
}
private:
std::vector<Worker> workers;
std::vector<Layer> layers;
Layer current_layer;
};
Here is a screenshot of what the 3rd method I tried, and it's results:
Sending packages of draw instructions
What would really be helpful is that if someone could simply point me in the right direction in regards to what method I should try. I have tried these four methods and have failed, so I stand before those who have done greater things than I for help. The least intelligent person in the room is the one that does not ask questions because his pride does not permit it. Please keep in mind though, this is my first question ever on Stack Overflow.

C++ - Pathfinding corner problems

So i'm trying to make characters walk around a tile map, using the following code. tilePath returns a vector of tiles in the path to their target, then their code makes them advance to the next tile in the vector. The problem is that when they get to a corner, they try to move around it too early and end up getting stuck on the wall.
The white squares are walkable tiles, the gray is a wall, and the red is a character. If the character gets to the corner walkable tile, it tries to move around the wall too soon, thus getting blocked by it. They usually end up making it around, but its very noticeable that they've gotten stuck on the wall for a bit.
std::vector<Tile*> TileMap::tilePath(Tile *p_start, Tile *p_end)
{
std::vector<Tile*> path;
std::vector<Tile*> open;
std::map<uint64_t, Tile*> closed;
p_start->previousTile = p_start;
p_start->g = 0;
p_start->h = 0;
p_start->f = 0;
float g, h, f, cost;
Tile* current = p_start;
Tile* previous = nullptr;
while(current != p_end) {
current->m_sprite.setColor(sf::Color(255,255,255,255));
for(auto neighbor : getNeighbors(current))
{
if(neighbor == current) continue;
if(!(neighbor->m_walkable)) continue;
previous = current;
if(tileTrace(current->previousTile, neighbor)) {
cost = manhattan(current->previousTile, neighbor);
g = current->previousTile->g + cost;
h = manhattan(neighbor, p_end);
f = g + h;
previous = current->previousTile;
} else {
cost = ((current->m_coordinates.x != neighbor->m_coordinates.x) && (current->m_coordinates.y != neighbor->m_coordinates.y)) ? 1.4 : 1;
g = current->g + cost;
h = manhattan(neighbor, p_end);
f = g + h;
}
if(std::find(open.begin(), open.end(), neighbor) != open.end() ||
closed.find(neighbor->key()) != closed.end()) {
if(neighbor->f > f) {
neighbor->f = f;
neighbor->g = g;
neighbor->h = h;
neighbor->previousTile = previous;
}
} else {
neighbor->f = f;
neighbor->g = g;
neighbor->h = h;
neighbor->previousTile = previous;
for(auto i = open.begin(); i != open.end(); i++)
{
if(neighbor->f < (*i)->f) {
open.insert(i, neighbor);
break;
}
}
open.push_back(neighbor);
}
}
closed[current->key()] = current;
if(open.size() == 0) {
return std::vector<Tile*>();
}
current = open.front();
open.erase(open.begin());
}
current = p_end;
path.insert(path.begin(), current);
while(current->previousTile != p_start)
{
current->m_sprite.setColor(sf::Color(255,0,0,255));
path.insert(path.begin(), current->previousTile);
current = current->previousTile;
}
return path;
}
bool TileMap::tileTrace(Tile* p_start, Tile* p_end)
{
int sx = p_start->m_coordinates.x;
int sy = p_start->m_coordinates.y;
int ex = p_end->m_coordinates.x;
int ey = p_end->m_coordinates.y;
int dx = fabsf(ex - sx);
int dy = fabsf(ey - sy);
int _x = sx, _y = sy;
int x_inc = (ex > sx) ? 1 : -1;
int y_inc = (ey > sy) ? 1 : -1;
int error = dx - dy;
for(int n = dx + dy; n > 0; n--) {
Tile& t = m_tiles[_y * m_width + _x];
if(!(t.m_walkable)) return false;
if(error < 0) {
_y += y_inc;
error += dx;
} else {
_x += x_inc;
error -= dy;
}
}
return true;
}
What can I do to make this a little smoother? I'd rather the characters not bounce off of the walls either.
Update:
I've added this to see if the character is moving towards or away from the tile it's "on". Granted it's more effective in getting the character around corners, but it sometimes gives a little strange movement.
sf::Vector2f d = start->m_position - p_object->m_position;
float dot = p_object->m_velocity.x * d.x + p_object->m_velocity.y * d.y;
if(dot > 0) {
path.insert(path.begin(), start);
}

Drawing lines with Bresenham's Line Algorithm

My computer graphics homework is to implement OpenGL algorithms using only the ability to draw points.
So obviously I need to get drawLine() to work before I can draw anything else. drawLine() has to be done using integers only. No floating point.
This is what I was taught. Basically, lines can be broken up into 4 different categories, positive steep, positive shallow, negative steep and negative shallow. This is the picture I am supposed to draw:
and this is the picture my program is drawing:
The colors are done for us. We are given vertices and we need to use Bresenham's Line algorithm to draw the lines based on the start and end points.
This is what I have so far:
int dx = end.x - start.x;
int dy = end.y - start.y;
//initialize varibales
int d;
int dL;
int dU;
if (dy > 0){
if (dy > dx){
//+steep
d = dy - 2*dx;
dL = -2*dx;
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; y <= end.y; y++){
Vertex v(x,y);
drawPoint(v);
if (d >= 1){
d += dL;
}else{
x++;
d += dU;
}
}
} else {
//+shallow
d = 2*dy - dx;
dL = 2*dy;
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; x <= end.x; x++) {
Vertex v(x,y);
drawPoint(v);
// if choosing L, next y will stay the same, we only need
// to update d by dL
if (d <= 0) {
d += dL;
// otherwise choose U, y moves up 1
} else {
y++;
d += dU;
}
}
}
} else {
if (-dy > dx){
cout << "-steep\n";
//-steep
d = dy - 2*dx;
//south
dL = 2*dx;
//southeast
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; y >= end.y; --y){
Vertex v(x,y);
drawPoint(v);
//if choosing L, next x will stay the same, we only need
//to update d
if (d >= 1){
d -= dL;
} else {
x++;
d -= dU;
}
}
} else {
cout << "-shallow\n";
//-shallow
d = 2*dy - dx;
dL = 2*dy;
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; x <= end.x; x++){
Vertex v(x,y);
drawPoint(v);
if (d >= 0){
d += dL;
} else {
--y;
d -= dU;
}
}
}
}
I know my error is going to be something silly, but I honestly cannot figure out what I am doing wrong. Why are some of the lines drawn incorrectly as shown above?
/*BRESENHAAM ALGORITHM FOR LINE DRAWING*/
#include<iostream.h>
#include<graphics.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<dos.h>
void bhm_line(int,int,int,int,int);
void main()
{
int ghdriver=DETECT,ghmode,errorcode,x1,x2,y1,y2;
initgraph(&ghdriver,&ghmode,"..\\bgi");
errorcode = graphresult();
if(errorcode !=grOk)
{
cout<<"Graphics error:%s\n"<<grapherrormsg(errorcode);
cout<<"Press any key to halt:";
getch();
exit(1);
}
clrscr();
cout<<"Enter the coordinates (x1,y1): ";
cin>>x1>>y1;
cout<<"Enter the coordinates (x2,y2): ";
cin>>x2>>y2;
bhm_line(x1,y1,x2,y2,1);
getch();
}
void bhm_line(int x1,int y1,int x2,int y2,int c)
{
int x,y,dx,dy,dx1,dy1,px,py,xe,ye,i;
dx=x2-x1;
dy=y2-y1;
dx1=fabs(dx);
dy1=fabs(dy);
px=2*dy1-dx1;
py=2*dx1-dy1;
if(dy1<=dx1)
{
if(dx>=0)
{
x=x1;
y=y1;
xe=x2;
}
else
{
x=x2;
y=y2;
xe=x1;
}
putpixel(x,y,c);
for(i=0;x<xe;i++)
{
x=x+1;
if(px<0)
{
px=px+2*dy1;
}
else
{
if((dx<0 && dy<0) || (dx>0 && dy>0))
{
y=y+1;
}
else
{
y=y-1;
}
px=px+2*(dy1-dx1);
}
delay(0);
putpixel(x,y,c);
}
}
else
{
if(dy>=0)
{
x=x1;
y=y1;
ye=y2;
}
else
{
x=x2;
y=y2;
ye=y1;
}
putpixel(x,y,c);
for(i=0;y<ye;i++)
{
y=y+1;
if(py<=0)
{
py=py+2*dx1;
}
else
{
if((dx<0 && dy<0) || (dx>0 && dy>0))
{
x=x+1;
}
else
{
x=x-1;
}
py=py+2*(dx1-dy1);
}
delay(0);
putpixel(x,y,c);
}
}
}
I implemented the original Bresenham's algorithm in C++ and tried to optimize as much as I could (especially regarding removing the IF from the interior loop).
It draws in a linear buffer instead of a surface, and for this matter, this implementation was almost as fast as EFLA (Extremely Fast Line Algorithm) (maybe 5% slower).
#include <vector>
#include <math.h>
using namespace std;
vector<unsigned char> buffer;
int imageSide = 2048; // the width of the surface
struct Point2Di
{
int x;
int y;
Point2Di(const int &x, const int &y): x(x), y(y){}
Point2Di(){}
};
void drawLine(const Point2Di &p0, const Point2Di &p1)
{
int dx = p1.x - p0.x;
int dy = p1.y - p0.y;
int dLong = abs(dx);
int dShort = abs(dy);
int offsetLong = dx > 0 ? 1 : -1;
int offsetShort = dy > 0 ? imageSide : -imageSide;
if(dLong < dShort)
{
swap(dShort, dLong);
swap(offsetShort, offsetLong);
}
int error = 2 * dShort - dLong;
int index = p0.y*imageSide + p0.x;
const int offset[] = {offsetLong, offsetLong + offsetShort};
const int abs_d[] = {2*dShort, 2*(dShort - dLong)};
for(int i = 0; i <= dLong; ++i)
{
buffer[index] = 255; // or a call to your painting method
const int errorIsTooBig = error >= 0;
index += offset[errorIsTooBig];
error += abs_d[errorIsTooBig];
}
}
The EFLA implementation that I am using is:
void drawLine(Point2Di p0, Point2Di p1)
{
bool yLonger=false;
int shortLen=p1.y-p0.y;
int longLen=p1.x-p0.x;
if (abs(shortLen)>abs(longLen)) {
swap(shortLen, longLen);
yLonger=true;
}
int decInc = longLen==0 ? decInc=0 : ((shortLen << 16) / longLen);
if (yLonger) {
p0.y*=imageSide;
p1.y*=imageSide;
if (longLen>0)
for (int j=0x8000+(p0.x<<16);p0.y<=p1.y;p0.y+=imageSide, j+=decInc)
buffer[p0.y + (j >> 16)] = 255; // or a call to your painting method
else
for (int j=0x8000+(p0.x<<16);p0.y>=p1.y;p0.y-=imageSide, j-=decInc)
buffer[p0.y + (j >> 16)] = 255; // or a call to your painting method
}
else
{
if (longLen>0)
for (int j=0x8000+(p0.y<<16);p0.x<=p1.x;++p0.x, j+=decInc)
buffer[(j >> 16) * imageSide + p0.x] = 255; // or a call to your painting method
else
for (int j=0x8000+(p0.y<<16);p0.x>=p1.x;--p0.x, j-=decInc)
buffer[(j >> 16) * imageSide + p0.x] = 255; // or a call to your painting method
}
}
In case anyone was wondering what the problem was, I still don't know what it was. What I ended up doing was re-factored my code so that the -shallow and -steep used the same algorithm as +shallow and +steep, respectively. After adjusting the x,y coordinates (negating the x or y coordinate), when I went to plot them I negated my original negation so that it plotted in the right spot.