package optics; import utilities.*; import displays.*; import java.awt.*; import java.awt.event.*; import java.applet.Applet; /************************************************************************** Optics Applet Generates applets with a control panel, layout image, camera image, and intensity image.

Parent class for all optics applets controls, graphic inputs and outputs and performs relevant claculations. Child classes handle specific scrollers and provide a routine to calculate intensity at a specified loaction. Screen (pixel) coordinates and actual coordinates are related by a scale factor. The centre of the pattern is always at (0,0) in real spatial coordinates. Distances are in mm.
Tim McIntyre
April 2006
V3.2 **************************************************************************/ abstract public class Optics extends java.applet.Applet implements Runnable, ActionListener, MouseListener, MouseMotionListener{ private volatile Thread runner; Image[] workspace; // Offscreen images Graphics[] offScreen; // Offscreen graphics int[] left, top; // Position of graphical devices int width, height; Panel thePanel, viewPanel; Button leftButton, rightButton; Button zoomInButton, zoomOutButton; Button defaultButton, displayButton; Button startButton; Button amplifyUpButton,amplifyDownButton; TextField xText, yText, intensityText; TColourScreen camera, layout; XYGraph timeHistory, linearArray; int timeDelay = 100; int mouseDragX, mouseDragY; // Position from centre of Camera Image boolean mouseDragging = false; // Mouse being dragged - draw box int detectorMax = 1000; // Full intensity on detector double screenX, screenY; // Physical position in space of centre of Camera Image int showWhat = 0; // Decides what to display 0 = linear array, 1 = history, 2 = layout boolean isLinear = false; // Symmetry of pattern - circular or linear double observationScreen = 1000; // Distance to observation screen in real units double scaleDefault = 50; // Initial width of display in real units double scale; // Current width of display in real units double amplification = 1; // Allows amplification of the signal int wavelengths = 1; // Number of wavelengths that are simultaneously drawn boolean erase = true; boolean paused = false; // Controls starting and stopping of applet //******* Constructors/Initialising routines: /************************************* Initialises all devices and displays. SHould be called first by child routines. *************************************/ public void init() { workspace = new Image[3]; offScreen = new Graphics[3]; left = new int[3]; top = new int[3]; camera = new TColourScreen(); layout = new TColourScreen(); timeHistory = new XYGraph(); linearArray = new XYGraph(); leftButton = new Button("Move Left"); leftButton.addActionListener(this); rightButton = new Button("Move Right"); rightButton.addActionListener(this); zoomInButton = new Button("Zoom In"); zoomInButton.addActionListener(this); zoomOutButton = new Button("Zoom Out"); zoomOutButton.addActionListener(this); startButton = new Button("Continue"); startButton.addActionListener(this); defaultButton = new Button("Default"); defaultButton.addActionListener(this); displayButton = new Button("Show History"); displayButton.addActionListener(this); amplifyUpButton = new Button("Amplify"); amplifyUpButton.addActionListener(this); amplifyDownButton = new Button("De-Amplify"); amplifyDownButton.addActionListener(this); xText = new TextField(5); yText = new TextField(5); intensityText = new TextField(5); addMouseListener(this); addMouseMotionListener(this); reset(); } /************************************* Initialises the control panel - should be called after adding appropriate controllers *************************************/ public void setControlPanel() { Font times = new Font("Times Roman", Font.PLAIN, 12); viewPanel = new Panel(); viewPanel.setLayout(new GridLayout(6,2,5,5)); viewPanel.setBackground(TColor.backB); Panel p1 = new Panel(); viewPanel.add(displayButton); viewPanel.add(defaultButton); viewPanel.add(leftButton); viewPanel.add(rightButton); viewPanel.add(zoomInButton); viewPanel.add(zoomOutButton); viewPanel.add(amplifyUpButton); viewPanel.add(amplifyDownButton); viewPanel.add(startButton); Panel xPanel = new Panel(); Label xlabel = new Label("x", Label.LEFT); xlabel.setFont(times); xPanel.add(xlabel); xPanel.add(xText); Label xmmlabel = new Label("mm", Label.LEFT); xmmlabel.setFont(times); xPanel.add(xmmlabel); viewPanel.add(xPanel); Panel iPanel = new Panel(); Label ilabel = new Label("Intensity", Label.LEFT); ilabel.setFont(times); iPanel.add(ilabel); iPanel.add(intensityText); viewPanel.add(iPanel); Panel yPanel = new Panel(); Label ylabel = new Label("y", Label.LEFT); ylabel.setFont(times); yPanel.add(ylabel); yPanel.add(yText); Label ymmlabel = new Label("mm", Label.LEFT); ymmlabel.setFont(times); yPanel.add(ymmlabel); viewPanel.add(yPanel); thePanel = new Panel(); thePanel.setLayout(new GridLayout(2,1,5,5)); thePanel.setBackground(TColor.applet); thePanel.add(getControlPanel()); thePanel.add(viewPanel); setLayout(new BorderLayout()); add("West",thePanel); setBackground(TColor.applet); } //******* Accessors /************************************* Gives the intensity at a particular point in real space @param x a double which is the x coordinate @param y a double which is the y coordinate @param p the index of the wavelength stored in the wavelengths array @return a double which is the intensity at the point in the range 0 - 1 *************************************/ abstract public double getIntensity(double x, double y, int p); // Must be supplied by the user to calculate the intensity (0-1) at x,y for pattern p /************************************* Gives the wavelength at a particular pattern @return a double which is the wavelength in nm of the pattern *************************************/ abstract public double getWavelength(int p); // Must be supplied by the user to calculate the wavelength in nm for pattern p /************************************* Procedure to calculate changes at each step betweens redrawing *************************************/ abstract public void Run(); /************************************* Procedure to generate the control panel for application specific inputs @return a Panel which has the user controls that are specific to this application *************************************/ abstract public Panel getControlPanel(); /************************************* Procedure to draw a diagram of the physical layout @param t a TColourScreen object to draw into @return a boolean which is true if the layout is drawn, and false otherwise *************************************/ abstract public boolean drawLayout(TColourScreen t); /************************************* Gives the distance to the observation screen @return a double which is the distance to the observation screen *************************************/ public double getObservationScreenDistance() { return observationScreen; } //******* Mutators /************************************* Procedure to set any initial parameters after the control panel has been drawn *************************************/ public void setInitial() { startButton.setLabel("Pause"); } /************************************* Sets whether the drawn pattern has circular symmetry or is linear (independent of y) @param isLinear a boolean which is true if the symmetry is linear *************************************/ public void setLinear(boolean isLinear) { this.isLinear = isLinear; } /************************************* Sets the number of wavelengths that are simultaneously drawn @param w the new number of wavelengths *************************************/ public void setWavelengths(int w) { wavelengths = w; } private void setSizes() { width = getWidth() - thePanel.getX() - thePanel.getWidth() - 5; height = (getHeight() - 5) / 2; for (int i = 0; i < 3; i++) { workspace[i] = createImage(width,height); if (workspace[i] != null) offScreen[i] = workspace[i].getGraphics(); } setPositions(0,thePanel.getX() + thePanel.getWidth() + 5, 0); setPositions(1,thePanel.getX() + thePanel.getWidth() + 5, getHeight() - height - 1); setPositions(2,thePanel.getX() + thePanel.getWidth() + 5, getHeight() - height - 1); setImages(); } private void setPositions(int Which, int Left, int Top) { top[Which] = Top; left[Which] = Left; } private void setImages() { camera.setSize(width,height); camera.setBorder(1); camera.setBorderColor(Color.white); camera.setScreen(offScreen[0]); camera.setCursorColor(Color.white); camera.setCursorOn(true); camera.setMouse(width/2,height/2); layout.setSize(width,height); layout.setBorder(1); layout.setBorderColor(Color.white); layout.setScreen(offScreen[2]); linearArray = new XYGraph(width-2*camera.getBorder(),width,height); linearArray.setForeground(Color.blue); linearArray.setBackground(TColor.backG); linearArray.setCursorColor(Color.red); linearArray.setCursorOn(true); linearArray.setBorder(1); linearArray.setBorderColor(Color.white); linearArray.setGraticuleColor(Color.white); linearArray.setXDivisions(4); linearArray.setYDivisions(4); linearArray.setLineOn(true); linearArray.setDotsOn(false); linearArray.setXLimits(0,width-2*camera.getBorder()); linearArray.setYLimits(0,1); timeHistory = new XYGraph(width-2*camera.getBorder(),width,height); timeHistory.setForeground(Color.blue); timeHistory.setBackground(TColor.backG); timeHistory.setCursorColor(Color.black); timeHistory.setCursorOn(false); timeHistory.setBorder(1); timeHistory.setBorderColor(Color.white); timeHistory.setGraticuleColor(Color.white); timeHistory.setXDivisions(4); timeHistory.setYDivisions(4); timeHistory.setLineOn(true); timeHistory.setDotsOn(false); timeHistory.setXLimits(0,width-2*camera.getBorder()); timeHistory.setYLimits(0,1); resetImages(); if (drawLayout(layout)) { showWhat = 2; displayButton.setLabel("Show Array"); } erase = true; } private void resetImages() { camera.setXLimits(screenX-scale/2,screenX+scale/2); camera.setYLimits(screenY-scale*height/width/2,screenY+scale*height/width/2); camera.setMouse(width/2,height/2); showMouseValues(); } private void reset() { screenX = 0; screenY = 0; scale = scaleDefault; amplification = 1; } private void showMouseValues() { double intensity = 0; double x = camera.getRealMouseX(); double y = camera.getRealMouseY(); for (int p = 0; p < wavelengths; p++) { intensity += getIntensity(x,y,p); } intensity /= wavelengths; xText.setText(TString.valueOf(x,2)); yText.setText(TString.valueOf(y,2)); intensityText.setText(TString.valueOf(intensity,2)); linearArray.setMouse((double)(camera.getMouseX()-camera.getBorder()),intensity*amplification); } /************************************* Draws images for circular symmetry centred on origin or linear symmetry - as determined by isCircular. Patterns describes the number of circles for each step - eg number of wavelengths present. User must ensure r, b and b are set correctly. *************************************/ public void calculateImages() { double[] intensityArray = new double[wavelengths]; double[] wavelengthArray = new double[wavelengths]; // Layout drawLayout(layout); // Camera camera.erase(); int offset = 0; if (!isLinear) { offset = height / 2; } for (int i = camera.getBorder() - offset; i < width - camera.getBorder() + offset; i++) { double x = camera.getPixelToX(i); int pradius = (int)(x/camera.getXPerPixel()); int pdiameter = (int)(2*x/camera.getXPerPixel()); double y = camera.getPixelToY(height/2); double intensity = 0; for (int p = 0; p < wavelengths; p++) { intensityArray[p] = getIntensity(x,y,p)*amplification; intensity += intensityArray[p]; wavelengthArray[p] = getWavelength(p); } camera.setColor(WColor.getMultiColor(wavelengthArray,intensityArray,wavelengths)); intensity /= wavelengths; if (isLinear) { camera.drawLine(i,0,i,height); } else { if (pradius < 0) camera.drawOval(i,height/2+pradius,-pdiameter,-pdiameter); else camera.drawOval(i-pdiameter,height/2-pradius,pdiameter,pdiameter); } } if (mouseDragging) { camera.setColor(Color.white); camera.drawRect(camera.getMouseX(),camera.getMouseY(),mouseDragX-camera.getMouseX(),mouseDragY-camera.getMouseY()); } camera.draw(); // Linear Array linearArray.reset(); for (int i = 0; i < width; i++) { double x = camera.getPixelToX(i + camera.getBorder()); double y = camera.getRealMouseY(); double intensity = 0; for (int p = 0; p < wavelengths; p++) { intensity += getIntensity(x,y,p); } intensity /= wavelengths; linearArray.add(i,intensity*amplification); } // Time history double x = camera.getRealMouseX(); double y = camera.getRealMouseY(); double intensity = 0; for (int p = 0; p < wavelengths; p++) { intensity += getIntensity(x,y,p); } intensity /= wavelengths; if (!paused) { if (timeHistory.getPoints() == width - 2 * camera.getBorder()) timeHistory.shift(1); timeHistory.add(timeHistory.getPoints(),intensity*amplification); } } //******* Event handling /************************************* Starts the applet *************************************/ public void start() { if (runner == null) { runner = new Thread(this); runner.start(); } } /************************************* Runs the applet *************************************/ public void run() { // Steps time Thread thisThread = Thread.currentThread(); if (runner == thisThread.currentThread()) { while (thePanel.getHeight() == 0) { try { thisThread.sleep(timeDelay*3); } catch (InterruptedException e) {} } setSizes(); } setInitial(); while (runner == thisThread.currentThread()) { if (!paused) { Run(); } calculateImages(); showMouseValues(); repaint(); try { Thread.sleep(timeDelay); } catch (InterruptedException e) { } } } /************************************ Responds to a button press @param e the ActionEvent that was initiated by the button press ************************************/ public void actionPerformed(ActionEvent e) { if (e.getSource() == leftButton) { screenX -= width/2*camera.getXPerPixel(); resetImages(); } else if (e.getSource() == rightButton) { screenX += width/2*camera.getXPerPixel(); resetImages(); } else if (e.getSource() == startButton) { paused = !paused; if (paused) startButton.setLabel("Continue"); else startButton.setLabel("Pause"); } else if (e.getSource() == zoomInButton) { scale /= 2; resetImages(); } else if (e.getSource() == zoomOutButton) { scale *= 2; resetImages(); } else if (e.getSource() == amplifyUpButton) { amplification *= 2; resetImages(); } else if (e.getSource() == amplifyDownButton) { amplification /= 2; resetImages(); } else if (e.getSource() == defaultButton) { reset(); resetImages(); } else if (e.getSource() == displayButton) { showWhat += 1; if (showWhat == 3 || (showWhat == 2 && !drawLayout(layout))) showWhat = 0; if (showWhat == 0) displayButton.setLabel("Show History"); else if (showWhat == 1 && drawLayout(layout)) displayButton.setLabel("Show Layout"); else displayButton.setLabel("Show Array"); } } /************************************* Responds to a mouse click (pressed and released). @param e the MouseEvent that occurred *************************************/ public void mouseClicked(MouseEvent e) { } /************************************* Responds to a mouse button being pressed @param e the MouseEvent that occurred *************************************/ public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (camera.isInDisplay(x - left[0], y - top[0])) { camera.setMouse(x - left[0], y - top[0]); showMouseValues(); } else if (showWhat == 0 && linearArray.isInDisplay(x - left[1], y - top[1])) { camera.setMouse(x - left[1], camera.getMouseY()); showMouseValues(); } } /************************************* Responds to a mouse button being released @param e the MouseEvent that occurred *************************************/ public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); int dx; mouseDragging = false; camera.setCursorOn(true); if (camera.isInDisplay(x - left[0], y - top[0])) { mouseDragX = x - left[0]; mouseDragY = y - top[0]; if (Math.abs(mouseDragX - camera.getMouseX()) > 10) { scale = Math.abs(camera.getPixelToX(mouseDragX) - camera.getRealMouseX()); screenX = (camera.getPixelToX(mouseDragX) + camera.getRealMouseX())/2; resetImages(); } } } /************************************* Responds to a mouse entering an object. @param e the MouseEvent that occurred *************************************/ public void mouseEntered(MouseEvent e) { } /************************************* Responds to a mouse leaving an object. @param e the MouseEvent that occurred *************************************/ public void mouseExited(MouseEvent e) { } /************************************* Responds to the mouse being dragged @param e the MouseEvent that occurred *************************************/ public void mouseDragged(MouseEvent e) { // Responds to a mouse drag int x = e.getX(); int y = e.getY(); if (camera.isInDisplay(x - left[0], y - top[0])) { mouseDragX = x - left[0]; mouseDragY = y - top[0]; mouseDragging = true; camera.setCursorOn(false); } } /************************************* Responds to the mouse moving. @param e the MouseEvent that occurred *************************************/ public void mouseMoved(MouseEvent e) { } /************************************* Draws the graphics objects @param g the Graphics object to draw into *************************************/ public void paint(Graphics g) { if (workspace != null && workspace[0] != null) { g.drawImage(workspace[0],left[0],top[0],this); } if (workspace != null && workspace[1] != null) { if (showWhat == 0) linearArray.draw(offScreen[1]); else timeHistory.draw(offScreen[1]); if (showWhat == 2) g.drawImage(workspace[2],left[2],top[2],this); else g.drawImage(workspace[1],left[1],top[1],this); } } /************************************* Redraws the screen without an erase @param g the Graphics object to draw into *************************************/ public void update(Graphics g) { if (erase) { erase = false; super.update(g); } else paint(g); } }