-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSampleImageConfigurator.java
348 lines (314 loc) · 12.2 KB
/
SampleImageConfigurator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
import javax.swing.JFrame;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.BorderLayout;
import javax.swing.*;
import blayzeTechUtils.env.TPolygonEntity;
import blayzeTechUtils.env.TPPolygonEntity;
import blayzeTechUtils.math.Point;
// Listeners
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class SampleImageConfigurator extends JFrame implements ActionListener {
public static class ImageContainer extends JPanel {
public TPolygonEntity reticule;
private TPPolygonEntity subReticule;
public TPolygonEntity directLinkReticule;
public BufferedImage image = null;
public BufferedImage NNmaskImage = null;
private BufferedImage NNmaskDisplay = null;
private boolean usingNNmask = false;
public BufferedImage DPmaskImage = null;
private BufferedImage DPmaskDisplay = null;
private boolean usingDPmask = false;
public ImageContainer (int w, int h)
{
super();
this.setPreferredSize(new Dimension(w, h));
// Draw the reticule
int reticuleResolution = 100;// Maybe this should be the hoz. resolution of the output?
Point[] reticulePoints = new Point[reticuleResolution +1];
for(int i = 0; i<reticuleResolution; i++)
{
double angle = i * (Math.PI*2/reticuleResolution);
reticulePoints[i] = new Point(Math.cos(angle)*0.5, Math.sin(angle)*0.5);
}
reticulePoints[reticuleResolution] = new Point(0.25,0);
reticule = new TPolygonEntity(w/2.0,h/2.0,reticulePoints);
reticule.setXscale(100);
reticule.setYscale(100);
// Draw the subreticule
Point[] subretPoints = new Point[6];
subretPoints[0] = new Point(0, -0.5);
subretPoints[1] = new Point(0, 0.5);
subretPoints[2] = new Point(0, 0);
subretPoints[3] = new Point(-0.5, 0);
subretPoints[4] = new Point(0.5, 0);
subretPoints[5] = new Point(0, 0);
subReticule = new TPPolygonEntity(0,0,subretPoints, reticule);
// Draw the direct link reticule
directLinkReticule = new TPolygonEntity(0.0,0.0,subretPoints);
directLinkReticule.setXscale(30);
directLinkReticule.setYscale(30);
}
// Checks if the reticule is within the bounds of the image, and moves it inward if not.
public void bounceReticule()
{
// The outer reticule has a radius of 0.5 and a diameter of 1, a.k.a. it's scale is it's total diameter.
double minImageDimension = Math.min(image.getWidth()-1, image.getHeight()-1);
reticule.setScale(Math.min(reticule.getXscale(), minImageDimension));
int radius = (int)Math.ceil(reticule.getXscale()/2.0);// Ceil to avoid half-pixel cliping
reticule.setX(Math.min(Math.max(reticule.getX(), radius), image.getWidth()-radius-1));
reticule.setY(Math.min(Math.max(reticule.getY(), radius), image.getHeight()-radius-1));
}
public void setImage(BufferedImage img)
{
image = img;
Dimension imgDim = new Dimension(img.getWidth(), img.getHeight());
setPreferredSize(imgDim);
setSize(imgDim);
setMaximumSize(imgDim);
reticule.setX(imgDim.getWidth()/2.0);
reticule.setY(imgDim.getHeight()/2.0);
bounceReticule();
repaint();
revalidate();
}
public void setNNmaskImage(BufferedImage img)
{
if(!dimensionCheck(img))
return;
// Save the mask image
usingNNmask = true;
NNmaskImage = img;
// Generate the display image
NNmaskDisplay = generateOutline(img, Color.GREEN);
repaint();
}
public void setNNmaskImage(boolean enableDisable)
{
usingNNmask = enableDisable;
repaint();
}
public boolean isNNmaskEnabled()
{
return usingNNmask;
}
public void setDPmaskImage(BufferedImage img)
{
if(!dimensionCheck(img))
return;
// Save the mask image
usingDPmask = true;
DPmaskImage = img;
// Generate the display image
DPmaskDisplay = generateOutline(img, Color.RED);
repaint();
}
public void setDPmaskImage(boolean enableDisable)
{
usingDPmask = enableDisable;
repaint();
}
public boolean isDPmaskEnabled()
{
return usingDPmask;
}
/**
* Checks that the given image has the same dimensions as the primary image
*/
private boolean dimensionCheck(BufferedImage img)
{
if(img.getWidth() == image.getWidth() && img.getHeight() == image.getHeight())
return true;
System.out.println("ERROR: The provided mask does not possess the same dimensions as the sample image.");
return false;
}
private BufferedImage generateOutline(BufferedImage img, Color c)
{
// Note: Now this method uses img.getRGB(x,y)&0xff to get the value of the color
// instead of (new Color(img.getRGB(x,y))).getRed() in an effort to save on RAM
// (and hopefully cycles)
System.out.print("Generating mask outline... ");
int width = img.getWidth();
int height = img.getHeight();
BufferedImage disp = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
{
boolean blackNear = false;
if((img.getRGB(x,y)&0xff) >= 128)
for(int i = Math.max(0,x-1); i<=Math.min(width-1,x+1); i++)
for(int o = Math.max(0,y-1); o<=Math.min(height-1, y+1); o++)
{
if(i == x && o == y)
continue;
if((img.getRGB(i,o)&0xff) < 128)
blackNear = true;
}
if(blackNear)
disp.setRGB(x,y,c.getRGB());
}
System.out.println("done!");
return disp;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g.setColor(Color.ORANGE);
g2d.drawImage(image,0,0,getWidth(),getHeight(), null);
if(NNmaskDisplay != null && usingNNmask)
g2d.drawImage(NNmaskDisplay,0,0,getWidth(),getHeight(), null);
if(DPmaskDisplay != null && usingDPmask)
{
g2d.drawImage(DPmaskDisplay,0,0,getWidth(),getHeight(), null);
directLinkReticule.draw(g2d);
}
reticule.draw(g2d);
subReticule.draw(g2d);
}
}
private static enum ControlButtonType {
UP, DOWN, LEFT, RIGHT, UPSCALE, DOWNSCALE, CLOCKWISE_ROTATE, COUNTERCLOCKWISE_ROTATE;
private static final String[] stringValues = new String[]{"^","v","<",">","*","/","CW","CCW"};
@Override
public String toString()
{
switch(this)
{
case UP: return(stringValues[0]);
case DOWN: return(stringValues[1]);
case LEFT: return(stringValues[2]);
case RIGHT: return(stringValues[3]);
case UPSCALE: return(stringValues[4]);
case DOWNSCALE: return(stringValues[5]);
case CLOCKWISE_ROTATE: return(stringValues[6]);
case COUNTERCLOCKWISE_ROTATE: return(stringValues[7]);
default: break;
}
return super.toString();
}
public static ControlButtonType fromString(String s)
{
for(ControlButtonType bt : ControlButtonType.values())
if(s.equals(bt.toString()))
return bt;
return null;
}
}
private JScrollPane scrollPane;
public ImageContainer imageContainer;
private JTextField moveSpeedSelector, scaleSelector, rotationSelector; // Contain the actual information here (A little sloppy, bit will work)
public SampleImageConfigurator(){
// Setup
super();
this.setLayout(new BorderLayout());
setTitle("Sample Image Configurator");
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Sample Image Display
JPanel containerContainer = new JPanel();// Put the container in a container to center it
imageContainer = new ImageContainer(1280, 720);
containerContainer.add(imageContainer);
scrollPane = new JScrollPane(containerContainer);
this.add(scrollPane, BorderLayout.CENTER);
//// The control buttons:
JPanel controlBtns = new JPanel();
for(ControlButtonType t : ControlButtonType.values())
{
JButton btn = new JButton(t.toString());
btn.addActionListener(this);
controlBtns.add(btn);
}
// The control text fields:
controlBtns.add(new JLabel("Move Speed:"));
moveSpeedSelector = new JTextField("1");
int newSize = (int)(moveSpeedSelector.getPreferredSize().width*5);
moveSpeedSelector.setPreferredSize(new Dimension(newSize, moveSpeedSelector.getPreferredSize().height));
controlBtns.add(moveSpeedSelector);
controlBtns.add(new JLabel("Radius:"));
scaleSelector = new JTextField("100");
scaleSelector.setPreferredSize(new Dimension(newSize, scaleSelector.getPreferredSize().height));
scaleSelector.addActionListener(this);
controlBtns.add(scaleSelector);
controlBtns.add(new JLabel("Rotation (degrees):"));
rotationSelector = new JTextField("0");
rotationSelector.setPreferredSize(new Dimension(newSize, rotationSelector.getPreferredSize().height));
rotationSelector.addActionListener(this);
controlBtns.add(rotationSelector);
this.add(controlBtns, BorderLayout.SOUTH);
// The Instructions
this.add(new JLabel("Align reticule with location of top reflective ray hits"), BorderLayout.NORTH);
// Prepare top-level UI
pack();
setResizable(true);
setVisible(true);
setLocationRelativeTo(null);
}
private void rerender()
{
imageContainer.reticule.setScale(Double.valueOf(scaleSelector.getText()));
imageContainer.reticule.setRotation(Double.valueOf(rotationSelector.getText())/180 * Math.PI);
}
public void actionPerformed(ActionEvent e)
{
rerender();
try{
double alterSpeed = Double.valueOf(moveSpeedSelector.getText());
switch(ControlButtonType.fromString(e.getActionCommand()))
{
case UP:
imageContainer.reticule.setY(imageContainer.reticule.getY() - alterSpeed);
break;
case DOWN:
imageContainer.reticule.setY(imageContainer.reticule.getY() + alterSpeed);
break;
case LEFT:
imageContainer.reticule.setX(imageContainer.reticule.getX() - alterSpeed);
break;
case RIGHT:
imageContainer.reticule.setX(imageContainer.reticule.getX() + alterSpeed);
break;
case UPSCALE:
imageContainer.reticule.setScale(imageContainer.reticule.getXscale() + alterSpeed);
scaleSelector.setText(Double.toString(imageContainer.reticule.getXscale()));
break;
case DOWNSCALE:
imageContainer.reticule.setScale(imageContainer.reticule.getXscale() - alterSpeed);
scaleSelector.setText(Double.toString(imageContainer.reticule.getXscale()));
break;
case COUNTERCLOCKWISE_ROTATE:
imageContainer.reticule.setRotation(imageContainer.reticule.getRotation() - alterSpeed/180*Math.PI);
rotationSelector.setText(Double.toString(imageContainer.reticule.getRotation()/Math.PI*180));
break;
case CLOCKWISE_ROTATE:
imageContainer.reticule.setRotation(imageContainer.reticule.getRotation() + alterSpeed/180*Math.PI);
rotationSelector.setText(Double.toString(imageContainer.reticule.getRotation()/Math.PI*180));
break;
default:
break;
}
}catch(Exception ex){
// This happens if we don't parse a regular command, but instead a string from one of the parameter options.
}
imageContainer.bounceReticule();
imageContainer.repaint();
SphericalDewarper.computeAndRenderVisualiser();
}
/**
* Returns the projected coordinate `angle` way around the reticule and `distanceFromCenter` from the center.
*
* @param angle the angle around the reticule this is (0-1)
* @param distanceFromCenter the distance from the center (0-1)
*/
public Point getCoordinateAt(double angle, double distanceFromCenter)
{
double trueDistance = distanceFromCenter*0.5;// Downscale to match the reticule
double trueAngle = angle*Math.PI*2;// Scale to be in full 360 deg range.
Point point = new Point(Math.cos(trueAngle)*trueDistance, Math.sin(trueAngle)*trueDistance);
return(imageContainer.reticule.projectToWorld(point));
}
}