/*
 * 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 javax.swing.KeyStroke;
import javax.swing.WindowConstants;
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.ColorMapper;
import org.opensourcephysics.display2d.ContourPlot;
import org.opensourcephysics.display2d.GrayscalePlot;
import org.opensourcephysics.display2d.GridData;
import org.opensourcephysics.display2d.GridPlot;
import org.opensourcephysics.display2d.GridTableFrame;
import org.opensourcephysics.display2d.InterpolatedPlot;
import org.opensourcephysics.display2d.Plot2D;
import org.opensourcephysics.display2d.SurfacePlot;
import org.opensourcephysics.display2d.SurfacePlotMouseController;

/**
 * A DrawingFrame that displays 2D plots of scalar fields.
 *
 * @author W. Christian
 * @version 1.0
 */
public class Scalar2DFrame extends DrawingFrame {
  String plotType = ""; //$NON-NLS-1$
  int paletteType = ColorMapper.SPECTRUM;
  boolean expanded = false;
  double expansionFactor = 1.0;
  boolean showGrid = true;
  GridData gridData;
  Plot2D plot = new GridPlot(null);
  SurfacePlotMouseController surfacePlotMC;
  JMenuItem surfaceItem, contourItem, gridItem, interpolatedItem, grayscaleItem;
  GridTableFrame tableFrame;
  //int[] gutters;

  /**
   * Constructs a Scalar2DFrame with the given axes labels and frame title.
   * @param xlabel String
   * @param ylabel String
   * @param frameTitle String
   */
  public Scalar2DFrame(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);
    //gutters = drawingPanel.getGutters();
  }

  /**
   * Constructs a Scalar2DFrame with the given frame title but without axes.
   * @param frameTitle String
   */
  public Scalar2DFrame(String frameTitle) {
    super(new InteractivePanel());
    setTitle(frameTitle);
    drawingPanel.addDrawable(plot);
    addMenuItems();
    setAnimated(true);
    setAutoclear(true);
    //gutters = drawingPanel.getGutters();
  }

  /**
   * Gets the x coordinate for the given index.
   *
   * @param i int
   * @return double the x coordiante
   */
  public double indexToX(int i) {
    if(gridData==null) {
      throw new IllegalStateException("Data has not been set.  Invoke setAll before invoking this method."); //$NON-NLS-1$
    }
    return gridData.indexToX(i);
  }

  /**
   * Gets the index that is closest to the given x value
   *
   * @return double the x coordiante
   */
  public int xToIndex(double x) {
    if(gridData==null) {
      throw new IllegalStateException("Data has not been set.  Invoke setAll before invoking this method."); //$NON-NLS-1$
    }
    return gridData.xToIndex(x);
  }

  /**
   * Gets the index that is closest to the given y value
   *
   * @return double the y coordiante
   */
  public int yToIndex(double y) {
    if(gridData==null) {
      throw new IllegalStateException("Data has not been set.  Invoke setAll before invoking this method."); //$NON-NLS-1$
    }
    return gridData.yToIndex(y);
  }

  /**
   * Gets the y coordinate for the given index.
   *
   * @param i int
   * @return double the y coordiante
   */
  public double indexToY(int i) {
    if(gridData==null) {
      throw new IllegalStateException("Data has not been set.  Invoke setAll before invoking this method."); //$NON-NLS-1$
    }
    return gridData.indexToY(i);
  }

  /**
   * Gets the number of x entries.
   * @return nx
   */
  public int getNx() {
    if(gridData==null) {
      return 0;
    }
    return gridData.getNx();
  }

  /**
   * Gets the number of y entries.
   * @return nx
   */
  public int getNy() {
    if(gridData==null) {
      return 0;
    }
    return gridData.getNy();
  }

  /**
   * Sets the autoscale flag and the floor and ceiling values for the colors.
   *
   * If autoscaling is true, then the min and max values of z are span the colors.
   *
   * If autoscaling is false, then floor and ceiling values limit the colors.
   * Values below min map to the first color; values above max map to the last color.
   *
   * @param isAutoscale
   * @param floor
   * @param ceil
   */
  public void setZRange(boolean isAutoscale, double floor, double ceil) {
    plot.setAutoscaleZ(isAutoscale, floor, ceil);
  }

  /**
   * Gets the maximum z value of the plot.
   * @return zmax
   */
  public double getCeiling() {
    return plot.getCeiling();
  }

  /**
   * Gets the minimum z value of the plot.
   * @return zmin
   */
  public double getFloor() {
    return plot.getFloor();
  }

  /**
   * Gets the autoscale flag for z.
   *
   * @return boolean
  */
  public boolean isAutoscaleZ() {
    return plot.isAutoscaleZ();
  }

  /**
   * Expands the z scale so as to enhance values close to zero.
   *
   * @param expanded boolean
   * @param expansionFactor double
   */
  public void setExpandedZ(boolean expanded, double expansionFactor) {
    this.expansionFactor = expansionFactor;
    this.expanded = expanded;
    plot.setExpandedZ(expanded, expansionFactor);
  }

  /**
   * Sets the color palette that will be used to color the scalar field.  Palette types are defined in ColorMapper.
   * @param type
   */
  public void setPaletteType(int type) {
    paletteType = type;
    plot.setPaletteType(type);
  }

  /**
   * Sets the buffered image option.
   *
   * Buffered panels copy the offscreen image into the panel during a repaint unless the image
   * has been invalidated.  Use the render() method to draw the image immediately.
   *
   * @param b
   */
  public void setBuffered(boolean b) {
    drawingPanel.setBuffered(b);
  }

  /**
   * Outlines the data grid's boundaries.
   *
   * @param showGrid
  */
  public void setShowGrid(boolean show) {
    showGrid = show;
    plot.setShowGridLines(show);
  }

  /**
   * True if the data grid's boundaries are shown.
   *
   * @return showGrid
  */
  public boolean isShowGrid() {
    return showGrid;
  }

  /**
   * 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("2DFrame.MenuItem.GridPlot")); //$NON-NLS-1$
    menubarGroup.add(gridItem);
    gridItem.setSelected(true);
    ActionListener tableListener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        convertToGridPlot();
      }

    };
    gridItem.addActionListener(tableListener);
    menu.add(gridItem);
    // contour plot menu item
    contourItem = new JRadioButtonMenuItem(DisplayRes.getString("2DFrame.MenuItem.ContourPlot")); //$NON-NLS-1$
    menubarGroup.add(contourItem);
    tableListener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        convertToContourPlot();
      }

    };
    contourItem.addActionListener(tableListener);
    menu.add(contourItem);
    // surface plot menu item
    surfaceItem = new JRadioButtonMenuItem(DisplayRes.getString("2DFrame.MenuItem.SurfacePlot")); //$NON-NLS-1$
    menubarGroup.add(surfaceItem);
    tableListener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        convertToSurfacePlot();
      }

    };
    surfaceItem.addActionListener(tableListener);
    menu.add(surfaceItem);
    // interpolated plot menu item
    interpolatedItem = new JRadioButtonMenuItem(DisplayRes.getString("2DFrame.MenuItem.InterpolatedPlot")); //$NON-NLS-1$
    menubarGroup.add(interpolatedItem);
    tableListener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        convertToInterpolatedPlot();
      }

    };
    interpolatedItem.addActionListener(tableListener);
    menu.add(interpolatedItem);
    // grayscale plot menu item
    grayscaleItem = new JRadioButtonMenuItem(DisplayRes.getString("2DFrame.MenuItem.GrayscalePlot")); //$NON-NLS-1$
    menubarGroup.add(grayscaleItem);
    tableListener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        convertToGrayscalePlot();
      }

    };
    grayscaleItem.addActionListener(tableListener);
    menu.add(grayscaleItem);
    menu.addSeparator();
    // a grid data table
    JMenuItem tableItem = new JMenuItem(DisplayRes.getString("DrawingFrame.DataTable_menu_item")); //$NON-NLS-1$
    tableItem.setAccelerator(KeyStroke.getKeyStroke('T', MENU_SHORTCUT_KEY_MASK));
    ActionListener actionListener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        showDataTable(true);
      }

    };
    tableItem.addActionListener(actionListener);
    menu.add(tableItem);
    // add data table to the popup menu
    if((drawingPanel!=null)&&(drawingPanel.getPopupMenu()!=null)) {
      JMenuItem item = new JMenuItem(DisplayRes.getString("DrawingFrame.DataTable_menu_item")); //$NON-NLS-1$
      item.addActionListener(actionListener);
      drawingPanel.getPopupMenu().add(item);
    }
  }

  /**
   * Clears 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;
  }

  /**
   * Sets the scalar field to zero.
   */
  public void clearData() {
    if(gridData!=null) {
      setAll(new double[gridData.getNx()][gridData.getNy()]);
    }
    drawingPanel.invalidateImage();
  }

  /**
   * Sets the type of plot type so that it cannot be changed from the menu.
   * @param type String
   */
  public void setPlotType(String type) {
    plotType = type;
    if(type.toLowerCase().equals("contour")) {             //$NON-NLS-1$
      convertToContourPlot();
      surfaceItem.setEnabled(false);
      contourItem.setEnabled(true);
      gridItem.setEnabled(false);
      interpolatedItem.setEnabled(false);
      grayscaleItem.setEnabled(false);
    } else if(type.toLowerCase().equals("grayscale")) {    //$NON-NLS-1$
      convertToGrayscalePlot();
      surfaceItem.setEnabled(false);
      contourItem.setEnabled(false);
      gridItem.setEnabled(false);
      interpolatedItem.setEnabled(false);
      grayscaleItem.setEnabled(true);
    } else if(type.toLowerCase().equals("grid")) {         //$NON-NLS-1$
      convertToGridPlot();
      surfaceItem.setEnabled(false);
      contourItem.setEnabled(false);
      gridItem.setEnabled(true);
      interpolatedItem.setEnabled(false);
      grayscaleItem.setEnabled(false);
    } else if(type.toLowerCase().equals("interpolated")) { //$NON-NLS-1$
      convertToInterpolatedPlot();
      surfaceItem.setEnabled(false);
      contourItem.setEnabled(false);
      gridItem.setEnabled(false);
      interpolatedItem.setEnabled(true);
      grayscaleItem.setEnabled(false);
    } else if(type.toLowerCase().equals("surface")) {      //$NON-NLS-1$
      convertToSurfacePlot();
      surfaceItem.setEnabled(true);
      contourItem.setEnabled(false);
      gridItem.setEnabled(false);
      interpolatedItem.setEnabled(false);
      grayscaleItem.setEnabled(false);
    } else {
      plotType = "";                                       //$NON-NLS-1$
      surfaceItem.setEnabled(true);
      contourItem.setEnabled(true);
      gridItem.setEnabled(true);
      interpolatedItem.setEnabled(true);
      grayscaleItem.setEnabled(true);
    }
  }

  /**
   * Converts to a contour plot.
   */
  public void convertToContourPlot() {
    if(!(plot instanceof ContourPlot)) {
      if(surfacePlotMC!=null) {
        drawingPanel.removeMouseListener(surfacePlotMC);
        drawingPanel.removeMouseMotionListener(surfacePlotMC);
        surfacePlotMC = null;
        // drawingPanel.setGutters(gutters);
        drawingPanel.resetGutters();
        drawingPanel.setClipAtGutter(true);
        if(drawingPanel instanceof PlottingPanel) {
          ((PlottingPanel) drawingPanel).getAxes().setVisible(true);
        }
        drawingPanel.setShowCoordinates(true);
      }
      boolean isAutoscaleZ = plot.isAutoscaleZ();
      double floor = plot.getFloor();
      double ceil = plot.getCeiling();
      Plot2D oldPlot = plot;
      plot = new ContourPlot(gridData);
      plot.setPaletteType(paletteType);
      if(expanded) {
        plot.setExpandedZ(expanded, expansionFactor);
      }
      plot.setAutoscaleZ(isAutoscaleZ, floor, ceil);
      drawingPanel.replaceDrawable(oldPlot, plot);
      plot.update();
      if((tableFrame!=null)&&tableFrame.isShowing()) {
        tableFrame.refreshTable();
      }
      drawingPanel.repaint();
      contourItem.setSelected(true);
    }
  }

  /*
   * Converts to an InterpolatedPlot plot.
   */
  public void convertToInterpolatedPlot() {
    if(!(plot instanceof InterpolatedPlot)) {
      if(surfacePlotMC!=null) {
        drawingPanel.removeMouseListener(surfacePlotMC);
        drawingPanel.removeMouseMotionListener(surfacePlotMC);
        surfacePlotMC = null;
        //drawingPanel.setGutters(gutters);
        drawingPanel.resetGutters();
        drawingPanel.setClipAtGutter(true);
        if(drawingPanel instanceof PlottingPanel) {
          ((PlottingPanel) drawingPanel).getAxes().setVisible(true);
        }
        drawingPanel.setShowCoordinates(true);
      }
      boolean isAutoscaleZ = plot.isAutoscaleZ();
      double floor = plot.getFloor();
      double ceil = plot.getCeiling();
      Plot2D oldPlot = plot;
      plot = new InterpolatedPlot(gridData);
      plot.setPaletteType(paletteType);
      if(expanded) {
        plot.setExpandedZ(expanded, expansionFactor);
      }
      plot.setAutoscaleZ(isAutoscaleZ, floor, ceil);
      drawingPanel.replaceDrawable(oldPlot, plot);
      plot.update();
      if((tableFrame!=null)&&tableFrame.isShowing()) {
        tableFrame.refreshTable();
      }
      drawingPanel.repaint();
      interpolatedItem.setSelected(true);
    }
  }

  /*
   * Converts to a GridPlot plot.
   */
  public void convertToGridPlot() {
    if(!(plot instanceof GridPlot)) {
      if(surfacePlotMC!=null) {
        drawingPanel.removeMouseListener(surfacePlotMC);
        drawingPanel.removeMouseMotionListener(surfacePlotMC);
        surfacePlotMC = null;
        //drawingPanel.setGutters(gutters);
        drawingPanel.resetGutters();
        drawingPanel.setClipAtGutter(true);
        if(drawingPanel instanceof PlottingPanel) {
          ((PlottingPanel) drawingPanel).getAxes().setVisible(true);
        }
        drawingPanel.setShowCoordinates(true);
      }
      boolean isAutoscaleZ = plot.isAutoscaleZ();
      double floor = plot.getFloor();
      double ceil = plot.getCeiling();
      Plot2D oldPlot = plot;
      plot = new GridPlot(gridData);
      if(expanded) {
        plot.setExpandedZ(expanded, expansionFactor);
      }
      plot.setShowGridLines(showGrid);
      plot.setPaletteType(paletteType);
      plot.setAutoscaleZ(isAutoscaleZ, floor, ceil);
      drawingPanel.replaceDrawable(oldPlot, plot);
      plot.update();
      if((tableFrame!=null)&&tableFrame.isShowing()) {
        tableFrame.refreshTable();
      }
      drawingPanel.repaint();
      gridItem.setSelected(true);
    }
  }

  /*
   * Converts to a GrayscalePlot plot.
   */
  public void convertToGrayscalePlot() {
    if(!(plot instanceof GrayscalePlot)) {
      if(surfacePlotMC!=null) {
        drawingPanel.removeMouseListener(surfacePlotMC);
        drawingPanel.removeMouseMotionListener(surfacePlotMC);
        surfacePlotMC = null;
        //drawingPanel.setGutters(gutters);
        drawingPanel.resetGutters();
        drawingPanel.setClipAtGutter(true);
        if(drawingPanel instanceof PlottingPanel) {
          ((PlottingPanel) drawingPanel).getAxes().setVisible(true);
        }
        drawingPanel.setShowCoordinates(true);
      }
      boolean isAutoscaleZ = plot.isAutoscaleZ();
      double floor = plot.getFloor();
      double ceil = plot.getCeiling();
      Plot2D oldPlot = plot;
      plot = new GrayscalePlot(gridData);
      if(expanded) {
        plot.setExpandedZ(expanded, expansionFactor);
      }
      plot.setPaletteType(paletteType);
      plot.setAutoscaleZ(isAutoscaleZ, floor, ceil);
      drawingPanel.replaceDrawable(oldPlot, plot);
      plot.update();
      if((tableFrame!=null)&&tableFrame.isShowing()) {
        tableFrame.refreshTable();
      }
      drawingPanel.repaint();
      grayscaleItem.setSelected(true);
    }
  }

  /**
   * Converts to a SurfacePlot plot.
   */
  public void convertToSurfacePlot() {
    if(!(plot instanceof SurfacePlot)) {
      Plot2D oldPlot = plot;
      try {
        SurfacePlot newPlot = new SurfacePlot(gridData);
        if(drawingPanel instanceof PlottingPanel) {
          String xLabel = ((PlottingPanel) drawingPanel).getAxes().getXLabel();
          String yLabel = ((PlottingPanel) drawingPanel).getAxes().getYLabel();
          newPlot.setAxisLabels(xLabel, yLabel, null);
        }
        plot = newPlot;
      } catch(IllegalArgumentException ex) {
        surfaceItem.setEnabled(false);
        gridItem.setSelected(true);
        convertToGridPlot();
        return;
      }
      //gutters = drawingPanel.getGutters();
      if(drawingPanel instanceof PlottingPanel) {
        ((PlottingPanel) drawingPanel).getAxes().setVisible(false);
      }
      drawingPanel.setShowCoordinates(false);
      drawingPanel.setGutters(0, 0, 0, 0);
      drawingPanel.setClipAtGutter(false);
      boolean isAutoscaleZ = oldPlot.isAutoscaleZ();
      double floor = oldPlot.getFloor();
      double ceil = oldPlot.getCeiling();
      plot.setPaletteType(paletteType);
      if(expanded) {
        plot.setExpandedZ(expanded, expansionFactor);
      }
      plot.setAutoscaleZ(isAutoscaleZ, floor, ceil);
      drawingPanel.replaceDrawable(oldPlot, plot);
      plot.update();
      if((tableFrame!=null)&&tableFrame.isShowing()) {
        tableFrame.refreshTable();
      }
      drawingPanel.repaint();
      if(surfacePlotMC==null) {
        surfacePlotMC = new SurfacePlotMouseController(drawingPanel, plot);
      }
      drawingPanel.addMouseListener(surfacePlotMC);
      drawingPanel.addMouseMotionListener(surfacePlotMC);
      surfaceItem.setSelected(true);
    }
  }

  /**
   * Resizes the number of columns and rows.
   *
   * @param nx int
   * @param ny int
   */
  public void resizeGrid(int nx, int ny) {
    double xmin, xmax, ymin, ymax;
    boolean cellScale = false;
    if(gridData==null) {
      xmin = drawingPanel.getPreferredXMin();
      xmax = drawingPanel.getPreferredXMax();
      ymin = drawingPanel.getPreferredYMin();
      ymax = drawingPanel.getPreferredYMax();
    } else {
      xmin = gridData.getLeft();
      xmax = gridData.getRight();
      ymin = gridData.getBottom();
      ymax = gridData.getTop();
      cellScale = gridData.isCellData();
    }
    gridData = new ArrayData(nx, ny, 1); // a grid with one data component
    gridData.setComponentName(0, "Amp"); //$NON-NLS-1$
    if(nx!=ny) {
      surfaceItem.setEnabled(false);
      if(plot instanceof SurfacePlot) {
        convertToGridPlot();
      }
    } else {
      if(plotType.equals("")) { //$NON-NLS-1$
        surfaceItem.setEnabled(true);
      }
    }
    if(cellScale) {
      gridData.setCellScale(xmin, xmax, ymin, ymax);
    } else {
      gridData.setScale(xmin, xmax, ymin, ymax);
    }
    plot.setGridData(gridData);
    plot.update();
    if((tableFrame!=null)&&tableFrame.isShowing()) {
      tableFrame.refreshTable();
    }
    drawingPanel.invalidateImage();
    drawingPanel.repaint();
  }

  /**
   * Sets the data in the given row to new values.
   *
   * @param row  int
   * @param vals double[] new values
   * @throws IllegalArgumentException if array length does not match grid size.
   */
  public void setRow(int row, double[] vals) throws IllegalArgumentException {
    if(gridData.getNx()!=vals.length) {
      throw new IllegalArgumentException("Row data length does not match grid size."); //$NON-NLS-1$
    }
    double[] rowData = gridData.getData()[0][row];
    System.arraycopy(vals, 0, rowData, 0, vals.length);
    plot.update();
    if((tableFrame!=null)&&tableFrame.isShowing()) {
      tableFrame.refreshTable();
    }
    drawingPanel.invalidateImage();
  }

  /**
   * Sets the scalar field's values and scale..
   *
   * @param vals int[][] the new values
   * @param xmin double
   * @param xmax double
   * @param ymin double
   * @param ymax double
   */
  public void setAll(double[][] vals, double xmin, double xmax, double ymin, double ymax) {
    setAll(vals);
    if(gridData.isCellData()) {
      gridData.setCellScale(xmin, xmax, ymin, ymax);
    } else {
      gridData.setScale(xmin, xmax, ymin, ymax);
    }
  }

  /**
   * Sets the scalar field's values.
   *
   * @param vals double[][] new values
   */
  public void setAll(double[][] vals) {
    if((gridData==null)||(gridData.getNx()!=vals.length)||(gridData.getNy()!=vals[0].length)) {
      resizeGrid(vals.length, vals[0].length);
    }
    double[][] data = gridData.getData()[0];
    // current grid has correct size
    int ny = vals[0].length;
    for(int i = 0, nx = data.length; i<nx; i++) {
      System.arraycopy(vals[i], 0, data[i], 0, ny);
    }
    plot.update();
    if((tableFrame!=null)&&tableFrame.isShowing()) {
      tableFrame.refreshTable();
    }
    drawingPanel.invalidateImage();
  }

  /**
   * Sets all the scalar field values using the given array.
   *
   * The array is assumed to contain numbers in row-major format.
   *
   * @param vals double[] field values
   */
  public void setAll(double[] vals) {
    if(gridData==null) {
      throw new IllegalArgumentException("Grid size must be set before using row-major format."); //$NON-NLS-1$
    }
    int nx = gridData.getNx(), ny = gridData.getNy();
    if(vals.length!=nx*ny) {
      throw new IllegalArgumentException("Grid does not have the correct size."); //$NON-NLS-1$
    }
    double[][] mag = gridData.getData()[0]; // magnitude
    for(int j = 0; j<ny; j++) {
      int offset = j*nx;
      for(int i = 0; i<nx; i++) {
        mag[i][j] = vals[offset+i];
      }
    }
    plot.update();
    if((tableFrame!=null)&&tableFrame.isShowing()) {
      tableFrame.refreshTable();
    }
    drawingPanel.invalidateImage();
  }

  /**
   * Shows or hides the data table.
   *
   * @param show boolean
   */
  public synchronized void showDataTable(boolean show) {
    if(show) {
      if((tableFrame==null)||!tableFrame.isDisplayable()) {
        if(gridData==null) {
          return;
        }
        tableFrame = new GridTableFrame(gridData);
        tableFrame.setTitle(DisplayRes.getString("Scalar2DFrame.Table.Title")); //$NON-NLS-1$
        tableFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
      }
      tableFrame.refreshTable();
      tableFrame.setVisible(true);
    } else {
      tableFrame.setVisible(false);
      tableFrame.dispose();
      tableFrame = null;
    }
  }

}

/*
 * 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
 */
