Merge branch 'upstream' into debian
[debian/openrocket] / core / src / net / sf / openrocket / gui / util / CustomFinImporter.java
1 package net.sf.openrocket.gui.util;
2
3 import java.awt.image.BufferedImage;
4 import java.io.File;
5 import java.io.IOException;
6 import java.util.List;
7 import java.util.ListIterator;
8
9 import javax.imageio.ImageIO;
10
11 import net.sf.openrocket.l10n.LocalizedIOException;
12 import net.sf.openrocket.util.ArrayList;
13 import net.sf.openrocket.util.Coordinate;
14
15 public class CustomFinImporter {
16         
17         private enum FacingDirections {
18                 UP, DOWN, LEFT, RIGHT
19         }
20         
21         private int startX;
22         private FacingDirections facing;
23         private int currentX, currentY;
24         
25         
26         
27         public List<Coordinate> getPoints(File file) throws IOException {
28                 ArrayList<Coordinate> points = new ArrayList<Coordinate>();
29                 
30                 BufferedImage pic = ImageIO.read(file);
31                 
32                 // Set initial values for parsing
33                 startX = -1;
34                 facing = FacingDirections.UP;
35                 
36                 if (!validateImage(pic)) {
37                         throw new LocalizedIOException("CustomFinImport.error.badimage");
38                 }
39                 
40                 points.add(Coordinate.NUL);
41                 loadFin(pic, points);
42                 optimizePoints(points);
43                 return points;
44         }
45         
46         
47         private boolean validateImage(BufferedImage pic) {
48                 int height = pic.getHeight();
49                 int width = pic.getWidth();
50                 boolean bottomEdgeFound = false;
51                 
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
56                                 {
57                                         if ((x == 0) || (x == width - 1) || (y == 0)) {
58                                                 // Left, right and top must have no black (fin)
59                                                 if (pixel == 0)
60                                                         return false;
61                                         } else if (y == height - 1) {
62                                                 if (pixel == 0) {
63                                                         bottomEdgeFound = true;
64                                                         if (startX == -1)
65                                                                 startX = x;
66                                                 }
67                                         }
68                                 } else {
69                                         // Found something other than a black or white pixel
70                                         return false;
71                                 }
72                         }
73                 }
74                 return bottomEdgeFound;
75         }
76         
77         private void loadFin(BufferedImage pic, ArrayList<Coordinate> points) {
78                 boolean calledTurnedAround = false;
79                 int height = pic.getHeight();
80                 
81                 currentX = startX;
82                 currentY = pic.getHeight() - 1;
83                 
84                 do {
85                         if (checkLeftIsFin(pic, currentX, currentY))
86                                 rotateLeft();
87                         else if (checkForwardIsFin(pic, currentX, currentY)) {
88                                 // Do nothing
89                         } else if (checkRightIsFin(pic, currentX, currentY))
90                                 rotateRight();
91                         else {
92                                 turnAround();
93                                 calledTurnedAround = true;
94                         }
95                         
96                         moveForward(pic);
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));
102                                 } else
103                                         calledTurnedAround = false;
104                         }
105                 } while (currentY < height - 1 && currentY >= 0);
106         }
107         
108         private boolean pixelIsFin(BufferedImage pic, int x, int y) {
109                 int height = pic.getHeight();
110                 int width = pic.getWidth();
111                 
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
114                         
115                         if (pixel == 0) // black is fin
116                                 return true;
117                 }
118                 return false;
119         }
120         
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);
130                 else
131                         return false;
132         }
133         
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);
143                 else
144                         return false;
145         }
146         
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);
156                 else
157                         return false;
158         }
159         
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;
169         }
170         
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;
180         }
181         
182         private void moveForward(BufferedImage pic) {
183                 if (facing == FacingDirections.UP) {
184                         if (currentY > 0)
185                                 currentY--;
186                 } else if (facing == FacingDirections.RIGHT) {
187                         if (currentX < pic.getWidth() - 1)
188                                 currentX++;
189                 } else if (facing == FacingDirections.DOWN) {
190                         if (currentY < pic.getHeight() - 1)
191                                 currentY++;
192                 } else if (facing == FacingDirections.LEFT) {
193                         if (currentX > 0)
194                                 currentX--;
195                 }
196         }
197         
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;
207         }
208         
209         private void optimizePoints(ArrayList<Coordinate> points) {
210                 int startIx;
211                 ListIterator<Coordinate> start, entry, entry2;
212                 Coordinate startPoint, endPoint, testPoint;
213                 
214                 startIx = 0;
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) {
225                                                 break;
226                                         }
227                                 }
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)) {
235                                                 entry2.remove();
236                                                 nextIx = entry2.nextIndex();
237                                                 check = entry2.next();
238                                         }
239                                         startIx = nextIx;
240                                         start = points.listIterator(startIx);
241                                         startPoint = start.next();
242                                         break;
243                                 }
244                         }
245                         if (endPoint == startPoint) {
246                                 startIx = start.nextIndex();
247                                 if (start.hasNext())
248                                         startPoint = start.next();
249                         }
250                 }
251         }
252         
253         private double pointDistanceFromLine(Coordinate startPoint, Coordinate endPoint, Coordinate testPoint) {
254                 Coordinate pt = closestPointOnSegment(startPoint, endPoint, testPoint);
255                 
256                 return testPoint.sub(pt).length();
257         }
258         
259         private Coordinate closestPointOnSegment(Coordinate a, Coordinate b, Coordinate p) {
260                 Coordinate D = b.sub(a);
261                 double numer = p.sub(a).dot(D);
262                 if (numer <= 0.0f)
263                         return a;
264                 double denom = D.dot(D);
265                 if (numer >= denom)
266                         return b;
267                 return a.add(D.multiply(numer / denom));
268         }
269 }