/* * Copyright (c) 2012 Diamond Light Source Ltd. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package uk.ac.diamond.scisoft.analysis.axis; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.eclipse.january.dataset.Dataset; import org.eclipse.january.dataset.DatasetFactory; import org.eclipse.january.dataset.DatasetUtils; import org.eclipse.january.dataset.DoubleDataset; import org.eclipse.january.dataset.IDataset; import org.eclipse.january.dataset.IndexIterator; /** * Container that holds values for an axis to Plot */ public class AxisValues implements Iterable<Double>, Serializable, Cloneable { private DoubleDataset values = null; private String name; private double minValue; private double maxValue; private boolean isDirty; // flag if min or max needs to be recalculated /** * @return Returns the values. */ public List<Double> getValues() { if (values == null) return new ArrayList<Double>(); ArrayList<Double> arrayList = new ArrayList<Double>(); for (int i = 0; i < values.getShape()[0]; i++) { arrayList.add(values.get(i)); } return arrayList; } /** * @return Returns a 1D dataset of values */ public DoubleDataset toDataset() { return values; } /** * Default constructor */ public AxisValues() { values = null; minValue = Float.MAX_VALUE; maxValue = -Float.MAX_VALUE; isDirty = false; } /** * Calls setValues with the data. * * @param data */ public AxisValues(List<Double> data) { setValues(data); } /** * @param doubleArray */ public AxisValues(double[] doubleArray) { setValues(doubleArray); } /** * @param data */ public AxisValues(Dataset data) { setValues(data); } public AxisValues(String name, IDataset data) { this.name=name; if (data!=null) setValues(data); } /** * Add a new entry into the collection * * @param newValue */ public void addValue(double newValue) { addValues(newValue); } /** * Add new entries into the collection * * @param newValues */ public void addValues(double... newValues) { if (values == null) { values = DatasetFactory.createFromObject(DoubleDataset.class, newValues); } else { int n = values.getSize(); values.resize(n + newValues.length); for (double v : newValues) { values.set(v, n++); } } isDirty = true; } /** * Please only call with the x values in increasing order. * * @param v * The values to set. */ public void setValues(List<Double> v) { values = (DoubleDataset) DatasetFactory.createFromList(v); isDirty = true; } /** * @param v */ public void setValues(double[] v) { values = DatasetFactory.createFromObject(DoubleDataset.class, v); isDirty = true; } /** * Set axis with a dataset after clear out stored values * * @param data */ public void setValues(IDataset data) { values = (DoubleDataset) DatasetUtils.cast(data, Dataset.FLOAT64); isDirty = true; } public void setLowEntry(double value) { boolean overwriteMin = (values.get(0) == minValue); values.set(value, 0); if (overwriteMin) minValue = value; } public void setTopEntry(double value) { int n = values.getSize() - 1; boolean overwriteMax = values.get(n) == maxValue; values.set(value, n); if (overwriteMax) maxValue = value; } /** * Return the value at the given position number in the collection * * @param nr * position number * @return the value at the position number */ public double getValue(int nr) { int n = values.getSize(); if (n > 0) { if (nr < n) return values.get(nr); return values.get(n - 1); } return Double.NaN; } /** * Get the distance between two position numbers in the collection * * @param nr * first position number * @param nr2 * second position number * @return the distance between the two */ public double distBetween(int nr, int nr2) { return values.get(nr2) - values.get(nr); } /** * Clears the collection of all values and resets the min and max value */ public void clear() { values = null; maxValue = -Float.MAX_VALUE; minValue = Float.MAX_VALUE; isDirty = false; } /** * Find the nearest element number in the values list to the request value if the value is larger or smaller than * the largest / smallest entry in the list it will return -1. It will always return the nearest upper boundary * entry * * @param value * search value * @return -1 if none could be found otherwise the upper boundary element number */ public int nearestUpEntry(double value) { if (isDirty) sanityCheckMinMax(); if (value > maxValue || value < minValue) return -1; int counter = isAscending() ? DatasetUtils.findIndexGreaterThanOrEqualTo(values, value) : DatasetUtils.findIndexLessThan(values, value); if (counter >= values.getSize()) return -1; return counter; } /** * Find the nearest element number in the values list to the request value if the value is larger or smaller than * the largest / smallest entry in the list it will return -1. It will always return the nearest lower boundary * entry * * @param value * search value * @return -1 if none could be found otherwise the lower boundary element number */ public int nearestLowEntry(double value) { if (isDirty) sanityCheckMinMax(); if (value > maxValue || value < minValue) return -1; if (isAscending()) { int counter = DatasetUtils.findIndexGreaterThanOrEqualTo(values, value); if (counter >= values.getSize()) { return -1; } if (values.getAbs(counter) > value) { counter--; } return counter; } int counter = DatasetUtils.findIndexLessThan(values, value); if (counter >= values.getSize()) { return -1; } return counter - 1; } /** * Get a subset from the AxisValues * * @param start * beginning * @param stop * end (exclusive) * @return the subset of the AxisValues */ public AxisValues subset(int start, int stop) { return subset(start, stop, 1); } /** * Get a subset from the AxisValues * * @param start * beginning * @param stop * end (exclusive) * @param step * @return the subset of the AxisValues */ public AxisValues subset(int start, int stop, int step) { return new AxisValues(values.getSlice(new int[] { start}, new int[] { stop }, new int[] { step })); } /** * Determine number of elements * * @return number of elements */ public int size() { if (values == null) { return 0; } return values.getSize(); } @Override public AxisValues clone() { AxisValues cloned = new AxisValues(values); cloned.maxValue = this.maxValue; cloned.minValue = this.minValue; cloned.isDirty = this.isDirty; return cloned; } /** * @return Returns the minValue. */ public double getMinValue() { sanityCheckMinMax(); return minValue; } /** * @param minValue * The minValue to set. */ public void setMinValue(double minValue) { this.minValue = minValue; isDirty = false; } /** * @return Returns the maxValue. */ public double getMaxValue() { sanityCheckMinMax(); return maxValue; } /** * @param maxValue * The maxValue to set. */ public void setMaxValue(double maxValue) { this.maxValue = maxValue; isDirty = false; } private void sanityCheckMinMax() { if (isDirty) { minValue = values.min().doubleValue(); maxValue = values.max().doubleValue(); isDirty = false; } if (minValue > maxValue) { double temp = minValue; minValue = maxValue; maxValue = temp; } } /** * Check if the AxisValues are ascending or descending * * @return true if ascending otherwise false */ public boolean isAscending() { if (values.getShape()[0] > 0) return values.get(0) < values.get(values.getShape()[0] - 1); return true; } @Override public String toString() { return values != null ? values.toString() : "Empty"; } private static Iterator<Double> NULL_ITERATOR = new Iterator<Double>() { @Override public boolean hasNext() { return false; } @Override public Double next() { throw new NoSuchElementException("No elements left as no values defined"); } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported"); } }; /** * Returns an iterator but note that remove is not supported * @return an iterator */ @Override public Iterator<Double> iterator() { Iterator<Double> iterator = values == null ? NULL_ITERATOR : new Iterator<Double>() { IndexIterator it = values.getIterator(); boolean read = true; @Override public void remove() { throw new UnsupportedOperationException("Remove not supported"); } @Override public Double next() { if (read) { if (it.hasNext()) { read = false; return next(); } throw new NoSuchElementException("No elements left"); } read = true; return values.getAbs(it.index); } @Override public boolean hasNext() { if (read) { read = false; return it.hasNext(); } return true; } }; return iterator; } public String getName() { if (name!=null) return name; if (values!=null) return values.getName(); return null; } public void setName(String name) { this.name = name; } public boolean isData() { return values!=null; } }