1 package net.sf.openrocket.gui.util;
3 import java.awt.image.BufferedImage;
5 import java.io.IOException;
7 import java.util.ListIterator;
9 import javax.imageio.ImageIO;
11 import net.sf.openrocket.l10n.LocalizedIOException;
12 import net.sf.openrocket.util.ArrayList;
13 import net.sf.openrocket.util.Coordinate;
15 public class CustomFinImporter {
17 private enum FacingDirections {
22 private FacingDirections facing;
23 private int currentX, currentY;
27 public List<Coordinate> getPoints(File file) throws IOException {
28 ArrayList<Coordinate> points = new ArrayList<Coordinate>();
30 BufferedImage pic = ImageIO.read(file);
32 // Set initial values for parsing
34 facing = FacingDirections.UP;
36 if (!validateImage(pic)) {
37 throw new LocalizedIOException("CustomFinImport.error.badimage");
40 points.add(Coordinate.NUL);
42 optimizePoints(points);
47 private boolean validateImage(BufferedImage pic) {
48 int height = pic.getHeight();
49 int width = pic.getWidth();
50 boolean bottomEdgeFound = false;
52 for (int x = 0; x < width; ++x) {
53 for (int y = 0; y < height; ++y) {
54 int pixel = pic.getRGB(x, y) & 0x00FFFFFF; // Clear alpha, we don't care about it
55 if ((pixel == 0xFFFFFF) || (pixel == 0)) // black or white only
57 if ((x == 0) || (x == width - 1) || (y == 0)) {
58 // Left, right and top must have no black (fin)
61 } else if (y == height - 1) {
63 bottomEdgeFound = true;
69 // Found something other than a black or white pixel
74 return bottomEdgeFound;
77 private void loadFin(BufferedImage pic, ArrayList<Coordinate> points) {
78 boolean calledTurnedAround = false;
79 int height = pic.getHeight();
82 currentY = pic.getHeight() - 1;
85 if (checkLeftIsFin(pic, currentX, currentY))
87 else if (checkForwardIsFin(pic, currentX, currentY)) {
89 } else if (checkRightIsFin(pic, currentX, currentY))
93 calledTurnedAround = true;
97 if (pixelIsFin(pic, currentX, currentY)) {
98 if (!calledTurnedAround) {
99 double x = (currentX - startX) * 0.001;
100 double y = (height - currentY - 1) * 0.001;
101 points.add(new Coordinate(x, y));
103 calledTurnedAround = false;
105 } while (currentY < height - 1 && currentY >= 0);
108 private boolean pixelIsFin(BufferedImage pic, int x, int y) {
109 int height = pic.getHeight();
110 int width = pic.getWidth();
112 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
113 int pixel = pic.getRGB(x, y) & 0x00FFFFFF; // Clear alpha, we don't care about it
115 if (pixel == 0) // black is fin
121 private boolean checkLeftIsFin(BufferedImage pic, int x, int y) {
122 if (facing == FacingDirections.DOWN)
123 return pixelIsFin(pic, x + 1, y);
124 else if (facing == FacingDirections.UP)
125 return pixelIsFin(pic, x - 1, y);
126 else if (facing == FacingDirections.LEFT)
127 return pixelIsFin(pic, x, y + 1);
128 else if (facing == FacingDirections.RIGHT)
129 return pixelIsFin(pic, x, y - 1);
134 private boolean checkRightIsFin(BufferedImage pic, int x, int y) {
135 if (facing == FacingDirections.DOWN)
136 return pixelIsFin(pic, x - 1, y);
137 else if (facing == FacingDirections.UP)
138 return pixelIsFin(pic, x + 1, y);
139 else if (facing == FacingDirections.LEFT)
140 return pixelIsFin(pic, x, y - 1);
141 else if (facing == FacingDirections.RIGHT)
142 return pixelIsFin(pic, x, y + 1);
147 private boolean checkForwardIsFin(BufferedImage pic, int x, int y) {
148 if (facing == FacingDirections.DOWN)
149 return pixelIsFin(pic, x, y + 1);
150 else if (facing == FacingDirections.UP)
151 return pixelIsFin(pic, x, y - 1);
152 else if (facing == FacingDirections.LEFT)
153 return pixelIsFin(pic, x - 1, y);
154 else if (facing == FacingDirections.RIGHT)
155 return pixelIsFin(pic, x + 1, y);
160 private void rotateLeft() {
161 if (facing == FacingDirections.UP)
162 facing = FacingDirections.LEFT;
163 else if (facing == FacingDirections.RIGHT)
164 facing = FacingDirections.UP;
165 else if (facing == FacingDirections.DOWN)
166 facing = FacingDirections.RIGHT;
167 else if (facing == FacingDirections.LEFT)
168 facing = FacingDirections.DOWN;
171 private void rotateRight() {
172 if (facing == FacingDirections.UP)
173 facing = FacingDirections.RIGHT;
174 else if (facing == FacingDirections.RIGHT)
175 facing = FacingDirections.DOWN;
176 else if (facing == FacingDirections.DOWN)
177 facing = FacingDirections.LEFT;
178 else if (facing == FacingDirections.LEFT)
179 facing = FacingDirections.UP;
182 private void moveForward(BufferedImage pic) {
183 if (facing == FacingDirections.UP) {
186 } else if (facing == FacingDirections.RIGHT) {
187 if (currentX < pic.getWidth() - 1)
189 } else if (facing == FacingDirections.DOWN) {
190 if (currentY < pic.getHeight() - 1)
192 } else if (facing == FacingDirections.LEFT) {
198 private void turnAround() {
199 if (facing == FacingDirections.UP)
200 facing = FacingDirections.DOWN;
201 else if (facing == FacingDirections.DOWN)
202 facing = FacingDirections.UP;
203 else if (facing == FacingDirections.RIGHT)
204 facing = FacingDirections.LEFT;
205 else if (facing == FacingDirections.LEFT)
206 facing = FacingDirections.RIGHT;
209 private void optimizePoints(ArrayList<Coordinate> points) {
211 ListIterator<Coordinate> start, entry, entry2;
212 Coordinate startPoint, endPoint, testPoint;
215 start = points.listIterator();
216 startPoint = start.next();
217 while ((start.hasNext()) && (startPoint != points.get(points.size() - 1))) {
218 entry = points.listIterator(points.size());
219 endPoint = entry.previous();
220 for (; endPoint != startPoint; endPoint = entry.previous()) {
221 entry2 = points.listIterator(start.nextIndex());
222 testPoint = entry2.next();
223 for (; testPoint != endPoint; testPoint = entry2.next()) {
224 if (pointDistanceFromLine(startPoint, endPoint, testPoint) > 0.001) {
228 if ((testPoint == endPoint) && (endPoint != startPoint)) {
229 // Entire segment was within distance, it's a strait line.
230 // Remove all but the first and last point
231 entry2 = points.listIterator(start.nextIndex());
232 int nextIx = entry2.nextIndex();
233 Coordinate check = entry2.next();
234 while ((entry2.nextIndex() != points.size()) && (check != endPoint)) {
236 nextIx = entry2.nextIndex();
237 check = entry2.next();
240 start = points.listIterator(startIx);
241 startPoint = start.next();
245 if (endPoint == startPoint) {
246 startIx = start.nextIndex();
248 startPoint = start.next();
253 private double pointDistanceFromLine(Coordinate startPoint, Coordinate endPoint, Coordinate testPoint) {
254 Coordinate pt = closestPointOnSegment(startPoint, endPoint, testPoint);
256 return testPoint.sub(pt).length();
259 private Coordinate closestPointOnSegment(Coordinate a, Coordinate b, Coordinate p) {
260 Coordinate D = b.sub(a);
261 double numer = p.sub(a).dot(D);
264 double denom = D.dot(D);
267 return a.add(D.multiply(numer / denom));