|
Author |
Topic: Bresenham Algorithm (Read 1581 times) |
|
st33d
|
Bresenham Algorithm
« on: Oct 17th, 2004, 2:43am » |
|
I'm writing a function that will scan the values of a line of pixels as opposed to one (get() but across a line). Since I'm going to be calling this more often than a teenage girl calls her mates I thought that the Bresenham Algorithm might be the way forward to get things done speedily. In a previous post I was directed to a site where some guy had put together a lot of his equations without brackets or explanation. If you're a bit slow like I am then that's no use at all. You can see this at: http://mandelbrot.dazibao.free.fr/Bresen/Bresen.htm 2 * (Xk+1) * Dy + 2 * y1 * Dx - 2 * x1 * Dy - 2 * Dx * Yk - Dx > 0 What the hell does that mean? Do I add two and then multiply by y1? Gibberish! Sorry fjen but that page is really confusing. Its logical when you know how its done but if you don't you can't make assumptions. For morons like me, this explanation is much easier to digest: http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html But what does that look like in Processing? My guess was this: Code: //the Bresenham algorithm //in Processing yd=y2-y1; xd=x2-x1; e=0; y=y1; for (int x=x1; x=<x2; x++){ point(x,y); if((2*(e+yd))<xd){ e+=yd; }else{ y++; e+=(yd-xd); } }//for; |
| Question 1: Have I got that right? Question 2: Is there a more efficient way of phrasing it? (Perhaps accounting for the fact that I'm going to have to write a different version for each octant it runs in.)
|
« Last Edit: Oct 17th, 2004, 2:44am by st33d » |
|
I could murder a pint.
|
|
|
Markavian
|
Re: Bresenham Algorithm
« Reply #1 on: Oct 17th, 2004, 3:37am » |
|
I rewrote the code from the first example, and all it drew was part of the circle: Code:// Bresen Curve colorMode(RGB, 100, 100, 100); size(100, 100); background(100, 100, 100); int rad = 40; int x = 0; int y = rad; int s = 1 - rad; color black = color(0, 0, 0); while(x < y) { if(s < 0) { s += 2 * x + 3; } else { s = s + 2 * (x - y) + 5; y--; } x++; set(50+x, 50+y, black); } |
|
|
|
|
|
st33d
|
Re: Bresenham Algorithm
« Reply #2 on: Oct 17th, 2004, 12:35pm » |
|
Yeah, the guy says you have to copy it round to create the rest of the circle. The code he lays out just gives you one octant. That's half the trouble with Bresen, you get 45 degrees of functionality and then you have to tweak the code to deal with the next 45 and so on. The upside is the computer loves you for not doing any division, trigonometry or using floats that take up twice the amount of memory as integers. Here's my code with the graphics. Code: //the Bresenham algorithm //in Processing int x1=0; int y1=0; int x2=80; int y2=40; int yd=y2-y1; int xd=x2-x1; int e=0; int y=y1; for (int x=x1; x<=x2; x++){ point(x,y); if((2*(e+yd))<xd){ e+=yd; }else{ y++; e+=(yd-xd); } }//for; |
| The second example mentioned using a left shift ("<<" perhaps) to remove the multiplication in the code. I'm not familiar with this command, does it work in java? And will my multiplication slow the computer down any? I ask because I'm going to be calling this function many, many times (like thousands and possibly millions).
|
I could murder a pint.
|
|
|
Markavian
|
Re: Bresenham Algorithm
« Reply #3 on: Oct 17th, 2004, 1:44pm » |
|
You're asking would this method be faster then the standard draw functions in Processing? I don't think so, because they have probably already optimized their code. E.g. if you compare my bresen circle code below against the ellipse function, they draw to exactly the same pixels - which suggests that they use the same code. Code: // Bresen Curve color black; int radius; void setup() { colorMode(RGB, 100, 100, 100); color black = color(0, 0, 0); ellipseMode(CENTER_DIAMETER); size(200, 200); background(100, 100, 100); } void loop() { background(100, 100, 100); radius = abs(mouseX-width/2); stroke(100, 0, 0); ellipse(width/2, width/2, radius*2, radius*2); drawBresenCircle(radius, width/2, height/2, black); } void drawBresenCircle(int rad, int cx, int cy, color lineCol) { int a = 0, x = 0, y = rad, s = 1 - rad; while(x < y) { setSymmetry(x, cx, y, cy, lineCol); setSymmetry(y, cx, x, cy, lineCol); if(s < 0) { s += 2 * x + 3; } else { s = s + 2 * (x - y) + 5; y--; } x++; } setSymmetry(x, cx, y, cy, lineCol); } void setSymmetry(int x, int cx, int y, int cy, color lineCol) { set(cx+x, cy+y, lineCol); set(cx+x, cy-y, lineCol); set(cx-x, cy+y, lineCol); set(cx-x, cy-y, lineCol); } |
|
|
« Last Edit: Oct 17th, 2004, 1:47pm by Markavian » |
|
|
|
|
st33d
|
Re: Bresenham Algorithm
« Reply #4 on: Oct 17th, 2004, 2:43pm » |
|
That's not what I'm asking (read my first post a little more carefully). I already understand that the Bresenham algorithms are probably implemented in most of the graphics functions. I intend to use the same algorithm to SCAN a line of pixels. Not draw them. I'm going to be scanning some large bitmaps using these algorithms. That's why I'm asking if my syntax is efficient enough and if there is a way to optimise it further. The master plan is to break down a bitmap into all of the possible vector lines you can imagine. That's a pretty big job (even if I'm just working on a black and white bitmap which is where I'm going to start). I can then create a class of objects that counts how often pixels are over used and begin to write some kind of filter. If you think about the thousands of ways we can communicate an image through lines its pretty mind boggling. What I intend to do is write a program which catalogues just that. Then I could expand the vector image and see what the mesh looks like for starters. Its part of my work to make a machine artist. This has already been done with Harold Cohen's AARON. But AARON is a hermetic system that can't interpret the outside world. I want something that does. And this is just one of my experiments in that line. You can take a look at AARON here: http://www.kurzweilcyberart.com/ So, can my Bresen code be optimised further or not?
|
I could murder a pint.
|
|
|
fjen
|
Re: Bresenham Algorithm
« Reply #5 on: Oct 17th, 2004, 3:56pm » |
|
mini optimization (shift) Code: // shift examples: int a = 5; println(a>>1); // devide by 2, carfull it's int, same as: (int)(a/2) println(a<<1); // times 2, same as: a*2 //the Bresenham LINE algorithm //in Processing int x1=0; int y1=0; int x2=80; int y2=40; int yd=y2-y1; int xd=x2-x1; int e=0; int y=y1; for (int x=x1; x<=x2; x++) { point(x,y); // shift-left by 1 is the same as *2 for int // if( ((e+yd)<<1) < xd ) { e+=yd; }else{ y++; e+=(yd-xd); } } |
| here's a list of most known line algorithms (+source): http://www.edepot.com/algorithm.html he claims to have the fastest line algorithm but there are tests on the net (check google) that show that in some cases it's not. sorry you didn't like the site i posted for you last time. it was just ment to get you started and see how far you've got ..! /F
|
|
|
|
elout
|
Re: Bresenham Algorithm
« Reply #6 on: Oct 17th, 2004, 5:45pm » |
|
for speed performance, writing directly into the screenbuffer; Code: pixels[(y*width)+x] = ( ( my_red << 16) | ( my_green << 8) | my_blue); |
| is still 2 till 10 times faster then when using set() or point()
|
|
|
|
st33d
|
Re: Bresenham Algorithm
« Reply #7 on: Oct 17th, 2004, 6:03pm » |
|
Cheers, that hit the spot. I'd seen shiftyness before didn't understand its use. I think I'll stick with the good old Bresen for the time being. The Wu Line is interesting stuff, something to consider when I try to move this project into colour. ...Hrmm, good point. I'm going to treat this function like a boolean to scan for a line of black pixels to start with. I was going to use get() for my line colour evaluator. Or should I be using pixel[]? Which is going to be faster?
|
« Last Edit: Oct 17th, 2004, 6:10pm by st33d » |
|
I could murder a pint.
|
|
|
fjen
|
Re: Bresenham Algorithm
« Reply #8 on: Oct 18th, 2004, 4:55am » |
|
use pixels ... like: int px = pixels[x + y*width]; // int alpha = ( px >> 24 ) & 0xFF; int red = ( px >> 16 ) & 0xFF; int green = ( px >> 8 ) & 0xFF; int blue = px & 0xFF; sorry, was kinda sleepy this morning .. /F
|
|
|
|
st33d
|
Re: Bresenham Algorithm
« Reply #9 on: Oct 18th, 2004, 11:26pm » |
|
Having some serious trouble getting my head around this. I have come across a bug in my algorithm that sends the scanner off screen. I really can't see what's going wrong here. The following code is in debug state which is why a blue line scans across the screen. I have remindered out the AIExport requests but its not them that's giving me grief just now. Its something in the way I've written my Bresenhams. I've pinpointed the source of the bug with printlns but I'm not sure how to deal with it. I'd be grateful if anyone can help. Code: int i=0; //AIExport ai; void setup(){ i=0; size (200,200); background (255); //BImage scan = loadImage ("test.jpg"); //image (scan,0,0); // ai = new AIExport( this, 1 ); // ai.toggleContinuousRecording(); //ai.ai_line fill(0); rect(5,5,100,100); } void loop(){ if (i<pixels.length){ if (pixels[i]==#000000){bscan(i);} println(i); pixels[i]=#0000ff; i++; } if (i==pixels.length){ //ai.toggleContinuousRecording(); i++; println("done"); } }//loop; void bscan(int it){ //int px = pixels[x + y*width]; int x1=(it)%width; int y1=it/height; int xd; int yd; int oct=0; boolean d; color c=#000000; for (int y2=0; y2<height; y2++){ for (int x2=0; x2<width; x2++){ println("x1:"+x1+"y1:"+y1+"x2:"+x2+"y2:"+y2); d=false; xd=x2-x1; yd=y2-y1; if ((x2>x1)&&(y2>y1)&&(abs(yd)<=abs(xd))){oct=0;} if ((x2>=x1)&&(y2>y1)&&(abs(yd)>abs(xd))){oct=1;} if ((x2<x1)&&(y2>y1)&&(abs(yd)>=abs(xd))){oct=2;} if ((x2<x1)&&(y2>=y1)&&(abs(yd)<abs(xd))){oct=3;} if ((x2<x1)&&(y2<y1)&&(abs(yd)<=abs(xd))){oct=4;} if ((x2<=x1)&&(y2<y1)&&(abs(yd)>abs(xd))){oct=5;} if ((x2>x1)&&(y2<y1)&&(abs(yd)>=abs(xd))){oct=6;} if ((x2>x1)&&(y2<=y1)&&(abs(yd)<abs(xd))){oct=7;} //the Bresenham algorithms //octant 0 print("octant 0"); if (oct==0){ int e=0; int y=y1; for (int x=x1; x<=x2; x++){ if(((e+yd)<<1)<xd){ e+=yd; }else{ y++; e+=(yd-xd); } if (pixels[x + y*width]==c){d=true;}else{d=false;} }//for; } print("octant 1"); //octant 1 if (oct==1){ int e=0; int x=x1; for (int y=y1; y<=y2; y++){ if(((e+xd)<<1)<yd){ e+=xd; }else{ x++; e+=(xd-yd); } if (pixels[x + y*width]==c){d=true;}else{d=false;} }//for; } print("octant 2"); //octant 2 if (oct==2){ int e=0; int x=x1; for (int y=y1; y<=y2; y++){ if(((e+xd)<<1)>(0-yd)){ e+=xd; }else{ x--; e+=(xd+yd); } if (pixels[x + y*width]==c){d=true;}else{d=false;} }//for; } print("octant 3"); //octant 3 if (oct==3){ xd=abs(xd); int e=0; int y=y1; for (int x=0; x<=xd; x++){ if(((e+yd)<<1)<xd){ e+=yd; }else{ y++; e+=(yd-xd); } if (pixels[(x1-x) + y*width]==c){d=true;}else{d=false;} }//for; } print("octant 4"); //octant 4 if (oct==4){ xd=abs(xd); int e=0; int y=y1; for (int x=0; x<=xd; x++){ if(((e+yd)<<1)>(0-xd)){ e+=yd; }else{ y--; e+=(yd+xd); } println((x1-x) + y*width); println("x"+x+"y"+y); if (pixels[(x1-x) + y*width]==c){d=true;}else{d=false;} }//for; } print("octant 5"); //octant 5 if (oct==5){ yd=abs(yd); int e=0; int x=x1; for (int y=0; y<=yd; y++){ if(((e+xd)<<1)>(0-yd)){ e+=xd; }else{ x--; e+=(xd+yd); } if (pixels[x + (y1-y)*width]==c){d=true;}else{d=false;} }//for; } print("octant 6"); //octant 6 if (oct==6){ yd=abs(yd); int e=0; int x=x1; for (int y=0; y<=yd; y++){ if(((e+xd)<<1)<yd){ e+=xd; }else{ x++; e+=(xd-yd); } if (pixels[x + (y1-y)*width]==c){d=true;}else{d=false;} }//for; } print("octant 7"); //octant 7 if (oct==7){ int e=0; int y=y1; for (int x=x1; x<=x2; x++){ if(((e+yd)<<1)>(0-xd)){ e+=yd; }else{ y--; e+=(yd+xd); } if (pixels[x + y*width]==c){d=true;}else{d=false;} }//for; } //if(d){ai.ai_line(x1,y1,x2,y2);} }//xfor; }//yfor; }//bscan; |
|
|
I could murder a pint.
|
|
|
fjen
|
Re: Bresenham Algorithm
« Reply #10 on: Oct 19th, 2004, 12:18am » |
|
ufff ... you are making this really complicated. some thoughts: (-1-) first: if you are just reading horizontal or vertical lines using bresenham makes no sense. just read the pixels like: Code: horizontalRead (int x1,int y1, int x2,int y2) { int yy = y1*width; for (int xx=x1; xx<=x2; xx++) int currentPixel = pixels[xx+yy]; // x1 < x2 and y1 == y2 in this case! } verticalRead (int x1,int y1, int x2,int y2) { for (int yy=y1; yy<=y2; yy++) int currentPixel = pixels[x1+yy*width]; // y1 < y2 and x1 == x2. } |
| (-2-) then, i see only four cases for the algorithm: Code: void lineB (int x1,int y1, int x2,int y2) { // case1: x1 == x2 -> vertical line // case2: y1 == y2 -> horizontal line // case3: ((x1 < x2) & (y1 < y2)) | ((x1 > x2) & (y1 > y2)) -> downward line (upper left to lower right) // case4: anything else -> upward line (lower left to upper right) // make sure to flip point1 and point2 in case3 if point1 is lower right .. // same for case4 if point1 is to the upper right. } |
| what do you think? /F
|
« Last Edit: Oct 19th, 2004, 12:19am by fjen » |
|
|
|
|
st33d
|
Re: Bresenham Algorithm
« Reply #11 on: Oct 19th, 2004, 12:02pm » |
|
Its not necessary to optimise the code with a vertical and horizontal case if the the Bresenhams don't work. I was testing to see if they did. I originally wrote a "break" into the routines to stop the scans short of doing a pointless pass across a white area. Which is why i didn't swap x1 and x2 over and so on. Its fine to do that if you're drawing a line but if you're sniffing out black lines why carry on if there's nothing in front? Bresenham algorithms only work in one octant. That's one of eight possible areas. If I work from top left to bottom right, because I'm using a gradient, as soon as I pass the diagonal where x and y are the same the algorithm just draws a diagonal line. I have to swap the stepping up from x to y and reverse the condition of moving x or y the same. eg: Code: //This code don't work 'cos its in the wrong octant int x1=0; int y1=0; int x2=70; int y2=100; int yd=y2-y1; int xd=x2-x1; int e=0; int y=y1; for (int x=x1; x<=x2; x++){ point(x,y); if(((e+yd)<<1)<xd){ e+=yd; }else{ y++; e+=(yd-xd); } }//for; |
| Which means 6 cases instead of 4 if I include the horizontal and vertical. I also have to write the for loop a little differently to get it to go backwards as well. Worst of all I wouldn't be able to stop sniffing out a direction halfway. Update: (sound of head smacking against table repeatedly) I put the pixel scan after the increment and not after the for loop command. That's why it's going off screen. Update: Ah, no. It still goes off screen. Now I really haven't a clue. I've slimmed everything down but don't understand what's going wrong. Even the line that should only draw over the black areas is going way off course and creating a black band down the middle. I'm about to have an aneurism here. Code: int i=0; void setup(){ size (100,60); background (255); fill(0); rect(20,20,20,20); } void loop(){ if (i<pixels.length){ if (pixels[i]==#000000){bscan(i);} println(i); pixels[i]=#0000ff; i++; } if (i==pixels.length){ i++; println("done"); } }//loop; void bscan(int it){ int x1=(it)%width; int y1=it/height; int xd; int yd; int oct=0; boolean d; color c=#000000; for (int y2=0; y2<height; y2++){ for (int x2=0; x2<width; x2++){ d=false; xd=x2-x1; yd=y2-y1; int e=0; int y=y1; for (int x=x1; x<=x2; x++){ if (pixels[x + y*width]==c){d=true;}else{d=false;} if(((e+yd)<<1)<xd){ e+=yd; }else{ y++; e+=(yd-xd); } }//for; if(d){line(x1,y1,x2,y2);}//where the ai.line should go }//xfor; }//yfor; }//bscan; |
|
|
« Last Edit: Oct 19th, 2004, 3:35pm by st33d » |
|
I could murder a pint.
|
|
|
fjen
|
Re: Bresenham Algorithm
« Reply #12 on: Oct 19th, 2004, 5:37pm » |
|
jikes ... you're right. 6 cases that is ... octants/2 + horizontal + vertical. here's my code: Code: Point2D p1, p2; void setup() { size(200,200); p1 = new Point2D((int)random(width-1), (int)random(height-1)); p2 = new Point2D((int)random(width-1), (int)random(height-1)); } void loop() { background(99); p1.draw(); p2.draw(); lineB( p1.x, p1.y, p2.x, p2.y); } // // // // // // // // Bresenham Line Algorithm ////////////////////////////////////////////////// int lineB (int x1,int y1, int x2,int y2) { // case1: x1 == x2 -> vertical line // case2: y1 == y2 -> horizontal line // case3: ((x1 < x2) & (y1 < y2)) | ((x1 > x2) & (y1 > y2)) -> downward line (upper left to lower right) // case4: anything else -> upward line (lower left to upper right) // CASE 1 if (x1 == x2) { return (y1 < y2 ? lineV(x1, y1, x2, y2) : lineV(x1, y2, x2, y1)); } // CASE 2 if (y1 == y2) { return (x1 < x2 ? lineH(x1, y1, x2, y2) : lineH(x2, y1, x1, y2)); } int yd=y2-y1; int xd=x2-x1; int c = color(0,100,0); // CASE 3 if ((xd > 0) == (yd > 0)) { //println("LineDownwards (UL-LR): "+xd+" "+yd+"\t| "+x2+"-"+x1+", "+y2+"-"+y1); if (xd < 0) { // flip start and end-point // xd = -xd; yd = -yd; int xxx = x1; x1 = x2; x2 = xxx; int yyy = y1; y1 = y2; y2 = yyy; c = color(100,255,0); } int e=0; if (abs(xd) > abs(yd)) { int y=y1; for (int x=x1; x<=x2; x++) { pixels[x+y*width] = c; //(((0x00<<8)+0x00)<<8)+0x00; // _RGB if( ((e+yd)<<1) < xd ) { e+=yd; }else{ y++; e+=(yd-xd); } } } else { int x=x1; for (int y=y1; y<=y2; y++) { pixels[x+y*width] = c; //(((0x00<<8)+0x00)<<8)+0x00; // _RGB if( ((e+xd)<<1) < yd ) { e+=xd; }else{ x++; e+=(xd-yd); } } } } // CASE 4 else { //println("LineUpwards (LL-UR): "+xd+" "+yd+"\t| "+x2+"-"+x1+", "+y2+"-"+y1); c = color(0,0,100); if (xd < 0) { xd = -xd; int xxx = x1; x1 = x2; x2 = xxx; c = color(0,255,100); } else { yd = -yd; int yyy = y1; y1 = y2; y2 = yyy; } int e=0; if (abs(xd) > abs(yd)) { int y=y1; for (int x=x1; x<=x2; x++) { pixels[x+(y1+(y2-y))*width] = c; //(((0x00<<8)+0x00)<<8)+0x00; // _RGB if( ((e+yd)<<1) < xd ) { e+=yd; }else{ y++; e+=(yd-xd); } } } else { int x=x1; for (int y=y1; y<=y2; y++) { pixels[x+(y1+(y2-y))*width] = c; //(((0x00<<8)+0x00)<<8)+0x00; // _RGB if( ((e+xd)<<1) < yd ) { e+=xd; }else{ x++; e+=(xd-yd); } } } } return -1; } int lineH (int x1,int y1, int x2,int y2) { int yy = y1*width; for (int xx=x1; xx<=x2; xx++) pixels[xx+yy] = (((255<<8)+0)<<8)+0; // _RGB // x1 < x2 and y1 == y2 in this case! //println("HLine"); return -1; } int lineV (int x1,int y1, int x2,int y2) { for (int yy=y1; yy<=y2; yy++) pixels[x1+yy*width] = (((255<<8)+255)<<8)+0; // _RGB // y1 < y2 and x1 == x2. //println("VLine"); return -1; } // // // // // // // // handle Mouse Input //////////////////////////////////////////// Point2D cp; void mousePressed () { if (p1.isIn(mouseX, mouseY)) cp = p1; else if (p2.isIn(mouseX, mouseY)) cp = p2; else cp = null; } void mouseDragged () { if (cp != null) cp.setP(mouseX, mouseY); } void mouseReleased () { cp = null; } // // // // // // // // POINT2D ///////////////////////////////// class Point2D { int x, y; Point2D (int _x, int _y) { setP(_x, _y); } void setP (int _x, int _y) { if ((_x >= 0 && _x < width) && (_y >= 0 && _y < height)) { this.x = _x; this.y = _y; } } boolean isIn (int xx, int yy) { if (abs(xx-x)<5 & abs(yy-y)<5) return true; return false; } void draw() { ellipseMode(CENTER_DIAMETER); ellipse(this.x,this.y, 5, 5); } } |
|
|
|
|
|
fjen
|
Re: Bresenham Algorithm
« Reply #13 on: Oct 19th, 2004, 5:41pm » |
|
... as you can see, both case 3 & 4 have two subcases ... /F
|
|
|
|
st33d
|
Re: Bresenham Algorithm
« Reply #14 on: Oct 19th, 2004, 7:34pm » |
|
(More table head smacky) Found my culprit. Code: int i=0; int x; int y; void setup(){ size (100,60); background (255); } void loop(){ if (i<pixels.length){ println(i); x=(i)%width; y=i/height; // <----WRONG!! stroke(255,0,0);point(x,y); pixels[i]=#0000ff; } if (i==pixels.length){ i++; println("done"); } i++; }//loop; |
| x=(i)%width; y=i/height; So near yet so wrong. Can't figure out what "y" is from the pixels[] index though. Who knows, if I get the y sorted I may have something to bring into class tomorrow to show teacher. Might even work with my slow code. Thanks for the example though fjen. If I make progress then I'll be able to print it out and pick out the bones for the scanner. It would have to be such a pain to SCAN instead of DRAW. Update: i/width;
|
« Last Edit: Oct 19th, 2004, 7:59pm by st33d » |
|
I could murder a pint.
|
|
|
|