package net.sf.openrocket.gui.util;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ListIterator;
import javax.imageio.ImageIO;
import net.sf.openrocket.l10n.LocalizedIOException;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.Coordinate;
public class CustomFinImporter {
private enum FacingDirections {
UP, DOWN, LEFT, RIGHT
}
private int startX;
private FacingDirections facing;
private int currentX, currentY;
public List<Coordinate> getPoints(File file) throws IOException {
ArrayList<Coordinate> points = new ArrayList<Coordinate>();
BufferedImage pic = ImageIO.read(file);
// Set initial values for parsing
startX = -1;
facing = FacingDirections.UP;
if (!validateImage(pic)) {
throw new LocalizedIOException("CustomFinImport.badFinImage");
}
// Load the fin
points.add(Coordinate.NUL);
loadFin(pic, points);
// Optimize the loaded fin
int count;
do {
count = points.size();
optimizePoints(points);
} while (count != points.size());
return points;
}
private boolean validateImage(BufferedImage pic) {
int height = pic.getHeight();
int width = pic.getWidth();
boolean bottomEdgeFound = false;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
int pixel = pic.getRGB(x, y) & 0x00FFFFFF; // Clear alpha, we don't care about it
// Convert to black & white
int red = (pixel & 0x00FF0000) >> 16;
int green = (pixel & 0x0000FF00) >> 8;
int blue = (pixel & 0x000000FF);
pixel = (int)(0.299*red + 0.587*green + 0.114*blue);
if (pixel > 200)
pixel = 0xFFFFFF; // White
else
pixel = 0; // Black
pic.setRGB(x, y, pixel);
if (y == height - 1) {
if (pixel == 0) {
bottomEdgeFound = true;
if (startX == -1)
startX = x;
}
}
}
}
return bottomEdgeFound;
}
private void loadFin(BufferedImage pic, ArrayList<Coordinate> points) {
int height = pic.getHeight();
Boolean offBottom = false;
currentX = startX;
currentY = height - 1;
do {
if (checkLeftIsFin(pic, currentX, currentY))
rotateLeft();
else if (checkForwardIsFin(pic, currentX, currentY)) {
// Do nothing
} else if (checkRightIsFin(pic, currentX, currentY))
rotateRight();
else {
turnAround();
}
moveForward(pic);
if (currentY < height - 1)
offBottom = true;
if (pixelIsFin(pic, currentX, currentY)) {
double x = (currentX - startX) * 0.001;
double y = (height - currentY - 1) * 0.001;
points.add(new Coordinate(x, y));
}
} while ((!offBottom) || (currentY < height - 1 && currentY >= 0));
}
private boolean pixelIsFin(BufferedImage pic, int x, int y) {
int height = pic.getHeight();
int width = pic.getWidth();
if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
int pixel = pic.getRGB(x, y) & 0x00FFFFFF; // Clear alpha, we don't care about it
if (pixel == 0) // black is fin
return true;
}
return false;
}
private boolean checkLeftIsFin(BufferedImage pic, int x, int y) {
if (facing == FacingDirections.DOWN)
return pixelIsFin(pic, x + 1, y);
else if (facing == FacingDirections.UP)
return pixelIsFin(pic, x - 1, y);
else if (facing == FacingDirections.LEFT)
return pixelIsFin(pic, x, y + 1);
else if (facing == FacingDirections.RIGHT)
return pixelIsFin(pic, x, y - 1);
else
return false;
}
private boolean checkRightIsFin(BufferedImage pic, int x, int y) {
if (facing == FacingDirections.DOWN)
return pixelIsFin(pic, x - 1, y);
else if (facing == FacingDirections.UP)
return pixelIsFin(pic, x + 1, y);
else if (facing == FacingDirections.LEFT)
return pixelIsFin(pic, x, y - 1);
else if (facing == FacingDirections.RIGHT)
return pixelIsFin(pic, x, y + 1);
else
return false;
}
private boolean checkForwardIsFin(BufferedImage pic, int x, int y) {
if (facing == FacingDirections.DOWN)
return pixelIsFin(pic, x, y + 1);
else if (facing == FacingDirections.UP)
return pixelIsFin(pic, x, y - 1);
else if (facing == FacingDirections.LEFT)
return pixelIsFin(pic, x - 1, y);
else if (facing == FacingDirections.RIGHT)
return pixelIsFin(pic, x + 1, y);
else
return false;
}
private void rotateLeft() {
if (facing == FacingDirections.UP)
facing = FacingDirections.LEFT;
else if (facing == FacingDirections.RIGHT)
facing = FacingDirections.UP;
else if (facing == FacingDirections.DOWN)
facing = FacingDirections.RIGHT;
else if (facing == FacingDirections.LEFT)
facing = FacingDirections.DOWN;
}
private void rotateRight() {
if (facing == FacingDirections.UP)
facing = FacingDirections.RIGHT;
else if (facing == FacingDirections.RIGHT)
facing = FacingDirections.DOWN;
else if (facing == FacingDirections.DOWN)
facing = FacingDirections.LEFT;
else if (facing == FacingDirections.LEFT)
facing = FacingDirections.UP;
}
private void moveForward(BufferedImage pic) {
if (facing == FacingDirections.UP) {
if (currentY > 0)
currentY--;
} else if (facing == FacingDirections.RIGHT) {
if (currentX < pic.getWidth() - 1)
currentX++;
} else if (facing == FacingDirections.DOWN) {
if (currentY < pic.getHeight() - 1)
currentY++;
} else if (facing == FacingDirections.LEFT) {
if (currentX > 0)
currentX--;
}
}
private void turnAround() {
if (facing == FacingDirections.UP)
facing = FacingDirections.DOWN;
else if (facing == FacingDirections.DOWN)
facing = FacingDirections.UP;
else if (facing == FacingDirections.RIGHT)
facing = FacingDirections.LEFT;
else if (facing == FacingDirections.LEFT)
facing = FacingDirections.RIGHT;
}
private void optimizePoints(ArrayList<Coordinate> points) {
int startIx;
ListIterator<Coordinate> start, entry, entry2;
Coordinate startPoint, endPoint, testPoint;
Boolean removedSection;
startIx = 0;
start = points.listIterator();
startPoint = start.next();
while ((start.hasNext()) && (startPoint != points.get(points.size() - 1))) {
removedSection = false;
entry = points.listIterator(points.size());
endPoint = entry.previous();
for (; endPoint != startPoint; endPoint = entry.previous()) {
entry2 = points.listIterator(start.nextIndex());
testPoint = entry2.next();
for (; testPoint != endPoint; testPoint = entry2.next()) {
if (pointDistanceFromLine(startPoint, endPoint, testPoint) > 0.0008) {
break;
}
}
if ((testPoint == endPoint) && (endPoint != startPoint)) {
// Entire segment was within distance, it's a strait line.
// Remove all but the first and last point
entry2 = points.listIterator(start.nextIndex());
int nextIx = entry2.nextIndex();
Coordinate check = entry2.next();
while ((entry2.nextIndex() != points.size()) && (check != endPoint)) {
entry2.remove();
nextIx = entry2.nextIndex();
check = entry2.next();
}
startIx = nextIx;
start = points.listIterator(startIx);
startPoint = start.next();
removedSection = true;
break;
}
}
if ((!removedSection) && (endPoint == startPoint)) {
startIx = start.nextIndex();
if (start.hasNext())
startPoint = start.next();
}
}
}
private double pointDistanceFromLine(Coordinate startPoint, Coordinate endPoint, Coordinate testPoint) {
Coordinate pt = closestPointOnSegment(startPoint, endPoint, testPoint);
return testPoint.sub(pt).length();
}
private Coordinate closestPointOnSegment(Coordinate a, Coordinate b, Coordinate p) {
Coordinate D = b.sub(a);
double numer = p.sub(a).dot(D);
if (numer <= 0.0f)
return a;
double denom = D.dot(D);
if (numer >= denom)
return b;
return a.add(D.multiply(numer / denom));
}
}