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