package ij.process;
import ij.*;
import ij.gui.*;
import java.awt.Rectangle;
/** This class fills polygons using the scan-line filling algorithm
described at "http://www.cs.rit.edu/~icss571/filling/". */
public class PolygonFiller {
int BLACK=0xff000000, WHITE=0xffffffff;
int edges; // number of edges
int activeEdges; // number of active edges
// the polygon
int[] x; // x coordinates
int[] y; // y coordinates
int n; // number of coordinates
// edge table
double[] ex; // x coordinates
int[] ey1; // upper y coordinates
int[] ey2; // lower y coordinates
double[] eslope; // inverse slopes (1/m)
// sorted edge table (indexes into edge table) (currently not used)
int[] sedge;
// active edge table (indexes into edge table)
int[] aedge;
/** Constructs a PolygonFiller. */
public PolygonFiller() {
}
/** Constructs a PolygonFiller using the specified polygon. */
public PolygonFiller(int[] x, int[] y, int n) {
setPolygon(x, y, n);
}
/** Specifies the polygon to be filled. */
public void setPolygon(int[] x, int[] y, int n) {
this.x = x;
this.y = y;
this.n = n;
}
void allocateArrays(int n) {
if (ex==null || n>ex.length) {
ex = new double[n];
ey1 = new int[n];
ey2 = new int[n];
sedge = new int[n];
aedge = new int[n];
eslope = new double[n];
}
}
/** Generates the edge table. */
void buildEdgeTable(int[] x, int[] y, int n) {
int length, iplus1, x1, x2, y1, y2;
edges = 0;
for (int i=0; i<n; i++) {
iplus1 = i==n-1?0:i+1;
y1 = y[i]; y2 = y[iplus1];
x1 = x[i]; x2 = x[iplus1];
if (y1==y2)
continue; //ignore horizontal lines
if (y1>y2) { // swap ends
int tmp = y1;
y1=y2; y2=tmp;
tmp=x1;
x1=x2; x2=tmp;
}
double slope = (double)(x2-x1)/(y2-y1);
ex[edges] = x1 + slope/2.0;
ey1[edges] = y1;
ey2[edges] = y2;
eslope[edges] = slope;
edges++;
}
for (int i=0; i<edges; i++)
sedge[i] = i;
activeEdges = 0;
//quickSort(sedge);
}
/** Currently not used since searching the entire edge table
does not seem to take a significant amount of time. */
void addToSortedTable(int edge) {
int index = 0;
while (index<edges && ey1[edges]>ey1[sedge[index]]) {
index++;
}
for (int i=edges-1; i>=index; i--) {
sedge[i+1] = sedge[i];
//IJ.log((i+1)+"="+i);
}
sedge[index] = edges;
}
/** Fills the polygon using the ImageProcessor's current drawing color. */
public void fill(ImageProcessor ip, Rectangle r) {
ip.fill(getMask(r.width, r.height));
}
/** Returns a byte mask containing a filled version of the polygon. */
public ImageProcessor getMask(int width, int height) {
allocateArrays(n);
buildEdgeTable(x, y, n);
//printEdges();
int x1, x2, offset, index;
ImageProcessor mask = new ByteProcessor(width, height);
byte[] pixels = (byte[])mask.getPixels();
for (int y=0; y<height; y++) {
removeInactiveEdges(y);
activateEdges(y);
offset = y*width;
for (int i=0; i<activeEdges; i+=2) {
x1 = (int)(ex[aedge[i]]+0.5);
if (x1<0) x1=0;
if (x1>width) x1 = width;
x2 = (int)(ex[aedge[i+1]]+0.5);
if (x2<0) x2=0;
if (x2>width) x2 = width;
//IJ.log(y+" "+x1+" "+x2);
for (int x=x1; x<x2; x++)
pixels[offset+x] = -1; // 255 (white)
}
updateXCoordinates();
}
return mask;
}
/** Updates the x coordinates in the active edges list and sorts the list if necessary. */
void updateXCoordinates() {
int index;
double x1=-Double.MAX_VALUE, x2;
boolean sorted = true;
for (int i=0; i<activeEdges; i++) {
index = aedge[i];
x2 = ex[index] + eslope[index];
ex[index] = x2;
if (x2<x1) sorted = false;
x1 = x2;
}
if (!sorted)
sortActiveEdges();
}
/** Sorts the active edges list by x coordinate using a selection sort. */
void sortActiveEdges() {
int min, tmp;
for (int i=0; i<activeEdges; i++) {
min = i;
for (int j=i; j<activeEdges; j++)
if (ex[aedge[j]] <ex[aedge[min]]) min = j;
tmp=aedge[min];
aedge[min] = aedge[i];
aedge[i]=tmp;
}
}
/** Removes edges from the active edge table that are no longer needed. */
void removeInactiveEdges(int y) {
int i = 0;
while (i<activeEdges) {
int index = aedge[i];
if (y<ey1[index] || y>=ey2[index]) {
for (int j=i; j<activeEdges-1; j++)
aedge[j] = aedge[j+1];
activeEdges--;
} else
i++;
}
}
/** Adds edges to the active edge table. */
void activateEdges(int y) {
for (int i=0; i<edges; i++) {
int edge =sedge[i];
if (y==ey1[edge]) {
int index = 0;
while (index<activeEdges && ex[edge]>ex[aedge[index]])
index++;
for (int j=activeEdges-1; j>=index; j--)
aedge[j+1] = aedge[j];
aedge[index] = edge;
activeEdges++;
}
}
}
/** Display the contents of the edge table*/
void printEdges() {
for (int i=0; i<edges; i++) {
int index = sedge[i];
IJ.log(i+" "+ex[index]+" "+ey1[index]+" "+ey2[index] + " " + IJ.d2s(eslope[index],2) );
}
}
/** Display the contents of the active edge table*/
void printActiveEdges() {
for (int i=0; i<activeEdges; i++) {
int index =aedge[i];
IJ.log(i+" "+ex[index]+" "+ey1[index]+" "+ey2[index] );
}
}
/*
void quickSort(int[] a) {
quickSort(a, 0, a.length-1);
}
void quickSort(int[] a, int from, int to) {
int i=from, j=to;
int center = a[(from+to)/2];
do {
//while ( i < to && center.compareTo(a[i]) > 0 ) i++;
while (i<to && ey1[center]>ey1[a[i]]) j--;
//while ( j > from && center.compareTo(a[j]) < 0 ) j--;
while (j>from && ey1[center]<ey1[a[j]]) j--;
if (i < j) {int temp = a[i]; a[i] = a[j]; a[j] = temp;}
if (i <= j) { i++; j--; }
} while(i <= j);
if (from < j) quickSort(a, from, j);
if (i < to) quickSort(a, i, to);
}
*/
}