package ij.process;
import ij.*;
import ij.gui.*;
import java.awt.Rectangle;
public class PolygonFiller {
int BLACK=0xff000000, WHITE=0xffffffff;
int edges; int activeEdges;
int[] x; int[] y; float[] xf, yf; double xOffset, yOffset; int n;
double[] ex; int[] ey1; int[] ey2; double[] eslope; int yMin, yMax;
int[] sedge;
int[] aedge;
public PolygonFiller() {
}
public PolygonFiller(int[] x, int[] y, int n) {
setPolygon(x, y, n);
}
public PolygonFiller(float[] xf, float[] yf, int n, double xOffset, double yOffset) {
setPolygon(xf, yf, n, xOffset, yOffset);
}
public void setPolygon(int[] x, int[] y, int n) {
this.x = x;
this.y = y;
this.n = n;
}
public void setPolygon(float[] xf, float[] yf, int n, double xOffset, double yOffset) {
x = y = null;
this.xf = xf;
this.yf = yf;
this.n = n;
this.xOffset = xOffset;
this.yOffset = yOffset;
}
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];
}
}
void buildEdgeTable() {
yMin = Integer.MAX_VALUE;
yMax = Integer.MIN_VALUE;
edges = 0;
int polyStart = 0; for (int i=0; i<n; i++) {
int iplus1 = i==n-1 ? polyStart : i+1;
if (x != null) { int y1 = y[i]; int y2 = y[iplus1];
int x1 = x[i]; int x2 = x[iplus1];
if (y1==y2)
continue; if (y1>y2) { int tmp = y1;
y1 = y2; y2 = tmp;
tmp = x1;
x1 = x2; x2 = tmp;
}
double slope = (double)(x2 - x1)/(y2 - y1);
ex[edges] = x1 + 0.5*slope + 1e-8; ey1[edges] = y1;
ey2[edges] = y2;
eslope[edges] = slope;
if (y1 < yMin) yMin = y1;
if (y2 > yMax) yMax = y2;
} else { if (Float.isNaN(xf[iplus1])) iplus1 = polyStart;
if (Float.isNaN(xf[i])) { polyStart = i + 1;
continue;
}
double y1f = yf[i] + yOffset; double y2f = yf[iplus1] + yOffset;
double x1f = xf[i] + xOffset; double x2f = xf[iplus1] + xOffset;
int y1 = (int)Math.round(y1f);
int y2 = (int)Math.round(y2f);
if (y1==y2 || (y1<=0 && y2<=0))
continue; if (y1>y2) { int tmp = y1;
y1 = y2; y2 = tmp;
double ftmp = y1f;
y1f = y2f; y2f = ftmp;
ftmp = x1f;
x1f = x2f; x2f = ftmp;
}
double slope = (x2f - x1f)/(y2f - y1f);
ex[edges] = x1f + (y1 - y1f + 0.5)*slope + 1e-8; ey1[edges] = y1;
ey2[edges] = y2;
eslope[edges] = slope;
if (y1 < yMin) yMin = y1;
if (y2 > yMax) yMax = y2;
}
edges++;
}
for (int i=0; i<edges; i++)
sedge[i] = i;
activeEdges = 0;
}
public void fill(ImageProcessor ip, Rectangle r) {
ip.fill(getMask(r.width, r.height));
}
public ImageProcessor getMask(int width, int height) {
ByteProcessor mask = new ByteProcessor(width, height);
fillByteProcessorMask(mask);
return mask;
}
public void fillByteProcessorMask(ByteProcessor mask) {
int width = mask.getWidth();
int height = mask.getHeight();
byte[] pixels = (byte[])mask.getPixels();
allocateArrays(n);
buildEdgeTable();
int x1, x2, offset, index;
int yStart = yMin>0 ? yMin : 0;
if (yMin != 0)
shiftXValuesAndActivate(yStart);
for (int y=yStart; y<Math.min(height, yMax+1); 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;
for (int x=x1; x<x2; x++)
pixels[offset+x] = -1; }
updateXCoordinates();
}
}
void shiftXValuesAndActivate(int yStart) {
for (int i=0; i<edges; i++) {
int index = sedge[i];
if (ey1[index] < yStart && ey2[index] >= yStart) {
ex[index] += eslope[index] * (yStart - ey1[index]);
aedge[activeEdges++] = index;
}
}
sortActiveEdges();
}
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();
}
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;
}
}
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++;
}
}
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++;
}
}
}
void printEdges() {
for (int i=0; i<edges; i++) {
int index = i;
IJ.log(i+": x="+IJ.d2s(ex[index])+" y="+ey1[index]+" to "+ey2[index] + " sl=" + IJ.d2s(eslope[index],2) );
}
}
void printActiveEdges() {
for (int i=0; i<activeEdges; i++) {
int index =aedge[i];
IJ.log(i+": x="+IJ.d2s(ex[index])+" y="+ey1[index]+" to "+ey2[index] + " sl=" + IJ.d2s(eslope[index],2) );
}
}
}