/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.frames; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.ButtonGroup; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JRadioButtonMenuItem; import org.opensourcephysics.display.DisplayRes; import org.opensourcephysics.display.Drawable; import org.opensourcephysics.display.DrawingFrame; import org.opensourcephysics.display.InteractivePanel; import org.opensourcephysics.display.PlottingPanel; import org.opensourcephysics.display2d.ArrayData; import org.opensourcephysics.display2d.ComplexColorMapper; import org.opensourcephysics.display2d.ComplexGridPlot; import org.opensourcephysics.display2d.ComplexInterpolatedPlot; import org.opensourcephysics.display2d.ComplexSurfacePlot; import org.opensourcephysics.display2d.GridData; import org.opensourcephysics.display2d.Plot2D; import org.opensourcephysics.display2d.SurfacePlotMouseController; import org.opensourcephysics.numerics.FFT2D; /** * FFT2DFrame computes a 2D FFT and displays the result as a complex grid plot. * * @author W. Christian * @version 1.0 */ public class FFT2DFrame extends DrawingFrame { static final double PI2 = Math.PI*2; public static final int MODE = 0; public static final int FREQ = 1; public static final int OMEGA = 2; public static final int WAVENUMBER = 3; public static final int MOMENTUM = 4; protected int domainType = WAVENUMBER; GridData gridData; FFT2D fft; double[] fftData; Plot2D plot = new ComplexGridPlot(null); // Plot2D plot = new ComplexInterpolatedPlot(null); SurfacePlotMouseController surfacePlotMC; JMenuItem gridItem, interpolatedItem, surfaceItem; /** * Constructs a Complex2DFrame with the given axes labels and frame title. * @param xlabel String * @param ylabel String * @param frameTitle String */ public FFT2DFrame(String xlabel, String ylabel, String frameTitle) { super(new PlottingPanel(xlabel, ylabel, null)); drawingPanel.setPreferredSize(new Dimension(350, 350)); setTitle(frameTitle); ((PlottingPanel) drawingPanel).getAxes().setShowMajorXGrid(false); ((PlottingPanel) drawingPanel).getAxes().setShowMajorYGrid(false); drawingPanel.addDrawable(plot); addMenuItems(); setAnimated(true); setAutoclear(true); } /** * Constructs a Complex2DFrame with the given frame title but without axes. * @param frameTitle String */ public FFT2DFrame(String frameTitle) { super(new InteractivePanel()); setTitle(frameTitle); drawingPanel.addDrawable(plot); addMenuItems(); setAnimated(true); setAutoclear(true); } /** * Adds Views menu items on the menu bar. */ protected void addMenuItems() { JMenuBar menuBar = getJMenuBar(); if(menuBar==null) { return; } JMenu helpMenu = this.removeMenu(DisplayRes.getString("DrawingFrame.Help_menu_item")); //$NON-NLS-1$ JMenu menu = getMenu(DisplayRes.getString("DrawingFrame.Views_menu")); //$NON-NLS-1$ if(menu==null) { menu = new JMenu(DisplayRes.getString("DrawingFrame.Views_menu")); //$NON-NLS-1$ menuBar.add(menu); menuBar.validate(); } else { // add a separator if tools already exists menu.addSeparator(); } if(helpMenu!=null) { menuBar.add(helpMenu); } ButtonGroup menubarGroup = new ButtonGroup(); // grid plot menu item gridItem = new JRadioButtonMenuItem(DisplayRes.getString("Scalar2DFrame.MenuItem.GridPlot")); //$NON-NLS-1$ menubarGroup.add(gridItem); gridItem.setSelected(true); ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent e) { convertToGridPlot(); } }; gridItem.addActionListener(actionListener); menu.add(gridItem); // surface plot menu item surfaceItem = new JRadioButtonMenuItem(DisplayRes.getString("Scalar2DFrame.MenuItem.SurfacePlot")); //$NON-NLS-1$ menubarGroup.add(surfaceItem); actionListener = new ActionListener() { public void actionPerformed(ActionEvent e) { convertToSurfacePlot(); } }; surfaceItem.addActionListener(actionListener); menu.add(surfaceItem); // interpolated plot menu item interpolatedItem = new JRadioButtonMenuItem(DisplayRes.getString("Scalar2DFrame.MenuItem.InterpolatedPlot")); //$NON-NLS-1$ menubarGroup.add(interpolatedItem); actionListener = new ActionListener() { public void actionPerformed(ActionEvent e) { convertToInterpolatedPlot(); } }; interpolatedItem.addActionListener(actionListener); menu.add(interpolatedItem); // add phase legend to tool menu menu.addSeparator(); JMenuItem phaseItem = new JMenuItem(DisplayRes.getString("GUIUtils.PhaseLegend")); //$NON-NLS-1$ actionListener = new ActionListener() { public void actionPerformed(ActionEvent e) { ComplexColorMapper.showPhaseLegend(); } }; phaseItem.addActionListener(actionListener); menu.add(phaseItem); } /* * Converts to an InterpolatedPlot plot. */ public void convertToInterpolatedPlot() { if(!(plot instanceof ComplexInterpolatedPlot)) { if(surfacePlotMC!=null) { drawingPanel.removeMouseListener(surfacePlotMC); drawingPanel.removeMouseMotionListener(surfacePlotMC); surfacePlotMC = null; } drawingPanel.removeDrawable(plot); plot = new ComplexInterpolatedPlot(gridData); drawingPanel.addDrawable(plot); drawingPanel.repaint(); interpolatedItem.setSelected(true); } } /* * Converts to a GridPlot plot. */ public void convertToGridPlot() { if(!(plot instanceof ComplexGridPlot)) { if(surfacePlotMC!=null) { drawingPanel.removeMouseListener(surfacePlotMC); drawingPanel.removeMouseMotionListener(surfacePlotMC); surfacePlotMC = null; } drawingPanel.removeDrawable(plot); plot = new ComplexGridPlot(gridData); drawingPanel.addDrawable(plot); drawingPanel.invalidateImage(); drawingPanel.repaint(); gridItem.setSelected(true); } } /** * Converts to a SurfacePlot plot. */ public void convertToSurfacePlot() { if(!(plot instanceof ComplexSurfacePlot)) { drawingPanel.removeDrawable(plot); plot = new ComplexSurfacePlot(gridData); drawingPanel.addDrawable(plot); drawingPanel.invalidateImage(); drawingPanel.repaint(); if(surfacePlotMC==null) { surfacePlotMC = new SurfacePlotMouseController(drawingPanel, plot); } drawingPanel.addMouseListener(surfacePlotMC); drawingPanel.addMouseMotionListener(surfacePlotMC); surfaceItem.setSelected(true); } } /** * Resizes the grid used to store the fft using the given spacial min/max values. * * @param nx int * @param ny int * @param xmin double * @param xmax double * @param ymin double * @param ymax double */ private void resizeGrid(int nx, int ny) { fftData = new double[2*nx*ny]; fft = new FFT2D(nx, ny); gridData = new ArrayData(nx, ny, 3); // a grid with three data components plot.setGridData(gridData); plot.update(); drawingPanel.invalidateImage(); drawingPanel.repaint(); } /** * Sets the units for the FFT output. * Domain types are: MODE, FREQ, OMEGA, WAVENUMBER, MOMENTUM * * @param type int */ public void setDomainType(int type) { domainType = type; switch(domainType) { case MODE : if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setXLabel("x mode"); //$NON-NLS-1$ } if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setYLabel("y mode"); //$NON-NLS-1$ } break; case FREQ : if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setXLabel("x frequency"); //$NON-NLS-1$ } if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setYLabel("y frequency"); //$NON-NLS-1$ } break; case OMEGA : if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setXLabel("x omega"); //$NON-NLS-1$ } if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setYLabel("y omega"); //$NON-NLS-1$ } break; case WAVENUMBER : if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setXLabel("k_x"); //$NON-NLS-1$ } if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setYLabel("k_y"); //$NON-NLS-1$ } break; case MOMENTUM : if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setXLabel("p_x"); //$NON-NLS-1$ } if(drawingPanel instanceof PlottingPanel) { ((PlottingPanel) drawingPanel).setYLabel("p_y"); //$NON-NLS-1$ } break; } } /** * Does an FFT of the given data array and repaints the panel. * * data[0][][] is assumed to contain the real components of the field. * data[1][][] is assumed to contain the imaginary components of the field. * * @param data double[][][] complex field values * @param xmin double * @param xmax double * @param ymin double * @param ymax double */ public void doFFT(double[][][] data, double xmin, double xmax, double ymin, double ymax) { if(gridData==null) { throw new IllegalStateException("Grid must be set before using row-major format."); //$NON-NLS-1$ } int nx = gridData.getNx(), ny = gridData.getNy(); if((data[0].length!=nx)||(data[0][0].length!=ny)) { throw new IllegalArgumentException("Grid does not have the correct size."); //$NON-NLS-1$ } double[][] reData = data[0]; double[][] imData = data[1]; int offX = (int) (nx*xmin/(xmax-xmin)); offX = Math.abs(offX); int offY = (int) (ny*ymin/(ymax-ymin)); offY = Math.abs(offY); for(int i = 0; i<nx; i++) { int ii = (offX+i)%nx; int offset = 2*ii*nx; for(int j = 0; j<ny; j++) { int jj = (offX+j)%ny; fftData[offset+2*jj] = reData[i][j]; fftData[offset+2*jj+1] = imData[i][j]; } } fft.transform(fftData); fft.toNaturalOrder(fftData); // double[] fx= fft.getNaturalOmegaX(xmin,xmax); // double[] fy= fft.getNaturalOmegaY(ymin,ymax); // gridData.setCellScale(fx[0], fx[nx-1],fy[0], fy[ny-1] ); double a1 = -nx/2, a2 = (nx+1)/2-1, b1 = -ny/2, b2 = (ny+1)/2-1; switch(domainType) { case MODE : break; case FREQ : a2 = fft.getFreqMax(xmin, xmax, nx); a1 = fft.getFreqMin(xmin, xmax, nx); b2 = fft.getFreqMax(ymin, ymax, ny); b1 = fft.getFreqMin(ymin, ymax, ny); break; case OMEGA : case MOMENTUM : case WAVENUMBER : a2 = PI2*fft.getFreqMax(xmin, xmax, nx); a1 = PI2*fft.getFreqMin(xmin, xmax, nx); b2 = PI2*fft.getFreqMax(ymin, ymax, ny); b1 = PI2*fft.getFreqMin(ymin, ymax, ny); break; } gridData.setCenteredCellScale(a1, a2, b2, b1); fillGrid(nx, ny, fftData); plot.update(); drawingPanel.invalidateImage(); drawingPanel.repaint(); } /* * Does an FFT of the given data array and repaints the panel. * * The data array is assumed to contain complex numbers in row-major format. * The given array remains unchanged. * * @param nx int * @param data double[] * @param xmin double * @param xmax double * @param ymin double * @param ymax double */ public void doFFT(double[] data, int nx, double xmin, double xmax, double ymin, double ymax) throws IllegalArgumentException { if((data.length/2)%nx!=0) { throw new IllegalArgumentException("Number of values in grid (nx*ny) must match number of values."); //$NON-NLS-1$ } int ny = data.length/nx/2; resizeGrid(nx, ny); // int nx = gridData.getNx(), ny = gridData.getNy(); if(data.length!=2*nx*ny) { throw new IllegalArgumentException("Grid does not have the correct size."); //$NON-NLS-1$ } int offX = (int) (nx*xmin/(xmax-xmin)); offX = Math.abs(offX); int offY = (int) (ny*ymin/(ymax-ymin)); offY = Math.abs(offY); for(int j = 0; j<ny; j++) { int jj = (offY+j)%ny; int offset = 2*j*nx; int offset2 = 2*jj*nx; for(int i = 0; i<nx; i++) { int ii = (offX+i)%nx; fftData[offset+2*ii] = data[offset2+2*i]; fftData[offset+2*ii+1] = data[offset2+2*i+1]; } } fft.transform(fftData); fft.toNaturalOrder(fftData); // double[] fx= fft.getNaturalModes(nx); // double[] fy= fft.getNaturalModes(ny); // gridData.setCellScale(fx[0], fx[nx-1], fy[ny-1],fy[0] ); double a1 = -nx/2, a2 = (nx+1)/2-1, b1 = -ny/2, b2 = (ny+1)/2-1; switch(domainType) { case MODE : break; case FREQ : a2 = fft.getFreqMax(xmin, xmax, nx); a1 = fft.getFreqMin(xmin, xmax, nx); b2 = fft.getFreqMax(ymin, ymax, ny); b1 = fft.getFreqMin(ymin, ymax, ny); break; case OMEGA : case MOMENTUM : case WAVENUMBER : a2 = PI2*fft.getFreqMax(xmin, xmax, nx); a1 = PI2*fft.getFreqMin(xmin, xmax, nx); b2 = PI2*fft.getFreqMax(ymin, ymax, ny); b1 = PI2*fft.getFreqMin(ymin, ymax, ny); break; } gridData.setCenteredCellScale(a1, a2, b2, b1); fillGrid(nx, ny, fftData); plot.update(); drawingPanel.invalidateImage(); drawingPanel.repaint(); } /** * Removes drawable objects added by the user from this frame. */ public void clearDrawables() { drawingPanel.clear(); // removes all drawables drawingPanel.addDrawable(plot); } /** * Gets Drawable objects added by the user to this frame. * * @return the list */ public synchronized ArrayList<Drawable> getDrawables() { ArrayList<Drawable> list = super.getDrawables(); list.remove(plot); return list; } /** * Gets Drawable objects added by the user of an assignable type. The list contains * objects that are assignable from the class or interface. * * @param c the type of Drawable object * * @return the cloned list * * @see #getObjectOfClass(Class c) */ public synchronized <T extends Drawable> ArrayList<T> getDrawables(Class<T> c) { ArrayList<T> list = super.getDrawables(c); list.remove(plot); return list; } private void fillGrid(int nx, int ny, double[] vals) { double[][] mag = gridData.getData()[0]; // magnitude maps to intensity double[][] reData = gridData.getData()[1]; double[][] imData = gridData.getData()[2]; for(int j = 0; j<ny; j++) { int offset = 2*j*nx; for(int i = 0; i<nx; i++) { double re = vals[offset+2*i]; double im = vals[offset+2*i+1]; mag[i][j] = Math.sqrt(re*re+im*im); reData[i][j] = re; imData[i][j] = im; } } } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */