package fr.unistra.pelican.util; import java.awt.Color; import java.awt.Point; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Iterator; import fr.unistra.pelican.DoubleImage; import fr.unistra.pelican.Image; import fr.unistra.pelican.algorithms.visualisation.MViewer; /** * @author Benjamin Perret * This Line represents a line segment in (x, y) coordinate space. * It provides an iterator on its discreet representation (Bresenham algorithm.) */ public class Line implements Iterator<Point>, Iterable<Point>{ /* type */ final static int VECT_HG = 0; final static int VECT_HD = 1; final static int VECT_VH = 2; final static int VECT_VB = 3; final static int OCTANT1 = 4; final static int OCTANT2 = 5; final static int OCTANT3 = 6; final static int OCTANT4 = 7; final static int OCTANT5 = 8; final static int OCTANT6 = 9; final static int OCTANT7 = 10; final static int OCTANT8 = 11; /* descibre line by two points */ private Point x1,x2; /* iterator flag */ boolean first=true; /* this is for Bresenham algorithm implementation*/ private int type; private int x,y; private int incr1,incr2; private int dx,dy; private int d; public Line (int x1, int y1, int x2, int y2) { this(new Point(x1,y1), new Point(x2,y2)); } /* Construct new segment [x1;x2] */ public Line (Point x1, Point x2) { this.x1=new Point(x1); this.x2=new Point(x2); x=this.x1.x; y=this.x1.y; dx=this.x2.x-x; dy=this.x2.y-y; incr1=2*dx; incr2=2*dy; if (dx != 0) { if (dx > 0) { if (dy != 0) { if (dy > 0) { // vecteur oblique dans le 1er quadran if (dx >= dy) {// vecteur diagonal ou oblique proche de l horizontale, dans le 1er octant type=OCTANT1; d=dx; } else { // vecteur oblique proche de la verticale, dans le 2nd octant d=dy; type=OCTANT2; } } else { // dy < 0 (et dx > 0) // vecteur oblique dans le 4e cadran if ( dx >= -dy) { // vecteur diagonal ou oblique proche de l horizontale, dans le 8e octant d=dx; type=OCTANT8; } else { // vecteur oblique proche de la verticale, dans le 7e octant d=dy; type=OCTANT7; } } } else { // dy = 0 (et dx > 0) // vecteur horizontal vers la droite type=VECT_HD; } } else // dx < 0 if (dy != 0) { if (dy > 0) { // vecteur oblique dans le 2nd quadran if( -dx >= dy) { // vecteur diagonal ou oblique proche de l horizontale, dans le 4e octant d=dx; type=OCTANT4; } else { // vecteur oblique proche de la verticale, dans le 3e octant d=dy; type=OCTANT3; } } else // dy < 0 (et dx < 0) { // vecteur oblique dans le 3e cadran if ( dx <= dy ) { // vecteur diagonal ou oblique proche de l horizontale, dans le 5e octant d=dx; type=OCTANT5; } else { // vecteur oblique proche de la verticale, dans le 6e octant d=dy; type=OCTANT6; } } } else {// dy = 0 (et dx < 0) type=VECT_HG; // vecteur horizontal vers la gauche } } else { // dx = 0 if (dy != 0) { if ( dy > 0 ) { type=VECT_VH; } else // dy < 0 (et dx = 0) { // vecteur vertical decroissant* type=VECT_VB; } } } } /* * Returns the starting Point of this Line. */ public Point getX1() { return x1; } /* * Returns the ending Point of this Line. */ public Point getX2() { return x2; } /* * Initialise Iterator */ public void initIterator() { first=true; x=x1.x; y=x1.y; } /* * To get the next point on Breseham representation. */ public Point next() { if(!first) switch(type) { case VECT_HG: x--; break; case VECT_HD: x++; break; case VECT_VH: y++; break; case VECT_VB: y--; break; case OCTANT1: x++; d=d-incr2; if( d < 0 ) { y++; d+=incr1; } break; case OCTANT2: y++; d=d-incr1; if( d < 0 ) { x++; d+=incr2; } break; case OCTANT3: y++; d=d+incr1; if( d <= 0 ) { x--; d+=incr2; } break; case OCTANT4: x--; d=d+incr2; if( d >=0 ) { y++; d+=incr1; } break; case OCTANT5: x--; d=d-incr2; if( d >=0 ) { y--; d+=incr1; } break; case OCTANT6: y--; d=d-incr1; if( d >=0 ) { x--; d+=incr2; } break; case OCTANT7: y--; d=d+incr1; if( d > 0 ) { x++; d+=incr2; } break; case OCTANT8: x++; d=d+incr2; if( d < 0 ) { y--; d+=incr1; } break; } else first=false; return (new Point(x,y)); } /** * To ensure iterator still has a Point to return. */ public boolean hasNext() { boolean res= true; if ( x==x2.x && y == x2.y ) res=false; return res; } /** * Draw the line segment on an image (single band) pixels are set to 255 * @param im */ public void drawGrayLine(Image im) { drawGrayLine(im, 255); } /** * Draw the line segment on an image (single band) pixels are set to level * @param im * @param level */ public void drawGrayLine(Image im, int level) { initIterator(); int xDim=im.getXDim(); int yDim=im.getYDim(); if(x>=0 && x<xDim && y>=0 && y<yDim) im.setPixelXYByte(x,y,level); while(hasNext()) { Point p=next(); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) { im.setPixelXYByte(p.x,p.y,level); } } } /** * Draw the line segment on an image (single band) pixels are set to level * @param im * @param band number of band to draw in * @param level */ public void drawGrayLine(Image im,int band, int level) { initIterator(); int xDim=im.getXDim(); int yDim=im.getYDim(); if(x>=0 && x<xDim && y>=0 && y<yDim) im.setPixelXYBByte(x,y,band,level); while(hasNext()) { Point p=next(); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) { im.setPixelXYBByte(p.x,p.y,band,level); } } } /** * Draw the line segment on an image (single band) pixels are set to level * @param im * @param band number of band to draw in * @param level */ public void drawGrayLine(Image im,int band, double level) { initIterator(); int xDim=im.getXDim(); int yDim=im.getYDim(); if(x>=0 && x<xDim && y>=0 && y<yDim) im.setPixelXYBDouble(x,y,band,level); while(hasNext()) { Point p=next(); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) { im.setPixelXYBDouble(p.x,p.y,band,level); } } } public void drawColorLine(Image im,Color c) { initIterator(); //im.setPixelXYByte(x,y,255); int xDim=im.getXDim(); int yDim=im.getYDim(); while(hasNext()) { Point p=next(); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) { im.setPixelXYBByte(p.x,p.y,0,c.getRed()); im.setPixelXYBByte(p.x,p.y,1,c.getGreen()); im.setPixelXYBByte(p.x,p.y,2,c.getBlue()); } } } /** * Draw the line segment on an image (single band) pixels values are interpolate between level1 and 2 * @param im * @param band number of band to draw in * @param level1 * @param level2 */ public void drawLine(Image im,int band, double level1, double level2) { int xDim=im.xdim; int yDim=im.ydim; double l=length(); double counter=0.0; double s=1.0/l; //if(x>=0 && x<xDim && y>=0 && y<yDim) // im.setPixelXYBDouble(x,y,band,level1); initIterator(); while(hasNext()) { Point p=next(); //System.out.println("draw " +p); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) im.setPixelXYBDouble(p.x,p.y,band,(level1*(1.0-s)+level2*s)); counter+=s; } } /** * Draw the line segment on an image (single band) pixels values are interpolate between level1 and 2 * @param im * @param band number of band to draw in * @param level1 * @param level2 */ public void drawLineMin(Image im,int band, double level1, double level2) { int xDim=im.xdim; int yDim=im.ydim; double l=length(); double counter=0.0; double s=1.0/l; //if(x>=0 && x<xDim && y>=0 && y<yDim) // im.setPixelXYBDouble(x,y,band,level1); initIterator(); while(hasNext()) { Point p=next(); //System.out.println("draw " +p); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) im.setPixelXYBDouble(p.x,p.y,band,Math.min(im.getPixelXYBDouble(p.x,p.y,band), (level1*(1.0-s)+level2*s))); counter+=s; } } /** * Compute intersections with the line passing through p1 and p2 and a box, mais be subject to rounding errors... * * @param box * @return */ public Point [] intersections(Box box) { double a=((double)(x1.y-x2.y))/((double)(x1.x-x2.x)); double b=(double)x1.y-a*(double)x1.x; double x1=box.x1.x; double x2=box.x2.x; double y1=box.x1.y; double y2=box.x2.y; if(x1>x2) { double tmp=x1; x1=x2; x2=tmp; } if(y1>y2) { double tmp=y1; y1=y2; y2=tmp; } ArrayList<Point> list=new ArrayList<Point>(); // first segment y=y1, x1<=x<=x2 double xx=(y1-b)/a; if(x1<=xx && xx<=x2) list.add(new Point((int)(xx+0.5),(int)y1)); // second segment y=y2, x1<=x<=x2 xx=(y2-b)/a; if(x1<=xx && xx<=x2) list.add(new Point((int)(xx+0.5),(int)y2)); // third segment x=x1, y1<=y<=y2 double yy=a*x1+b; if(y1<yy && yy<y2) list.add(new Point((int)(x1),(int)(yy+0.5))); // fourth segment x=x2, y1<=y<=y2 yy=a*x2+b; if(y1<yy && yy<y2) list.add(new Point((int)(x2),(int)(yy+0.5))); return list.toArray(new Point[list.size()]); } /** * Test if the segment intersects a box. * @param box * @return */ public boolean intersects(Box box) { // Find min and max X for the segment double minX=Math.min(x1.x, x2.x); double maxX=Math.max(x1.x, x2.x);; double bMaxX=Math.max(box.x1.x,box.x2.x ); double bMinX=Math.min(box.x1.x,box.x2.x ); double bMaxY=Math.max(box.x1.y,box.x2.y ); double bMinY=Math.min(box.x1.y,box.x2.y ); // Find the intersection of the segment's and rectangle's x-projections if(maxX > bMaxX) { maxX = bMaxX; } if(minX < bMinX) { minX = bMinX; } if(minX > maxX) // If their projections do not intersect return false { return false; } // Find corresponding min and max Y for min and max X we found before double minY = x1.y; double maxY = x2.y; double dx = x2.x - x1.x; if(Math.abs(dx) > 0.0000001) { double a = (x2.y - x1.y) / dx; double b = x1.y - a * x1.x; minY = a * minX + b; maxY = a * maxX + b; } if(minY > maxY) { double tmp = maxY; maxY = minY; minY = tmp; } // Find the intersection of the segment's and rectangle's y-projections if(maxY > bMaxY) { maxY = bMaxY; } if(minY < bMinY) { minY = bMinY; } if(minY > maxY) // If Y-projections do not intersect return false { return false; } return true; } public String toString() { return ("Line["+x1+";" +x2+"]"); } /** * Not implemented */ public void remove() { } public Iterator<Point> iterator() { initIterator(); return this; } public int length() { int c=0; initIterator(); while(hasNext()) { c++; next(); } return c; } public double[] imProfileDouble (Image im) { double[] tab=new double[length()]; initIterator(); int c=0; while(hasNext()) { Point p=next(); tab[c++]=im.getPixelXYDouble(p.x, p.y); } return tab; } public double[] imProfileDouble (Image im, int band) { double[] tab=new double[length()]; initIterator(); int c=0; int xDim=im.xdim; int yDim=im.ydim; while(hasNext()) { Point p=next(); if(p.x>=0 && p.x<xDim && p.y>=0 && p.y<yDim) { tab[c]=im.getPixelXYBDouble(p.x, p.y,band); } c++; } return tab; } public int[] imProfileInt (Image im) { int[] tab=new int[length()]; initIterator(); int c=0; while(hasNext()) { Point p=next(); tab[c++]=im.getPixelXYInt(p.x, p.y); } return tab; } public static void main(String [] args){ Image im=new DoubleImage(300,300,1,1,1); Box b=new Box(60,60,200,200); Line l1=new Line(0,0,250,250); Line l2=new Line(100,100,150,100); Line l3=new Line(50,50,250,50); b.drawGrayRectangle(im); l1.drawGrayLine(im, 100); l2.drawGrayLine(im, 150); l3.drawGrayLine(im, 200); Point [] p1=l1.intersections(b); Point [] p2=l2.intersections(b); Point [] p3=l3.intersections(b); System.out.println("l1 : " + p1.length + " int? " + l1.intersects(b)); System.out.println("l2 : " + p2.length + " int? " + l2.intersects(b)); System.out.println("l3 : " + p3.length + " int? " + l3.intersects(b)); MViewer.exec(im); } }