create changelog entry
[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.badFinImage");
38                 }
39
40                 // Load the fin
41                 points.add(Coordinate.NUL);
42                 loadFin(pic, points);
43
44                 // Optimize the loaded fin
45                 int count;
46                 do {
47                         count = points.size();
48                         optimizePoints(points);
49                 } while (count != points.size());
50
51                 return points;
52         }
53         
54         
55         private boolean validateImage(BufferedImage pic) {
56                 int height = pic.getHeight();
57                 int width = pic.getWidth();
58                 boolean bottomEdgeFound = false;
59                 
60                 for (int x = 0; x < width; ++x) {
61                         for (int y = 0; y < height; ++y) {
62                                 int pixel = pic.getRGB(x, y) & 0x00FFFFFF; // Clear alpha, we don't care about it
63                                 // Convert to black & white
64                                 int red = (pixel & 0x00FF0000) >> 16;  
65                                 int green = (pixel & 0x0000FF00) >> 8;  
66                                 int blue = (pixel & 0x000000FF);
67                                 pixel =  (int)(0.299*red + 0.587*green + 0.114*blue);
68                                 if (pixel > 200)
69                                         pixel = 0xFFFFFF; // White
70                                 else
71                                         pixel = 0; // Black
72                                 pic.setRGB(x, y, pixel);
73                                 if (y == height - 1) {
74                                         if (pixel == 0) {
75                                                 bottomEdgeFound = true;
76                                                 if (startX == -1)
77                                                         startX = x;
78                                         }
79                                 }
80                         }
81                 }
82                 return bottomEdgeFound;
83         }
84         
85         private void loadFin(BufferedImage pic, ArrayList<Coordinate> points) {
86                 int height = pic.getHeight();
87                 Boolean offBottom = false;
88                 
89                 currentX = startX;
90                 currentY = height - 1;
91                 
92                 do {
93                         if (checkLeftIsFin(pic, currentX, currentY))
94                                 rotateLeft();
95                         else if (checkForwardIsFin(pic, currentX, currentY)) {
96                                 // Do nothing
97                         } else if (checkRightIsFin(pic, currentX, currentY))
98                                 rotateRight();
99                         else {
100                                 turnAround();
101                         }
102                         
103                         moveForward(pic);
104                         if (currentY < height - 1)
105                                 offBottom = true;
106                         if (pixelIsFin(pic, currentX, currentY)) {
107                                 double x = (currentX - startX) * 0.001;
108                                 double y = (height - currentY - 1) * 0.001;
109                                 points.add(new Coordinate(x, y));
110                         }
111                 } while ((!offBottom) || (currentY < height - 1 && currentY >= 0));
112         }
113         
114         private boolean pixelIsFin(BufferedImage pic, int x, int y) {
115                 int height = pic.getHeight();
116                 int width = pic.getWidth();
117                 
118                 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
119                         int pixel = pic.getRGB(x, y) & 0x00FFFFFF; // Clear alpha, we don't care about it
120                         
121                         if (pixel == 0) // black is fin
122                                 return true;
123                 }
124                 return false;
125         }
126         
127         private boolean checkLeftIsFin(BufferedImage pic, int x, int y) {
128                 if (facing == FacingDirections.DOWN)
129                         return pixelIsFin(pic, x + 1, y);
130                 else if (facing == FacingDirections.UP)
131                         return pixelIsFin(pic, x - 1, y);
132                 else if (facing == FacingDirections.LEFT)
133                         return pixelIsFin(pic, x, y + 1);
134                 else if (facing == FacingDirections.RIGHT)
135                         return pixelIsFin(pic, x, y - 1);
136                 else
137                         return false;
138         }
139         
140         private boolean checkRightIsFin(BufferedImage pic, int x, int y) {
141                 if (facing == FacingDirections.DOWN)
142                         return pixelIsFin(pic, x - 1, y);
143                 else if (facing == FacingDirections.UP)
144                         return pixelIsFin(pic, x + 1, y);
145                 else if (facing == FacingDirections.LEFT)
146                         return pixelIsFin(pic, x, y - 1);
147                 else if (facing == FacingDirections.RIGHT)
148                         return pixelIsFin(pic, x, y + 1);
149                 else
150                         return false;
151         }
152         
153         private boolean checkForwardIsFin(BufferedImage pic, int x, int y) {
154                 if (facing == FacingDirections.DOWN)
155                         return pixelIsFin(pic, x, y + 1);
156                 else if (facing == FacingDirections.UP)
157                         return pixelIsFin(pic, x, y - 1);
158                 else if (facing == FacingDirections.LEFT)
159                         return pixelIsFin(pic, x - 1, y);
160                 else if (facing == FacingDirections.RIGHT)
161                         return pixelIsFin(pic, x + 1, y);
162                 else
163                         return false;
164         }
165         
166         private void rotateLeft() {
167                 if (facing == FacingDirections.UP)
168                         facing = FacingDirections.LEFT;
169                 else if (facing == FacingDirections.RIGHT)
170                         facing = FacingDirections.UP;
171                 else if (facing == FacingDirections.DOWN)
172                         facing = FacingDirections.RIGHT;
173                 else if (facing == FacingDirections.LEFT)
174                         facing = FacingDirections.DOWN;
175         }
176         
177         private void rotateRight() {
178                 if (facing == FacingDirections.UP)
179                         facing = FacingDirections.RIGHT;
180                 else if (facing == FacingDirections.RIGHT)
181                         facing = FacingDirections.DOWN;
182                 else if (facing == FacingDirections.DOWN)
183                         facing = FacingDirections.LEFT;
184                 else if (facing == FacingDirections.LEFT)
185                         facing = FacingDirections.UP;
186         }
187         
188         private void moveForward(BufferedImage pic) {
189                 if (facing == FacingDirections.UP) {
190                         if (currentY > 0)
191                                 currentY--;
192                 } else if (facing == FacingDirections.RIGHT) {
193                         if (currentX < pic.getWidth() - 1)
194                                 currentX++;
195                 } else if (facing == FacingDirections.DOWN) {
196                         if (currentY < pic.getHeight() - 1)
197                                 currentY++;
198                 } else if (facing == FacingDirections.LEFT) {
199                         if (currentX > 0)
200                                 currentX--;
201                 }
202         }
203         
204         private void turnAround() {
205                 if (facing == FacingDirections.UP)
206                         facing = FacingDirections.DOWN;
207                 else if (facing == FacingDirections.DOWN)
208                         facing = FacingDirections.UP;
209                 else if (facing == FacingDirections.RIGHT)
210                         facing = FacingDirections.LEFT;
211                 else if (facing == FacingDirections.LEFT)
212                         facing = FacingDirections.RIGHT;
213         }
214         
215         private void optimizePoints(ArrayList<Coordinate> points) {
216                 int startIx;
217                 ListIterator<Coordinate> start, entry, entry2;
218                 Coordinate startPoint, endPoint, testPoint;
219                 Boolean removedSection;
220                 
221                 startIx = 0;
222                 start = points.listIterator();
223                 startPoint = start.next();
224                 while ((start.hasNext()) && (startPoint != points.get(points.size() - 1))) {
225                         removedSection = false;
226                         entry = points.listIterator(points.size());
227                         endPoint = entry.previous();
228                         for (; endPoint != startPoint; endPoint = entry.previous()) {
229                                 entry2 = points.listIterator(start.nextIndex());
230                                 testPoint = entry2.next();
231                                 for (; testPoint != endPoint; testPoint = entry2.next()) {
232                                         if (pointDistanceFromLine(startPoint, endPoint, testPoint) > 0.0008) {
233                                                 break;
234                                         }
235                                 }
236                                 if ((testPoint == endPoint) && (endPoint != startPoint)) {
237                                         // Entire segment was within distance, it's a strait line.
238                                         // Remove all but the first and last point
239                                         entry2 = points.listIterator(start.nextIndex());
240                                         int nextIx = entry2.nextIndex();
241                                         Coordinate check = entry2.next();
242                                         while ((entry2.nextIndex() != points.size()) && (check != endPoint)) {
243                                                 entry2.remove();
244                                                 nextIx = entry2.nextIndex();
245                                                 check = entry2.next();
246                                         }
247                                         startIx = nextIx;
248                                         start = points.listIterator(startIx);
249                                         startPoint = start.next();
250                                         removedSection = true;
251                                         break;
252                                 }
253                         }
254                         if ((!removedSection) && (endPoint == startPoint)) {
255                                 startIx = start.nextIndex();
256                                 if (start.hasNext())
257                                         startPoint = start.next();
258                         }
259                 }
260         }
261         
262         private double pointDistanceFromLine(Coordinate startPoint, Coordinate endPoint, Coordinate testPoint) {
263                 Coordinate pt = closestPointOnSegment(startPoint, endPoint, testPoint);
264                 
265                 return testPoint.sub(pt).length();
266         }
267         
268         private Coordinate closestPointOnSegment(Coordinate a, Coordinate b, Coordinate p) {
269                 Coordinate D = b.sub(a);
270                 double numer = p.sub(a).dot(D);
271                 if (numer <= 0.0f)
272                         return a;
273                 double denom = D.dot(D);
274                 if (numer >= denom)
275                         return b;
276                 return a.add(D.multiply(numer / denom));
277         }
278 }