package plots.views; import java.security.InvalidParameterException; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; public class Plot implements IPlottable { // the data to plot private int[] domain; private int[] values; // the range in the data domain private PlotRange range; // range in the data co-domain private int min; private int max; // modes boolean baseZero = false; public Plot(int[] vals) { setData(vals); } public void setData(int[] indexes, int[] values) { if (indexes == null || values == null || indexes.length != values.length) throw new IllegalArgumentException("indexes and values must be non null and the same size"); this.domain = indexes; this.values = values; // use the complete domain as range setDomainRange(indexes[0], indexes[indexes.length - 1]); min = Integer.MAX_VALUE; max = Integer.MIN_VALUE; for (int i = 0; i < indexes.length; i++) { int v = values[i]; if (v > max) max = v; if (v < min) min = v; } // System.out.printf("Plot[%s].setData(%s, size=%d)\n", this, values, values.length ); } public void setData(int[] values) { int[] indexes = new int[values.length]; for (int i = 0; i < indexes.length; i++) indexes[i] = i; setData(indexes, values); } /* (non-Javadoc) * @see plots.views.IPlottable#plot(org.eclipse.swt.graphics.GC, org.eclipse.swt.graphics.Rectangle) */ @Override public void plot(GC gc, Rectangle dst) { if (values.length == 0) return; int base = baseZero ? 0 : min; int lx = 0; int ly = 0; int li = 0; int origx = dst.x; // preconditions try { check(range.getSpan() > 0); check(values.length > 1); check(domain.length > 0); check(domain[domain.length -1] - domain[0] > 0); } catch (Exception e) { // these exceptions lead to no graph being drawn return; } dst = range.clippedDisplayRect(dst); // adjust origin x // the client has set a domain range that should be mapped to the dst rectangle // if the range is smaller than the domain, then the plot will start with an offset // from dst.x origin dst.x += (range.getRangeStart() - domain[0]) * dst.width / (domain[domain.length - 1] - domain[0]); dst.width = range.getRangeLength() * dst.width / (domain[domain.length - 1] - domain[0]); int startIndex = range.getDomainStartIndex(domain); int endIndex = range.getDomainEndIndex(domain); final double scalex = (double) (endIndex - startIndex) / dst.width; final double scaley = (double) dst.height / (max - base); // only paint what is visible dst = dst.intersection(gc.getClipping()); if (dst.isEmpty()) return; lx = 0; ly = dst.y + dst.height - (int)((values[(int)((lx + (dst.x - origx)) * scalex)] - base) * scaley); for (int x = 1; x < dst.width; x++) { final int i = (int) ((x + (dst.x - origx)) * scalex); int val = (int) ((values[i] - base) * scaley); if (i - li > 1) { int min = val, max = val; // TODO: now average on interval, better to show both min/max? for (int j = li + 1; j < i ; j++) { int v = (int) ((values[j] - base) * scaley); if (min > v) min = v; if (max < v) max = v; } final int miny = dst.y + dst.height - min; final int maxy = dst.y + dst.height - max; gc.drawLine(x + dst.x, miny, x + dst.x, maxy); final int y = dst.y + dst.height - val; gc.drawLine(lx + dst.x, ly, x + dst.x, y); lx = x; ly = y; li = i; } else { final int y = dst.y + dst.height - val; gc.drawLine(lx + dst.x, ly, x + dst.x, y); lx = x; ly = y; li = i; } } } private void check(boolean b) { if (!b) throw new InvalidParameterException("check failed"); } @Override public Plot setDomainRange(int[] range) { if (range == null || range.length != 2) throw new IllegalArgumentException("Range array must have two elements"); setDomainRange(range[0], range[1]); return this; } @Override public Plot setDomainRange(int start, int end) { int dataStart = domain[0]; int dataLength = domain[domain.length - 1] - dataStart; range = new PlotRange(dataStart, dataLength).setRange(start, end); return this; } @Override public int getCount() { return values == null ? 0 : values.length; } }