package gov.nasa.arc.mct.fastplot.scatter; import gov.nasa.arc.mct.fastplot.bridge.AbstractAxis.AxisVisibleOrientation; import gov.nasa.arc.mct.fastplot.bridge.AbstractAxisBoundManager; import gov.nasa.arc.mct.fastplot.bridge.AbstractPlotDataSeries; import gov.nasa.arc.mct.fastplot.bridge.AbstractPlotLine; import gov.nasa.arc.mct.fastplot.bridge.AbstractPlottingPackage; import gov.nasa.arc.mct.fastplot.bridge.LegendEntry; import gov.nasa.arc.mct.fastplot.view.legend.AbstractLegendEntry; import java.util.Collection; import java.util.Collections; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeSet; public class ScatterPlotDataSeries implements AbstractPlotDataSeries { private AbstractPlottingPackage plot; private AbstractPlotLine plotLine; private SortedMap<Long, Double> dependent; private SortedMap<Long, Double> independent; private SortedSet<Long> timestamps = new TreeSet<Long>(); private AbstractLegendEntry legend; public ScatterPlotDataSeries(AbstractPlottingPackage plot, SortedMap<Long, Double> independent, SortedMap<Long, Double> dependent, LegendEntry legend) { super(); this.dependent = dependent; this.independent = independent; this.legend = legend; this.plotLine = plot.createPlotLine(); this.plot = plot; // TODO: Setup plot line, etc! } @Override public AbstractLegendEntry getLegendEntry() { return legend; } public AbstractPlotLine getPlotLine() { return plotLine; } public void updatePlotLine() { if (timestamps.size() > 0) { addData(independent.headMap(timestamps.first()), dependent.headMap(timestamps.first()), false); addData(independent.tailMap(timestamps.last() + 1), dependent.tailMap(timestamps.last() + 1), true); } else { addData(independent, dependent, true); } } private void addData(SortedMap<Long, Double> iMap, SortedMap<Long, Double> dMap, boolean comesAfter) { if (iMap.size() > 0 && dMap.size() > 0) { Long[] ts = getMatchingTimestamps(iMap, dMap); if (ts.length > 0) { double[][] pairs = getPairs(iMap, dMap, ts); if (comesAfter) { plotLine.appendData(pairs[0], pairs[1]); } else { plotLine.prependData(pairs[0], pairs[1]); } informBoundManagers(ts, pairs[0], pairs[1]); } Collections.addAll(timestamps, ts); } } private double[][] getPairs(SortedMap<Long, Double> a, SortedMap<Long, Double> b, Long[] timestamps) { int sz = timestamps.length; int i = 0; double[][] values = { new double[sz], new double[sz] }; for(Long key : timestamps) { values[0][i] = a.get(key); values[1][i] = b.get(key); i++; } return values; } private void informBoundManagers(Long[] timestamps, double[] independent, double[] dependent) { Collection<AbstractAxisBoundManager> indMgr = plot.getBoundManagers(AxisVisibleOrientation.HORIZONTAL); Collection<AbstractAxisBoundManager> depMgr = plot.getBoundManagers(AxisVisibleOrientation.VERTICAL); for (int i = 0; i < timestamps.length; i++) { for (AbstractAxisBoundManager mgr : indMgr) { mgr.informPointPlottedAtTime(timestamps[i], independent[i]); } for (AbstractAxisBoundManager mgr : depMgr) { mgr.informPointPlottedAtTime(timestamps[i], dependent[i]); } } } private Long[] getMatchingTimestamps(SortedMap<Long, Double> a, SortedMap<Long, Double> b) { SortedMap<Long, Double> small = a.size() > b.size() ? b : a; SortedMap<Long, Double> large = small == a ? b : a; int count = 0; for (Long key : small.keySet()) if (large.containsKey(key)) count++; Long[] ts = new Long[count]; int i = 0; for (Long key : small.keySet()) if (large.containsKey(key)) ts[i++] = key; return ts; } public void clearBefore(long timestamp) { if (!timestamps.isEmpty() && timestamps.first() < timestamp) { SortedSet<Long> head = timestamps.headSet(timestamp); plotLine.removeFirst(head.size()); timestamps.retainAll(timestamps.tailSet(timestamp+1)); } } public void clearAfter(long timestamp) { if (!timestamps.isEmpty() && timestamps.last() > timestamp) { SortedSet<Long> tail = timestamps.tailSet(timestamp + 1); plotLine.removeLast(tail.size()); timestamps.retainAll(timestamps.headSet(timestamp + 1)); } } @Override public void setLegendEntry(AbstractLegendEntry entry) { legend = entry; entry.attachPlotLine(plotLine); } }