GnuplotSink.java

/*
 * Created on 2007/02/05
 * Copyright (C) 2007 Koga Laboratory. All rights reserved.
 *
 */
package org.mklab.tool.control.system.sink;

import java.io.IOException;

import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.tool.control.system.SystemOperatorException;
import org.mklab.tool.control.system.parameter.Parameter;
import org.mklab.tool.control.system.parameter.ParameterAccessException;
import org.mklab.tool.control.system.parameter.ParameterContainer;
import org.mklab.tool.graph.gnuplot.Canvas;
import org.mklab.tool.graph.gnuplot.Gnuplot;


/**
 * Gnuplotへの出力器を表わすクラスです。
 * 
 * @author koga
 * @version $Revision: 1.35 $, 2007/02/05
 */
public class GnuplotSink extends DoubleMatrixExportSink {

  /** Gnuplot */
  private Gnuplot gnuplot;

  /** キャンバス */
  private Canvas canvas;

  /** タイトル */
  @Parameter(name = "title", description = "GnuplotSink.1", update = true, internationalization = true)
  private String title = ""; //$NON-NLS-1$

  /** x軸ラベル */
  @Parameter(name = "xlabel", description = "GnuplotSink.4", update = true, internationalization = true)
  private String xLabel = ""; //$NON-NLS-1$

  /** y軸ラベル */
  @Parameter(name = "ylabel", description = "GnuplotSink.7", update = true, internationalization = true)
  private String yLabel = ""; //$NON-NLS-1$

  /** x軸の表示最小値 */
  @Parameter(name = "xMinimum", description = "GnuplotSink.10", update = true, internationalization = true)
  private double xMinimum;

  /** x軸の表示最大値 */
  @Parameter(name = "xMaximum", description = "GnuplotSink.12", update = true, internationalization = true)
  private double xMaximum;

  /** y軸の表示最小値 */
  @Parameter(name = "yMinimum", description = "GnuplotSink.14", update = true, internationalization = true)
  private double yMinimum;

  /** y軸の表示最大値 */
  @Parameter(name = "yMaximum", description = "GnuplotSink.16", update = true, internationalization = true)
  private double yMaximum;

  /** x軸のグリッド間隔 */
  @Parameter(name = "xGridInterval", description = "GnuplotSink.18", update = true, internationalization = true)
  private double xGridInterval;

  /** y軸のグリッド間隔 */
  @Parameter(name = "yGridInterval", description = "GnuplotSink.20", update = true, internationalization = true)
  private double yGridInterval;

  /** グリッドを表示するならばtrue */
  @Parameter(name = "grid", description = "GnuplotSink.22", update = true, internationalization = true)
  private boolean grid = true;

  /** 表示領域やグリッド間隔を自動設定にするならばtrue */
  @Parameter(name = "autoScale", description = "GnuplotSink.0", update = true, internationalization = true)
  private boolean autoScale = true;

  /** 線の名前 */
  @Parameter(name = "lineNames", description = "GnuplotSink.24", update = true, internationalization = true)
  private String[] lineNames;

  /** 線の幅 */
  @Parameter(name = "lineWidth", description = "GnuplotSink.25", update = true, internationalization = true)
  private int lineWidth = 1;

  /** フォントの大きさ */
  @Parameter(name = "fontSize", description = "GnuplotSink.26", update = true, internationalization = true)
  private int fontSize = 12;

  /** 新しい設定条件で再描画するならばtrue */
  private boolean justReploting = false;

  /**
   * 新しく生成された<code>GnuplotSink</code>オブジェクトを初期化します。
   */
  public GnuplotSink() {
    super();
  }

  /**
   * @see org.mklab.tool.control.system.sink.DoubleMatrixExportSink#open()
   */
  @Override
  public void open() {
    super.open();

    if (this.gnuplot != null && this.gnuplot.isRunning()) {
      this.gnuplot.close();
    }

    try {
      this.gnuplot = new Gnuplot();
    } catch (IOException e) {
      throw new SystemOperatorException(e);
    }

    this.canvas = this.gnuplot.createCanvas();
    this.canvas.setGridVisible(this.grid);
    this.canvas.setTitle(this.title);
    this.canvas.setXLabel(this.xLabel);
    this.canvas.setYLabel(this.yLabel);
    this.canvas.setFontSize(this.fontSize);
  }

  /**
   * @see org.mklab.tool.control.system.sink.Exporter#close()
   */
  public void close() {
    if (this.gnuplot != null && this.gnuplot.isRunning()) {
      this.gnuplot.close();
    }
    this.gnuplot = null;
    this.canvas = null;
  }

  /**
   * @see org.mklab.tool.control.system.sink.Exporter#isActive()
   */
  public boolean isActive() {
    if (this.gnuplot == null) {
      return false;
    }
    return this.gnuplot.isRunning();
  }

  /**
   * @see org.mklab.tool.control.system.sink.ContinuousSink#setInputSize(int)
   */
  @Override
  public void setInputSize(final int inputSize) {
    super.setInputSize(inputSize);

    if (inputSize == -1) {
      return;
    }

    if (isNameInitializing(inputSize)) {
      setupLineNames(inputSize, "y"); //$NON-NLS-1$

      try {
        setupParameters(this.getClass());
      } catch (ParameterAccessException e) {
        throw new RuntimeException(e);
      }
    }
  }

  /**
   * @see org.mklab.tool.control.system.sink.Exporter#exportData()
   */
  public void exportData() {
    if (isReady() == false) {
      return;
    }

    final DoubleMatrix data = getData();
    final DoubleMatrix x = data.getRowVector(1);
    final DoubleMatrix y = data.getRowVectors(2, data.getRowSize());

    if (isJustReploting() == false && isNameInitializing(y.getRowSize())) {
      setupLineNames(y.getRowSize(), "y"); //$NON-NLS-1$
    }

    exportData(x, y);
  }

  /**
   * 名前を初期化するべきか判定します。
   * 
   * @param lineSize 線の数
   * @return 名前を初期化するべきならばtrue、そうでなければfalse
   */
  boolean isNameInitializing(final int lineSize) {
    return this.lineNames == null || this.lineNames.length != lineSize;
  }

  /**
   * 出力の準備ができているか判定します。
   * 
   * @return 出力の準備ができていればtrue、そうでなければfalse
   */
  boolean isReady() {
    if (getDataLength() == 0) {
      this.justReploting = false;
      return false;
    }

    if (this.canvas == null) {
      return false;
    }

    return true;
  }

  /**
   * データを出力します。
   * 
   * @param x x軸方向のデータ
   * @param y y軸方向のデータ
   */
  void exportData(final DoubleMatrix x, final DoubleMatrix y) {
    this.canvas.setBuffering(true);
    
    this.canvas.plot(x, y, this.lineNames);
    this.canvas.setLineWidth(this.lineWidth);
    
    DoubleMatrix finiteY = new DoubleMatrix(y.getRowSize(), y.getColumnSize());
    for (int i = 1; i <= y.getColumnSize(); i++) {
      for (int j = 1; j <= y.getRowSize(); j++) {
        if (y.getElement(j, i).isFinite()) {
          finiteY.setElement(j, i, y.getDoubleElement(j, i));
        }
      }
    }

    if (this.justReploting == false) {
      if (this.autoScale) {
        setXMinimum(x.min().doubleValue());
        setXMaximum(x.max().doubleValue());
        setXGridInterval(getGridIntervalCandidate(this.xMinimum, this.xMaximum));
      } else {
        setXMinimum(getXMinimum());
        setXMaximum(getXMaximum());
        setXGridInterval(getXGridInterval());
      }

      if (this.autoScale) {
        setYMinimum(getMinimumCandidate(finiteY));
        setYMaximum(getMaximumCandidate(finiteY));
        setYGridInterval(getGridIntervalCandidate(this.yMinimum, this.yMaximum));
      } else {
        setYMinimum(getYMinimum());
        setYMaximum(getYMaximum());
        setYGridInterval(getYGridInterval());
      }
    }
    
    this.canvas.setBuffering(false);
    this.canvas.redraw();

    this.justReploting = false;
  }

  /**
   * 線の名前を決定します。
   * 
   * @param lineSize 線の数
   * @param lineName 線の名前
   */
  void setupLineNames(final int lineSize, final String lineName) {
    final String[] oldLineNames = this.lineNames;
    this.lineNames = new String[lineSize];

    int oldLineSize = 0;
    if (oldLineNames != null) {
      oldLineSize = oldLineNames.length;
      System.arraycopy(oldLineNames, 0, this.lineNames, 0, Math.min(lineSize, oldLineSize));
    }

    if (lineSize == 1) {
      this.lineNames[0] = lineName;
    } else {
      for (int i = oldLineSize; i < lineSize; i++) {
        this.lineNames[i] = lineName + (i + 1);
      }
    }

    if (this.canvas != null) {
      this.canvas.setKeyVisible(true);
    }
  }
  
  /**
   * 自動調節するか設定します。
   * 
   * @param autoScale 自動調節するならばtrue
   */
  public void setAutoScale(final boolean autoScale) {
    this.autoScale = autoScale;
  }

  /**
   * 自動調節するかを返します。
   * 
   * @return 自動調節するか
   */
  public boolean isAutoScale() {
    return this.autoScale;
  }

  /**
   * グリッド間隔の候補を返します。
   * 
   * @param minimum 最小値
   * @param maximum 最大値
   * @return グリッド間隔の候補
   */
  private double getGridIntervalCandidate(final double minimum, final double maximum) {
    final double maximumScale;
    if (maximum == 0) {
      maximumScale = 0;
    } else {
      maximumScale = Math.pow(10, Math.floor(Math.log10(Math.abs(maximum) / 2)));
    }

    final double minimumScale;
    if (minimum == 0) {
      minimumScale = 0;
    } else {
      minimumScale = Math.pow(10, Math.floor(Math.log10(Math.abs(minimum) / 2)));
    }

    final double scaleCandidate = Math.max(minimumScale, maximumScale);
    final double scale = (0 == scaleCandidate) ? 1 : scaleCandidate;

    final int gridNumber;
    if (Math.signum(minimum) == Math.signum(maximum)) {
      gridNumber = (int)(Math.ceil(Math.abs(maximum - minimum) / scale));
    } else {
      gridNumber = (int)(Math.ceil(Math.abs(minimum) / scale) + Math.ceil(Math.abs(maximum) / scale));
    }

    if (20 <= gridNumber) {
      return scale * 4;
    }

    if (10 <= gridNumber) {
      if (gridNumber % 3 == 0) {
        return scale * 3;
      }

      return scale * 2;
    }

    if (gridNumber < 2) {
      final int expectedGridNumber5 = (int)Math.rint((maximum - minimum) / (scale / 5));
      if (expectedGridNumber5 % 5 == 0) {
        return scale / 5;
      }

      final int expectedGridNumber4 = (int)Math.rint((maximum - minimum) / (scale / 4));
      if (expectedGridNumber4 >= 4) {
        return scale / 4;
      }

      return scale / 8;
    }

    if (gridNumber < 4) {
      return scale / 2;
    }

    return scale;
  }

  /**
   * 表示最大値の第一候補を返します。
   * 
   * @param data データ
   * @return 表示最大値の第一候補
   */
  private double getMaximumFirstCandidate(final DoubleMatrix data) {
    final double minimum = data.min().doubleValue();
    final double maximum = data.max().doubleValue();

    final double scale = getScaleForCandidate(minimum, maximum);

    double scaledMagnitude;
    if (maximum < 0) {
      scaledMagnitude = Math.floor(Math.abs(maximum) / scale);
    } else {
      scaledMagnitude = Math.ceil(Math.abs(maximum) / scale);
    }

    if (minimum == maximum) {
      scaledMagnitude = scaledMagnitude + 1;
    }

    return Math.signum(maximum) * scaledMagnitude * scale;
  }

  /**
   * 表示最小値の第一候補を返します。
   * 
   * @param data データ
   * @return 表示最小値の第一候補
   */
  private double getMinimumFirstCandidate(final DoubleMatrix data) {
    final double minimum = data.min().doubleValue();
    final double maximum = data.max().doubleValue();

    final double scale = getScaleForCandidate(minimum, maximum);

    double scaledMagnitude;
    if (minimum < 0) {
      scaledMagnitude = Math.ceil(Math.abs(minimum) / scale);
    } else {
      scaledMagnitude = Math.floor(Math.abs(minimum) / scale);
    }

    if (minimum == maximum) {
      scaledMagnitude = scaledMagnitude - 1;
    }

    return Math.signum(minimum) * scaledMagnitude * scale;
  }

  /**
   * 表示最大値の候補を返します。
   * 
   * @param data データ
   * @return 表示最大値の候補
   */
  private double getMaximumCandidate(final DoubleMatrix data) {
    final double maximumCandidate = getMaximumFirstCandidate(data);
    final double minimumCandidate = getMinimumFirstCandidate(data);
    final double gridCandidate = getGridIntervalCandidate(minimumCandidate, maximumCandidate);

    return Math.signum(maximumCandidate) * Math.ceil(Math.abs(maximumCandidate / gridCandidate)) * gridCandidate;
  }

  /**
   * 表示最小値の候補を返します。
   * 
   * @param data データ
   * @return 表示最小値の候補
   */
  private double getMinimumCandidate(final DoubleMatrix data) {
    final double maximumCandidate = getMaximumFirstCandidate(data);
    final double minimumCandidate = getMinimumFirstCandidate(data);
    final double gridCandidate = getGridIntervalCandidate(minimumCandidate, maximumCandidate);

    return Math.signum(minimumCandidate) * Math.ceil(Math.abs(minimumCandidate / gridCandidate)) * gridCandidate;
  }

  /**
   * 表示最大・最小値の候補を決定するためのスケーリングファクタを返します。
   * 
   * @param minimum 最小値
   * @param maximum 最大値
   * @return 表示最大・最小値の候補を決定するためのスケーリングファクタ
   */
  private double getScaleForCandidate(final double minimum, final double maximum) {
    final double minimumMagnitude = Math.abs(minimum);
    final double maximumMagnitude = Math.abs(maximum);

    final double maximumScale;
    if (maximumMagnitude == 0) {
      maximumScale = 0.1;
    } else {
      maximumScale = Math.pow(10, Math.floor(Math.log10(maximumMagnitude))) / 10;
    }

    final double minimumScale;
    if (minimumMagnitude == 0) {
      minimumScale = 0.1;
    } else {
      minimumScale = Math.pow(10, Math.floor(Math.log10(minimumMagnitude))) / 10;
    }

    final double scale = minimumScale < maximumScale ? maximumScale : minimumScale;
    return scale;
  }

  /**
   * タイトルを設定します。
   * 
   * @param title タイトル
   */
  public void setTitle(final String title) {
    this.title = title;
    if (this.canvas != null) {
      this.canvas.setTitle(this.title);
    }
  }

  /**
   * 線の幅を設定します。
   * 
   * @param lineWidth 線の幅
   */
  public void setLineWidth(final int lineWidth) {
    this.lineWidth = lineWidth;
    if (this.canvas != null) {
      this.canvas.setLineWidth(lineWidth);
    }
  }

  /**
   * フォントの大きさを設定します。
   * 
   * @param fontSize フォントの大きさ
   */
  public void setFontSize(final int fontSize) {
    this.fontSize = fontSize;
    if (this.canvas != null) {
      this.canvas.setFontSize(fontSize);
    }
  }

  /**
   * フォントの大きさを返します。
   * 
   * @return フォントの大きさ
   */
  public int getFontSize() {
    return this.fontSize;
  }

  /**
   * x軸ラベルを設定します。
   * 
   * @param label ラベル
   */
  public void setXLabel(final String label) {
    this.xLabel = label;
    if (this.canvas != null) {
      this.canvas.setXLabel(label);
    }
  }

  /**
   * y軸ラベルを設定します。
   * 
   * @param label ラベル
   */
  public void setYLabel(final String label) {
    this.yLabel = label;
    if (this.canvas != null) {
      this.canvas.setYLabel(label);
    }
  }

  /**
   * 線の名前を設定します。
   * 
   * @param index 線の番号
   * @param name 線の名前
   */
  public void setLineNames(final int index, final String name) {
    this.lineNames[index] = name;
    //this.justReploting = true;
    exportData();
  }

  /**
   * x軸の表示最小値を設定します。
   * 
   * @param minimum x軸の表示最小値
   */
  public void setXMinimum(final double minimum) {
    this.xMinimum = minimum;
    if (this.canvas != null) {
      this.canvas.setXRange(this.xMinimum, this.xMaximum);
      if (this.autoScale) {
        setXGridInterval(getGridIntervalCandidate(this.xMinimum, this.xMaximum));
      }
    }
  }

  /**
   * x軸の表示最大値を設定します。
   * 
   * @param maximum x軸の表示最大値
   */
  public void setXMaximum(final double maximum) {
    this.xMaximum = maximum;
    if (this.canvas != null) {
      this.canvas.setXRange(this.xMinimum, this.xMaximum);
      if (this.autoScale) {
        setXGridInterval(getGridIntervalCandidate(this.xMinimum, this.xMaximum));
      }
    }
  }

  /**
   * x軸の表示最大値を返します。
   * 
   * @return x軸の表示最大値
   */
  public double getXMaximum() {
    return this.xMaximum;
  }

  /**
   * x軸の表示最小値を返します。
   * 
   * @return x軸の表示最小値
   */
  public double getXMinimum() {
    return this.xMinimum;
  }

  /**
   * y軸の表示最小値を設定します。
   * 
   * @param minimum y軸の表示最小値
   */
  public void setYMinimum(final double minimum) {
    this.yMinimum = minimum;
    if (this.canvas != null) {
      this.canvas.setYRange(this.yMinimum, this.yMaximum);
      if (this.autoScale) {
        setYGridInterval(getGridIntervalCandidate(this.yMinimum, this.yMaximum));
      }
    }
  }

  /**
   * y軸の表示最大値を設定します。
   * 
   * @param maximum y軸の表示最大値
   */
  public void setYMaximum(final double maximum) {
    this.yMaximum = maximum;
    if (this.canvas != null) {
      this.canvas.setYRange(this.yMinimum, this.yMaximum);
      if (this.autoScale) {
        setYGridInterval(getGridIntervalCandidate(this.yMinimum, this.yMaximum));
      }
    }
  }

  /**
   * y軸の表示最小値を返します。
   * 
   * @return y軸の表示最小値
   */
  public double getYMinimum() {
    return this.yMinimum;
  }

  /**
   * y軸の表示最大値を返します。
   * 
   * @return y軸の表示最大値
   */
  public double getYMaximum() {
    return this.yMaximum;
  }

  /**
   * x軸のグリッド間隔を設定します。
   * 
   * @param interval x軸のグリッド間隔
   */
  public void setXGridInterval(final double interval) {
    this.xGridInterval = interval;
    if (this.canvas != null) {
      this.canvas.setXTics(this.xMinimum, this.xGridInterval, this.xMaximum);
    }
  }

  /**
   * x軸のグリッド間隔を返します。
   * 
   * @return x軸のグリッド間隔
   */
  public double getXGridInterval() {
    return this.xGridInterval;
  }

  /**
   * y軸のグリッド間隔を設定します。
   * 
   * @param interval y軸のグリッド間隔
   */
  public void setYGridInterval(final double interval) {
    this.yGridInterval = interval;
    if (this.canvas != null) {
      this.canvas.setYTics(this.yMinimum, this.yGridInterval, this.yMaximum);
    }
  }

  /**
   * y軸のグリッド間隔を返します。
   * 
   * @return y軸のグリッド間隔
   */
  public double getYGridInterval() {
    return this.yGridInterval;
  }

  /**
   * グリッドを表示するか設定します。
   * 
   * @param grid グリッドを表示するならばtrue
   */
  public void setGrid(final boolean grid) {
    this.grid = grid;
    if (this.canvas != null) {
      this.canvas.setGridVisible(this.grid);
    }
  }

  /**
   * Gnuplotを返します。
   * 
   * @return Gnuplot
   */
  public Gnuplot getGnuplot() {
    return this.gnuplot;
  }

  /**
   * @see org.mklab.tool.control.system.parameter.ParameterUpdator#updateWith(java.lang.String)
   */
  @Override
  public boolean updateWith(String parameter) {
    if (super.updateWith(parameter)) {
      return true;
    }
    
    if (this.gnuplot != null && this.gnuplot.isRunning() == false) {
      open();
      this.gnuplot.reset();
    }

    if (parameter.equals("title")) { //$NON-NLS-1$
      setTitle(this.title);
      return true;
    } else if (parameter.equals("xlabel")) { //$NON-NLS-1$
      setXLabel(this.xLabel);
      return true;
    } else if (parameter.equals("ylabel")) { //$NON-NLS-1$
      setYLabel(this.yLabel);
      return true;
    } else if (parameter.equals("xMinimum")) { //$NON-NLS-1$
      setXMinimum(this.xMinimum);
      return true;
    } else if (parameter.equals("xMaximum")) { //$NON-NLS-1$
      setXMaximum(this.xMaximum);
      return true;
    } else if (parameter.equals("yMinimum")) { //$NON-NLS-1$
      setYMinimum(this.yMinimum);
      return true;
    } else if (parameter.equals("yMaximum")) { //$NON-NLS-1$
      setYMaximum(this.yMaximum);
      return true;
    } else if (parameter.equals("xGridInterval")) { //$NON-NLS-1$
      setXGridInterval(this.xGridInterval);
      return true;
    } else if (parameter.equals("yGridInterval")) { //$NON-NLS-1$
      setYGridInterval(this.yGridInterval);
      return true;
    } else if (parameter.equals("grid")) { //$NON-NLS-1$
      setGrid(this.grid);
      return true;
    } else if (parameter.equals("fontSize")) { //$NON-NLS-1$
      setFontSize(this.fontSize);
      return true;
    } else if (parameter.equals("lineWidth")) { //$NON-NLS-1$
      setLineWidth(this.lineWidth);
      return true;
    } else if (parameter.startsWith("lineNames[")) { //$NON-NLS-1$
      final int index = ParameterContainer.getIndexOfArray(parameter);
      setLineNames(index, this.lineNames[index]);
      return true;
    }

    return false;
  }

  /**
   * @see org.mklab.tool.control.system.SystemOperator#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!super.equals(o)) {
      return false;
    }
    if (o == null) {
      return false;
    }
    if (o.getClass() != getClass()) {
      return false;
    }
    GnuplotSink castedObj = (GnuplotSink)o;
    return ((this.gnuplot == null ? castedObj.gnuplot == null : this.gnuplot.equals(castedObj.gnuplot)) && (this.canvas == null ? castedObj.canvas == null : this.canvas.equals(castedObj.canvas))
        && (this.title == null ? castedObj.title == null : this.title.equals(castedObj.title)) && (this.xLabel == null ? castedObj.xLabel == null : this.xLabel.equals(castedObj.xLabel))
        && (this.yLabel == null ? castedObj.yLabel == null : this.yLabel.equals(castedObj.yLabel)) && (this.xMinimum == castedObj.xMinimum) && (this.xMaximum == castedObj.xMaximum)
        && (this.yMinimum == castedObj.yMinimum) && (this.yMaximum == castedObj.yMaximum) && (this.xGridInterval == castedObj.xGridInterval) && (this.yGridInterval == castedObj.yGridInterval)
        && (this.grid == castedObj.grid) && java.util.Arrays.equals(this.lineNames, castedObj.lineNames) && (this.justReploting == castedObj.justReploting));
  }

  /**
   * @see org.mklab.tool.control.system.SystemOperator#hashCode()
   */
  @Override
  public int hashCode() {
    int hashCode = super.hashCode();
    hashCode = 31 * hashCode + (this.gnuplot == null ? 0 : this.gnuplot.hashCode());
    hashCode = 31 * hashCode + (this.canvas == null ? 0 : this.canvas.hashCode());
    hashCode = 31 * hashCode + (this.title == null ? 0 : this.title.hashCode());
    hashCode = 31 * hashCode + (this.xLabel == null ? 0 : this.xLabel.hashCode());
    hashCode = 31 * hashCode + (this.yLabel == null ? 0 : this.yLabel.hashCode());
    hashCode = 31 * hashCode + (int)(Double.doubleToLongBits(this.xMinimum) ^ (Double.doubleToLongBits(this.xMinimum) >>> 32));
    hashCode = 31 * hashCode + (int)(Double.doubleToLongBits(this.xMaximum) ^ (Double.doubleToLongBits(this.xMaximum) >>> 32));
    hashCode = 31 * hashCode + (int)(Double.doubleToLongBits(this.yMinimum) ^ (Double.doubleToLongBits(this.yMinimum) >>> 32));
    hashCode = 31 * hashCode + (int)(Double.doubleToLongBits(this.yMaximum) ^ (Double.doubleToLongBits(this.yMaximum) >>> 32));
    hashCode = 31 * hashCode + (int)(Double.doubleToLongBits(this.xGridInterval) ^ (Double.doubleToLongBits(this.xGridInterval) >>> 32));
    hashCode = 31 * hashCode + (int)(Double.doubleToLongBits(this.yGridInterval) ^ (Double.doubleToLongBits(this.yGridInterval) >>> 32));
    hashCode = 31 * hashCode + (this.grid ? 1231 : 1237);
    for (int i0 = 0; this.lineNames != null && i0 < this.lineNames.length; i0++) {
      hashCode = 31 * hashCode + (this.lineNames == null ? 0 : this.lineNames[i0].hashCode());
    }
    hashCode = 31 * hashCode + (this.justReploting ? 1231 : 1237);
    return hashCode;
  }

  /**
   * 新しい設定条件で再描画するか判定します。
   * 
   * @return 新しい設定条件で再描画するならばtrue
   */
  boolean isJustReploting() {
    return this.justReploting;
  }
}