All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jcamp.math.IrregularGrid1D Maven / Gradle / Ivy

Go to download

The JCAMP-DX project is the reference implemention of the IUPAC JCAMP-DX spectroscopy data standard.

The newest version!
package org.jcamp.math;

/**
 * generic 1-dimensional grid.
 * linear interpolation between grid samples.
 * 
 * @author Thomas Weber
 */
public class IrregularGrid1D
  extends Grid1D {
  
  /** for serialization. */
  private static final long serialVersionUID = -3347468188186084124L;

  private double[] samples;
  
  private boolean ascending;
  
  private Range1D.Double range = new Range1D.Double();
  
  /**
   * default ctor.
   */
  public IrregularGrid1D() {
    this.samples = new double[] {
    };
  }
  
  /**
   * IrregularGrid1D constructor comment.
   * @param length int
   */
  public IrregularGrid1D(double[] samples) {
    this(samples, true);
  }
  
  /**
   * IrregularGrid1D internal copy constructor.
   */
  IrregularGrid1D(double[] samples, Range1D range, boolean ascending) {
    super(samples.length);
    this.samples = (double[]) samples.clone();
    this.range.set(range);
    this.ascending = ascending;
  }
  
  /**
   * IrregularGrid1D constructor comment.
   */
  public IrregularGrid1D(double[] samples, boolean copy) {
    super(samples.length);
    setSamples(samples, copy);
  }
  
  /**
   * IrregularGrid1D constructor comment.
   * @param length int
   */
  public IrregularGrid1D(IOrderedArray1D samples) {
    this(samples.toArray(), false);
  }
  
  /**
   * resampling to wider or narrower grid range (extrapolation)
   * @param length int
   */
  public IrregularGrid1D(IrregularGrid1D from, Range1D range) {
    super();
    double gleft = from.coordinateAt(range.getXMinAsDouble());
    double gright = from.coordinateAt(range.getXMaxAsDouble());
    int length = 0;
    if (from.ascending) {
      double l = Math.floor(gleft);
      double r = Math.ceil(gright);
      length = (int) (r - l) + 1;
      this.samples = new double[length];
      for (int i = 0; i < length; i++) {
	this.samples[i] = from.valueAt((int) l + i);
      }
    } else {
      double l = Math.floor(gright);
      double r = Math.ceil(gleft);
      length = (int) (r - l) + 1;
      this.samples = new double[length];
      for (int i = 0; i < length; i++) {
	this.samples[i] = from.valueAt((int) l + i);
      }
    }
    setLength(length);
    if (length < 2)
      throw new RuntimeException("samples do not form a valid grid");
    this.range.set(samples[0], samples[0]);
    this.ascending = (samples[length - 1] > samples[0]);
    // check for strict monotony
    if (this.ascending) {
      for (int i = 1; i < length; i++) {
	if (this.samples[i] < this.range.getXMin())
	  this.range.set(this.samples[i], this.range.getXMax());
	if (this.samples[i] > this.range.getXMax())
	  this.range.set(this.range.getXMin(), samples[i]);
	if (samples[i] <= samples[i - 1]) {
	  throw new RuntimeException("samples do not form a valid grid");
	}
      }
    } else {
      for (int i = 1; i < length; i++) {
	if (this.samples[i] < this.range.getXMin())
	  this.range.set(this.samples[i], this.range.getXMax());
	if (this.samples[i] > this.range.getXMax())
	  this.range.set(this.range.getXMin(), this.samples[i]);
	if (this.samples[i] >= this.samples[i - 1]) {
	  throw new RuntimeException("samples do not form a valid grid");
	}
      }
    }
  }
  
  /**
   * cloning.
   * @return Object
   */
  @Override
  public Object clone() {
    return new IrregularGrid1D(samples, range, ascending);
  }
  
  /**
   * 
   */
  @Override
  public double coordinateAt(double x) {
    double grid;
    if (Double.isNaN(x))
      grid = Double.NaN;
    else {
      int left = 0;
      int right = getLength() - 1;
      int mid;
      double sleft = samples[left];
      double sright = samples[right];
      if (ascending) {
	if (x < sleft) { // extrapolate
	  grid = left + (x - sleft) / (samples[left + 1] - sleft);
	  return grid;
	} else if (x > sright) { // extrapolate
	  grid = right + (x - sright) / (sright - samples[right - 1]);
	  return grid;
	}
	while (right - left > 1) {
	  mid = (right + left) / 2;
	  double sx = samples[mid];
	  if (x > sx)
	    left = mid;
	  else if (x < sx)
	    right = mid;
	  else {
	    left = mid;
	    break;
	  }
	}
	grid = left + (x - samples[left]) / (samples[left + 1] - samples[left]);
      } else {
	if (x < sright) { // extrapolate
	  grid = right + (sright - x) / (samples[right - 1] - sright);
	  return grid;
	} else if (x > sleft) { // extrapolate
	  grid = left + (sleft - x) / (sleft - samples[left + 1]);
	  return grid;
	}
	while (right - left > 1) {
	  mid = (right + left) / 2;
	  double sx = samples[mid];
	  if (x < sx)
	    left = mid;
	  else if (x > sx)
	    right = mid;
	  else {
	    left = mid;
	    break;
	  }
	}
	grid = left + (samples[left] - x) / (samples[left] - samples[left + 1]);
      }
    }
    return grid;
  }
  
  /**
   * 
   */
  @Override
  public double[] coordinatesAt(double[] value) {
    int n = value.length;
    double[] grid = new double[n];
    for (int i = 0; i < n; i++) {
      double x = value[i];
      if (Double.isNaN(x))
	grid[i] = Double.NaN;
      else {
	int left = 0;
	int right = getLength() - 1;
	int mid;
	double sleft = samples[left];
	double sright = samples[right];
	if (ascending) {
	  if (x < sleft) { // extrapolate
	    grid[i] = left + (x - sleft) / (samples[left + 1] - sleft);
	  continue;
	  } else if (x > sright) { // extrapolate
	    grid[i] = right + (x - sright) / (sright - samples[right - 1]);
	    continue;
	  }
	  while (right - left > 1) {
	    mid = (right + left) / 2;
	    double sx = samples[mid];
	    if (x > sx)
	      left = mid;
	    else if (x < sx)
	      right = mid;
	    else {
	      left = mid;
	      break;
	    }
	  }
	  grid[i] = left + (x - samples[left]) / (samples[left + 1] - samples[left]);
	} else {
	  if (x < sright) { // extrapolate
	    grid[i] = right + (sright - x) / (samples[right - 1] - sright);
	    continue;
	  } else if (x > sleft) { // extrapolate
	    grid[i] = left + (sleft - x) / (sleft - samples[left + 1]);
	    continue;
	  }
	  while (right - left > 1) {
	    mid = (right + left) / 2;
	    double sx = samples[mid];
	    if (x < sx)
	      left = mid;
	    else if (x > sx)
	      right = mid;
	    else {
	      left = mid;
	      break;
	    }
	  }
	  grid[i] = left + (samples[left] - x) / (samples[left] - samples[left + 1]);
	}
      }
    }
    return grid;
  }
  
  /**
   * getArray method comment.
   */
  @Override
  public IArray1D getArray(int index) throws java.lang.ArrayIndexOutOfBoundsException {
    if (index == 0)
      return this;
    else
      throw new ArrayIndexOutOfBoundsException();
  }
  
  /**
   * gets range of grid.
   */
  @Override
  public Range.Double getRange() {
    return (Range.Double) range.clone();
  }
  
  /**
   * gets range of grid.
   */
  @Override
  public Range1D.Double getRange1D() {
    return (Range1D.Double) range.clone();
  }
  
  /**
   * Insert the method's description here.
   * Creation date: (01/06/00 16:51:44)
   * @return double
   */
  public double[] getSamples() {
    return samples;
  }
  
  /**
   * flag if grid has ascending values
   * @return boolean
   */
  @Override
  public boolean isAscending() {
    return ascending;
  }
  
  /**
   * getValue method comment.
   */
  @Override
  public double pointAt(int index) {
    int length = getLength();
    if (index >= 0 && index < length) {
      return samples[index];
    } else if (index < 0) { // extrapolation
      return samples[0] + index * (samples[1] - samples[0]);
    } else { // (index>=length)
      return samples[length - 1] + (index - length + 1) * (samples[length - 1] - samples[length - 2]);
    }
  }
  
  public void scale(double amount) {
    for (int i = 0; i < samples.length; i++)
      samples[i] *= amount;
    range = new Range1D.Double(range.getXMin() * amount, range.getXMax() * amount);
    if (amount < 0)
      ascending = !ascending;
  }
  
  /**
   * set sample array.
   * @param newSamples double[]
   */
  public void setSamples(double[] newSamples) {
    setSamples(newSamples, true);
  }
  
  /**
   * sets the grid array.
   * @param newSamples double[]
   * @param copy boolean
   */
  public void setSamples(double[] newSamples, boolean copy) {
    this.samples = newSamples;
    if (copy) {
      this.samples = (double[]) newSamples.clone();
    } else
      this.samples = newSamples;
    this.range.set(samples[0], samples[0]);
    int length = getLength();
    ascending = (samples[length - 1] > samples[0]);
    // check for strict monotony
    if (ascending) {
      for (int i = 1; i < length; i++) {
	if (samples[i] < range.getXMin())
	  range.set(samples[i], range.getXMax());
	if (samples[i] > range.getXMax())
	  range.set(range.getXMin(), samples[i]);
	if (samples[i] <= samples[i - 1]) {
	  throw new RuntimeException("samples do not form a valid grid");
	}
      }
    } else {
      for (int i = 1; i < length; i++) {
	if (samples[i] < range.getXMin())
	  range.set(samples[i], range.getXMax());
	if (samples[i] > range.getXMax())
	  range.set(range.getXMin(), samples[i]);
	if (samples[i] >= samples[i - 1]) {
	  throw new RuntimeException("samples do not form a valid grid");
	}
      }
    }
  }
  
  /**
   * convert grid to double array.
   * 
   * @return double[]
   */
  public double[] toArray() {
    double[] a = new double[samples.length];
    System.arraycopy(samples, 0, a, 0, samples.length);
    return a;
  }
  
  public void translate(double amount) {
    for (int i = 0; i < samples.length; i++)
      samples[i] += amount;
    range.translate(amount);
  }
  
  /**
   * getValueFromGrid method comment.
   */
  @Override
  public double valueAt(double gx) {
    double value;
    int length = getLength();
    if (gx < 0) {
      double dx = samples[1] - samples[0];
      value = samples[0] + dx * gx;
    } else if (gx > length - 1) {
      double dx = samples[length - 1] - samples[length - 2];
      value = samples[length - 1] + dx * (gx - length + 1);
    } else {
      // find nearest integer lesser than g
      int gx0 = (int) Math.floor(gx + 0.5);
      if (gx0 < 0)
	gx0 = 0;
      if (gx0 > length - 2)
	gx0 = length - 2;
      double dx = gx - gx0;
      value = (1 - dx) * samples[gx0] + dx * samples[gx0 + 1];
    }
    return value;
  }
  
  /**
   * getValueFromGrid method comment.
   */
  @Override
  public double[] valuesAt(double[] grid) {
    int n = grid.length;
    double[] value = new double[n];
    int length = getLength();
    for (int i = 0; i < n; i++) {
      double gx = grid[i];
      if (gx < 0) {
	double dx = samples[1] - samples[0];
	value[i] = samples[0] + dx * gx;
      } else if (gx > length - 1) {
	double dx = samples[length - 1] - samples[length - 2];
	value[i] = samples[length - 1] + dx * (gx - length + 1);
      } else {
	// find nearest integer lesser than g
	int gx0 = (int) Math.floor(gx + 0.5);
	if (gx0 < 0)
	  gx0 = 0;
	if (gx0 > length - 2)
	  gx0 = length - 2;
	double dx = gx - gx0;
	value[i] = (1 - dx) * samples[gx0] + dx * samples[gx0 + 1];
      }
    }
    return value;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy