Package multichannel

Source Code of multichannel.Oscilloscope$Data

package multichannel;

import java.util.ArrayList;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;

/**
* Animated widget that tries to mimic an Oscilloscope.
*
* <i>An oscilloscope (also known as a scope, CRO or, an O-scope) is a type of electronic test instrument that allows observation of constantly
* varying signal voltages, usually as a two-dimensional graph of one or more electrical potential differences using the vertical or 'Y' axis, plotted
* as a function of time, (horizontal or 'x' axis).</i>
* <p/>
* <a href="http://en.wikipedia.org/wiki/Oscilloscope">http://en.wikipedia.org/ wiki/Oscilloscope<a/>
*
* @author Wim.Jongman (@remainsoftware.com)
*
*/
public class Oscilloscope extends Canvas {

    /**
     * The default comfortable widget width.
     */
    public static final int DEFAULT_WIDTH = 180;

    /**
     * The default comfortable widget height.
     */
    public static final int DEFAULT_HEIGHT = 100;

    /**
     * The default amount of tail fading in percentages (25).
     */
    public static final int TAILFADE_DEFAULT = 25;

    /**
     * No tailfade.
     */
    public static final int TAILFADE_NONE = 0;

    private Color bg;
    private Color mGridColor;

    public class Data {

  Color fg;
  private int cursor = 50;
  int width = DEFAULT_WIDTH;
  int height = DEFAULT_HEIGHT;
  private int base;
  IntegerFiFoCircularStack stack;
  int tailSize;
  private int lineWidth = 1;
  private boolean percentage = false;
  OscilloscopeDispatcher dispatcher;

  /**
   * This contains the old or historical input and is used to paint the tail of the graph.
   */
  private int[] tail;

  /**
   * This contains the actual values that where input by the user before scaling. If the user resized we can calculate how the tail would have
   * looked with the new window dimensions.
   *
   * @see Oscilloscope#tail
   */
  // private int[] originalTailInput;
  private int originalTailSize;
  boolean steady;
  private int tailFade = 25;
  private boolean fade;
  private boolean connect;
  int originalSteadyPosition = STEADYPOSITION_75PERCENT;
  int baseOffset = BASE_CENTER;
  private ArrayList<OscilloscopeStackAdapter> stackListeners;
  private int progression = 0;
  String name = "";

    }

    // Blocks painting if true
    boolean paintBlock;

    private Data[] chan;

    private int myLowRangeValue = 0;

    private int myhighValue = 0;

    private boolean ShowLabels = false;
    private String myStatus = "";

    public String getStatus() {
  return myStatus;
    }

    public void setStatus(String status) {
  myStatus = status;
    }

    public boolean isShowLabels() {
  return ShowLabels;
    }

    public void setShowLabels(boolean showLabels) {
  ShowLabels = showLabels;
    }

    public void SetChannelName(int channel, String name) {
  chan[channel].name = name;
    }

    /**
     * This set of values will draw a figure that is similar to the heart beat that you see on hospital monitors.
     */
    public static final int[] HEARTBEAT = new int[] { 2, 10, 2, -16, 16, 44, 49, 44, 32, 14, -16, -38, -49, -47, -32, -10, 8, 6, 6, -2, 6, 4, 2, 0,
      0, 6, 8, 6 };
    /**
     * Will draw a maximum tail.
     */
    public static final int TAILSIZE_MAX = -1;

    /**
     * Will draw a tail from the left border but is only valid if the boolean in {@link #setSteady(boolean, int)} was set to true, will default to
     * {@link #TAILSIZE_MAX} otherwise.
     */
    public static final int TAILSIZE_FILL = -2;

    /**
     * The default tail size is 75% of the width.
     */
    public static final int TAILSIZE_DEFAULT = -3;

    /**
     * Steady position @ 75% of graph.
     */
    public static final int STEADYPOSITION_75PERCENT = -1;

    /**
     * The base of the line is positioned at the center of the widget.
     *
     * @see #setBaseOffset(int)
     */
    public static final int BASE_CENTER = 50;

    /**
     * The stack will not overflow if you push too many values into it but it will rotate and overwrite the older values. Think of the stack as a
     * closed ring with one hole to push values in and one that lets them out.
     *
     */
    public class IntegerFiFoCircularStack {
  final private int[] stack;
  private int top;
  private int bottom;
  private final int capacity;
  private int storedValues;

  /**
   * Creates a stack with the indicated capacity.
   *
   * @param capacity
   */
  public IntegerFiFoCircularStack(int capacity) {
      if (capacity <= 1)
    throw new RuntimeException("Stack capacity must be > 1");
      this.capacity = capacity;
      stack = new int[capacity];
      top = 0;
      bottom = 0;
  }

  /**
   * Creates stack with the indicated capacity and copies the old stack into the new stack and the old stack will be empty after this action.
   *
   * @param capacity
   * @param oldStack
   */
  public IntegerFiFoCircularStack(int capacity, IntegerFiFoCircularStack oldStack) {
      this(capacity);
      while (!oldStack.isEmpty())
    push(oldStack.pop(0));
  }

  /**
   * Clears the stack.
   */
  public void clear() {
      synchronized (stack) {
    for (int i = 0; i < stack.length; i++) {
        stack[i] = 0;
    }
    top = 0;
    bottom = 0;
      }
  }

  /**
   * Puts a value on the stack.
   *
   * @param value
   */
  public void push(int value) {
      if (storedValues == capacity) {
    top = bottom;
    bottom++;
    if (bottom == capacity) {
        bottom = 0;
    }
      } else
    storedValues++;

      if (top == capacity)
    top = 0;

      stack[top++] = value;
  }

  /**
   * Returns the oldest value from the stack. Returns the supplied entry if the stack is empty.
   *
   * @param valueIfEmpty
   * @return int
   */
  public int pop(int valueIfEmpty) {
      if (isEmpty())
    return valueIfEmpty;

      storedValues--;
      int result = stack[bottom++];

      if (bottom == capacity)
    bottom = 0;

      return result;
  }

  /**
   * Returns the oldest value from the stack and negates the value. Returns the supplied entry if the stack is empty.
   *
   * @param valueIfEmpty
   * @return int
   */
  public int popNegate(int valueIfEmpty) {
      return pop(valueIfEmpty) * -1;
  }

  /**
   * Returns the oldest value from the stack without removing the value from the stack. Returns the supplied entry if the stack is empty.
   *
   * @param valueIfEmpty
   * @return int
   */
  public int peek(int valueIfEmpty) {
      if (storedValues > 0)
    return stack[bottom];
      return valueIfEmpty;
  }

  /**
   *
   * @return boolean
   */
  public boolean isEmpty() {
      return storedValues == 0;
  }

  /**
   *
   * @return boolean
   */
  public boolean isFull() {
      return storedValues == capacity;
  }

  /**
   *
   * @return boolean
   */
  public int getLoad() {
      return storedValues;
  }

  public int getCapacity() {
      return capacity;
  }
    }

    public Oscilloscope(Composite parent, int style, Color backgroundColor, Color foregroundColor, Color gridColor) {
  this(1, null, parent, style, backgroundColor, foregroundColor, gridColor);
    }

    public Oscilloscope(int channels, Composite parent, int style, Color backgroundColor, Color foregroundColor, Color gridColor) {
  this(channels, null, parent, style, backgroundColor, foregroundColor, gridColor);
    }

    /**
     * Creates a new Oscilloscope.
     *
     * @param parent
     * @param style
     */
    public Oscilloscope(int channels, OscilloscopeDispatcher dispatcher, Composite parent, int style, Color backgroundColor, Color foregroundColor,
      Color gridColor) {
  super(parent, SWT.DOUBLE_BUFFERED | style);
  bg = backgroundColor;
  mGridColor = gridColor;
  chan = new Data[channels];
  for (int i = 0; i < chan.length; i++) {

      chan[i] = new Data();

      if (dispatcher == null)
    chan[i].dispatcher = new OscilloscopeDispatcher(i, this);
      else {
    chan[i].dispatcher = dispatcher;
    dispatcher.setOscilloscope(this);
      }

      setBackground(bg);

      chan[i].fg = foregroundColor;

      setTailSize(i, TAILSIZE_DEFAULT);
  }

  addDisposeListener(new DisposeListener() {
      @Override
      public void widgetDisposed(DisposeEvent e) {
    Oscilloscope.this.widgetDisposed(e);
      }
  });

  addPaintListener(new PaintListener() {
      @Override
      public void paintControl(PaintEvent e) {
    if (!paintBlock)
        Oscilloscope.this.paintControl(e);
    paintBlock = false;
      }
  });

  addControlListener(new ControlListener() {
      @Override
      public void controlResized(ControlEvent e) {
    paintBlock = true;
    Oscilloscope.this.controlResized(e);

      }

      @Override
      public void controlMoved(ControlEvent e) {
    Oscilloscope.this.controlMoved(e);
      }
  });

    }

    /**
     * @param e
     *            no need to use e
     */
    protected void controlMoved(ControlEvent e) {
  // not interested; I guess
    }

    /**
     * @param e
     *            no need to use e
     */
    protected void controlResized(ControlEvent e) {
  setSizeInternal(getSize().x, getSize().y);

  if (getBounds().width > 0) {
      for (int channel = 0; channel < chan.length; channel++) {

    setSteady(channel, chan[channel].steady, chan[channel].originalSteadyPosition);
    setTailSizeInternal(channel);
      }
  }
    }

    /**
     * Returns the size of the tail.
     *
     * @return int
     * @see #setTailSize(int)
     * @see #TAILSIZE_DEFAULT
     * @see #TAILSIZE_FILL
     * @see #TAILSIZE_MAX
     *
     */
    public int getTailSize(int channel) {
  checkWidget();
  return chan[channel].tailSize;
    }

    private void setSizeInternal(int width, int height) {

  for (int c = 0; c < chan.length; c++) {

      chan[c].width = width;
      chan[c].height = height;

      // calculate the base of the line
      calculateBase(c);

      if (width > 1)
    if (chan[c].stack == null)
        chan[c].stack = new IntegerFiFoCircularStack(width);
    else
        chan[c].stack = new IntegerFiFoCircularStack(width, chan[c].stack);
  }
    }

    /**
     * Gets the relative location where the line is drawn in the widget.
     *
     * @return baseOffset
     */
    public int getBaseOffset(int channel) {
  return chan[channel].baseOffset;
    }

    /**
     * Gets the relative location where the line is drawn in the widget, the default is <code>BASE_CENTER</code> which is in the middle of the scope.
     *
     * @param baseOffset
     *            must be between 100 and -100, exceeding values are rounded to the closest allowable value.
     */
    public void setBaseOffset(int channel, int baseOffset) {

  if (baseOffset > 100)
      chan[channel].baseOffset = 100;

  else if (baseOffset < -100)
      chan[channel].baseOffset = -100;
  else
      chan[channel].baseOffset = baseOffset;

  calculateBase(channel);
    }

    private void calculateBase(int channel) {
  if ((myLowRangeValue != 0) || (myhighValue != 0))
      chan[channel].base = 0;
  else if (chan[channel].height > 2)
      chan[channel].base = (chan[channel].height * +(100 - getBaseOffset(channel))) / 100;
    }

    protected void widgetDisposed(DisposeEvent e) {
  // bg.dispose();
  // for (int channel = 0; channel < chan.length; channel++) {
  // chan[channel].fg.dispose();
  // }
    }

    protected int ConvertValueToScreenPosition(int Value, int ScreenHeight) {
  if ((myLowRangeValue == 0) && (myhighValue == 0))
      return ScreenHeight - Value;
  float ret = ((float) (Value - myLowRangeValue) / (float) (myhighValue - myLowRangeValue)) * (float) ScreenHeight;
  return ScreenHeight - (int) ret;
    }

    protected int ConvertScreenPositionToValue(int screenYPosition) {
  // todo write this stuff
  return 0;
    }

    protected void PositionPolyLine(int[] l1) {
  for (int i = 0; i < l1.length - 4; i += 4) {
      l1[i + 1] = ConvertValueToScreenPosition(l1[i + 1], getSize().y);
      l1[i + 3] = ConvertValueToScreenPosition(l1[i + 3], getSize().y);
  }
    }

    protected void paintControl(PaintEvent e) {

  long start = System.currentTimeMillis();

  for (int c = 0; c < chan.length; c++) {

      // if (chan[c].tailSize <= 0) {
      // chan[c].stack.popNegate(0);
      // continue;
      // }

      // Go calculate the line
      Object[] result = calculate(c);
      int[] l1 = (int[]) result[0];
      int[] l2 = (int[]) result[1];

      PositionPolyLine(l1);
      PositionPolyLine(l2);
      // System.out.print(System.currentTimeMillis() - start + "-");

      // Draw it
      GC gc = e.gc;
      gc.setForeground(getForeground(c));
      gc.setAdvanced(true);
      gc.setAntialias(SWT.ON);
      gc.setLineWidth(getLineWidth(c));

      // Fade tail
      if (isFade(c)) {
    gc.setAlpha(0);
    double fade = 0;
    double fadeOutStep = (double) 125 / (double) ((getTailSize(c) * (getTailFade(c)) / 100));
    for (int i = 0; i < l1.length - 4;) {
        fade += (fadeOutStep / 2);
        setAlpha(gc, fade);

        gc.drawLine(l1[i], l1[i + 1], l1[i + 2], l1[i + 3]);
        i += 2;
    }

    for (int i = 0; i < l2.length - 4;) {
        fade += (fadeOutStep / 2);
        setAlpha(gc, fade);
        gc.drawLine(l2[i], l2[i + 1], l2[i + 2], l2[i + 3]);
        i += 2;
    }

      } else {
    long time = System.nanoTime();
    gc.drawPolyline(l1);
    gc.drawPolyline(l2);
    // System.out.println(System.nanoTime() - time + " nanoseconds");
      }

      // Connects the head with the tail
      if (isConnect(c) && !isFade(c) && chan[c].originalTailSize == TAILSIZE_MAX && l1.length > 0 && l2.length > 0) {
    gc.drawLine(l2[l2.length - 2], l2[l2.length - 1], l1[0], l1[1]);
      }
  }

  // System.out.println(System.currentTimeMillis() - start + " milliseconds for all channels");

    }

    public Color getForeground(int channel) {
  checkWidget();
  return chan[channel].fg;
    }

    public void setForeground(int channel, Color color) {
  chan[channel].fg = color;
    }

    /**
     * This method calculates the progression of the line.
     *
     * @return
     */
    private Object[] calculate(int channel) {

  int c = channel;

  int[] line1 = null;
  int[] line2 = null;
  int splitPos = 0;

  // for (int progress = 0; progress < getProgression(c); progress++) {

  // if (chan[c].stack.isEmpty() && chan[c].stackListeners != null)
  // notifyListeners(c);

  splitPos = chan[c].tailSize * 4;

  if (!isSteady(c))
      chan[c].cursor++;
  if (chan[c].cursor >= chan[c].width)
      chan[c].cursor = 0;

  line1 = new int[chan[c].tailSize * 4];
  line2 = new int[chan[c].tailSize * 4];

  // chan[c].tail[chan[c].tailSize] = transform(c, chan[c].width,
  // chan[c].height, chan[c].stack.popNegate(0));

  for (int i = 0; i < chan[c].tailSize; i++) {

      int posx = chan[c].cursor - chan[c].tailSize + i;
      int pos = i * 4;
      if (posx < 0) {
    posx += chan[c].width;
    line1[pos] = posx - 1;

    line1[pos + 1] = getBase(c) + (isSteady(c) ? 0 : chan[c].tail[i]);
    line1[pos + 2] = posx;
    line1[pos + 3] = getBase(c) + (isSteady(c) ? 0 : chan[c].tail[i + 1]);
      }

      else {
    if (splitPos == chan[c].tailSize * 4)
        splitPos = pos;
    line2[pos] = posx - 1;
    line2[pos + 1] = getBase(c) + chan[c].tail[i];
    line2[pos + 2] = posx;
    line2[pos + 3] = (getBase(c) + chan[c].tail[i + 1]);
      }
      // chan[c].tail[tailIndex - 1] = chan[c].tail[tailIndex++];
  }
  // }

  int[] l1 = new int[splitPos];
  System.arraycopy(line1, 0, l1, 0, l1.length);
  int[] l2 = new int[(chan[c].tailSize * 4) - splitPos];
  System.arraycopy(line2, splitPos, l2, 0, l2.length);

  return new Object[] { l1, l2 };
    }

    // private void printTails() {
    // if (originalTailInput != null) {
    // System.out.print("x " + tail[0]);
    // for (int i = 1; i < tail.length; i++) {
    // System.out.print(", " + tail[i]);
    // }
    // System.out.println();
    // System.out.print("o " + originalTailInput[0]);
    // for (int i = 1; i < tail.length; i++) {
    // System.out.print(", " + originalTailInput[i]);
    // }
    // System.out.println();
    // System.out.println("----");
    // System.out.println();
    // }
    // }

    /**
     * Transforms the value before it is drawn.
     *
     * @param value
     *            the next value to be processed
     * @return the transformed value
     */
    private int transform(int channel, int vWidth, int vHeight, int value) {
  if (isPercentage(channel))
      return (((vHeight / 2) * value) / 100);
  return value;
    }

    /**
     * This method sets the view of the monitor This range is used for all channels which are not set on percentage To go back to default set lowValue
     * and highValue back to 0 This method invalidates the widget so it gets redrawn.
     *
     * @param lowValue
     *            The value at the bottom of the monitor
     * @param highValue
     *            The value at the top of the monitor
     */
    public void setRange(int lowValue, int highValue) {
  myLowRangeValue = lowValue;
  myhighValue = highValue;
  setnewBackgroundImage();

    }

    public void setnewBackgroundImage() {
  Image old = getBackgroundImage();
  if (null != old)
      old.dispose();
  setBackgroundImage(CreateBackgroundImage(5, 6));
    }

    /**
     * Create a background image that shows the scale and channels as requested
     *
     * @return
     */
    private Image CreateBackgroundImage(int numHorizontalLines, int numVerticalLines) {
  // * Image i = new Image(device, boundsRectangle);
  // * GC gc = new GC(i);
  // * gc.drawRectangle(0, 0, 50, 50);
  // * gc.dispose();
  Rectangle rect = getBounds();
  if ((rect.width == 0) || (rect.height == 0))
      return null;
  Image TheImage = new Image(getDisplay(), rect);
  GC gc = new GC(TheImage);
  gc.setBackground(bg);
  int step = (getSize().y - 20) / (numHorizontalLines - 1);
  int width = getSize().x;
  int height = getSize().y;
  gc.fillRectangle(0, 0, width, getSize().y);
  gc.setForeground(mGridColor);
  int Value = myLowRangeValue + (int) (10.0 * (float) (myhighValue - myLowRangeValue) / (float) rect.height);
  int RangeStep = (int) ((float) (rect.height - 20) / (float) (numHorizontalLines - 1) * (float) (myhighValue - myLowRangeValue) / (float) rect.height);
  for (int i = 0; i < numHorizontalLines; i++) {
      gc.drawLine(0, 10 + (step * i), width, 10 + (step * i));
      gc.drawString(Integer.toString(Value + RangeStep * (numHorizontalLines - (i + 1))), 10, 10 + (step * i));
  }
  step = (getSize().x - 20) / (numVerticalLines - 1);
  for (int i = 0; i < numVerticalLines; i++) {
      gc.drawLine(10 + (step * i), 0, 10 + (step * i), height);
  }
  step = (int) Math.round(gc.getFont().getFontData()[0].height * 1.5);
  if (ShowLabels) {
      for (int i = 0; i < chan.length; i++) {
    gc.setForeground(chan[i].fg);
    gc.drawString(chan[i].name, 50, 10 + (step * i));
      }
  }
  gc.setForeground(chan[0].fg);
  gc.drawString(myStatus, 200, 10);

  // gc.drawString(string, x, y)
  gc.dispose();
  return TheImage;
    }

    /**
     *
     * @return the value represented by the bottom of the oscilloscope
     */
    public int getLowRangeValue() {
  return myLowRangeValue;
    }

    /**
     *
     * @return the value represented by the top of the oscilloscope
     */
    public int getHighRangeValue() {
  return myhighValue;
    }

    /**
     * @return the base of the line
     */
    public int getBase(int channel) {
  return chan[channel].base;
    }

    /**
     * @return the number of internal calculation steps at each draw request.
     * @see #setProgression(int)
     */
    public int getProgression(int channel) {
  return chan[channel].progression;
    }

    /**
     * The number of internal steps that must be made before drawing. Normally this will slide the graph one pixel. Setting this to a higher value
     * will speed up the animation at the cost of a more jerky motion.
     *
     * @param progression
     */
    public void setProgression(int channel, int progression) {
  if (progression > 0)
      chan[channel].progression = progression;

    }

    private void notifyListeners(int channel) {
  if (chan[channel].stackListeners == null || chan[channel].stackListeners.size() == 0)
      return;
  for (int i = 0; i < chan[channel].stackListeners.size(); i++) {
      ((OscilloscopeStackAdapter) chan[channel].stackListeners.get(i)).stackEmpty(this, channel);
  }
    }

    /**
     * @return boolean, true if the tail and the head of the graph must be connected if tail size is {@link #TAILSIZE_MAX} no fading graph.
     */
    public boolean isConnect(int channel) {
  checkWidget();
  return chan[channel].connect;
    }

    /**
     * @return int, number of channels.
     */
    public int getChannels() {
  checkWidget();
  return chan.length;
    }

    /**
     * Connects head and tail only if tail size is {@link #TAILSIZE_MAX} and no fading.
     *
     * @param connectHeadAndTail
     */
    public void setConnect(int channel, boolean connectHeadAndTail) {
  checkWidget();
  chan[channel].connect = connectHeadAndTail;
    }

    /**
     * @see #setFade(boolean)
     * @return boolean fade
     */
    public boolean isFade(int channel) {
  checkWidget();
  return chan[channel].fade;
    }

    /**
     * Sets fade mode so that a percentage of the tail will be faded out at the costs of extra CPU utilization (no beauty without pain or as the Dutch
     * say: "Wie mooi wil gaan moet pijn doorstaan"). The reason for this is that each pixel must be drawn separately with alpha faded in instead of
     * the elegant {@link GC#drawPolygon(int[])} routine which does not support alpha blending.
     * <p>
     * In addition to this, set the percentage of tail that must be faded out {@link #setTailFade(int)}.
     *
     * @param fade
     *            true or false
     * @see #setTailFade(int)
     */
    public void setFade(int channel, boolean fade) {
  checkWidget();
  chan[channel].fade = fade;
    }

    private void setAlpha(GC gc, double fade) {

  if (gc.getAlpha() == fade)
      return;
  if (fade >= 255)
      gc.setAlpha(255);
  else
      gc.setAlpha((int) fade);
    }

    /**
     * gets the percentage of tail that must be faded out.
     *
     * @return int percentage
     * @see #setFade(boolean)
     */
    public int getTailFade(int channel) {
  checkWidget();
  return chan[channel].tailFade;
    }

    /**
     * @return boolean steady indicator
     * @see Oscilloscope#setSteady(boolean, int)
     */
    public boolean isSteady(int channel) {
  checkWidget();
  return chan[channel].steady;
    }

    /**
     * Set a bunch of values that will be drawn. The values will be stored in a stack and popped once a value is needed. The size of the stack is the
     * width of the widget. If you resize the widget, the old stack will be copied into a new stack with the new capacity.
     *
     * @param values
     */
    public synchronized void setValues(int channel, int[] values) {
  checkWidget();

  if (getBounds().width <= 0)
      return;

  if (!super.isVisible())
      return;

  if (chan[channel].stack == null)
      chan[channel].stack = new IntegerFiFoCircularStack(chan[channel].width);

  for (int i = 0; i < values.length; i++) {
      chan[channel].stack.push(values[i]);
  }
    }

    /**
     * Sets a value to be drawn relative to the middle of the widget. Supply a positive or negative value.
     *
     * @param value
     */
    public void setValueOrg(int channel, int value) {
  checkWidget();
  if (getBounds().width <= 0)
      return;

  if (!super.isVisible())
      return;

  if (chan[channel].stack.capacity > 0)
      chan[channel].stack.push(value);
    }

    public void setValue(int channel, int value) {
  // chan[c].tail[tailIndex - 1] = chan[c].tail[tailIndex++];
  int copysize = chan[channel].tail.length;
  System.arraycopy(chan[channel].tail, 1, chan[channel].tail, 0, copysize - 1);
  chan[channel].tail[chan[channel].tailSize] = value;
    }

    /**
     * The tail size defaults to TAILSIZE_DEFAULT which is 75% of the width. Setting it with TAILSIZE_MAX will leave one pixel between the tail and
     * the head. All values are absolute except TAILSIZE*. If the width is smaller then the tail size then the tail size will behave like
     * TAILSIZE_MAX.
     *
     * @param size
     *            the size of the tail
     * @see #getTailSize()
     * @see #TAILSIZE_DEFAULT
     * @see #TAILSIZE_FILL
     * @see #TAILSIZE_MAX
     */
    public void setTailSize(int channel, int size) {
  checkWidget();

  if (size == TAILSIZE_FILL && !isSteady(channel))
      size = TAILSIZE_MAX;

  if (chan[channel].originalTailSize != size) {
      tailSizeCheck(size);
      chan[channel].originalTailSize = size;
      setTailSizeInternal(channel);
  }
    }

    private void tailSizeCheck(int size) {
  if (size < -3 || size == 0)
      throw new RuntimeException("Invalid tail size " + size);
    }

    private void setTailSizeInternal(int channel) {

  if (chan[channel].originalTailSize == TAILSIZE_DEFAULT) {
      // tail = new int[(width / 4) * 3];
      chan[channel].tailSize = (chan[channel].width / 4) * 3;
      chan[channel].tailSize--;
  } else if (chan[channel].originalTailSize == TAILSIZE_FILL) {
      if (isSteady(channel)) {
    chan[channel].tailSize = chan[channel].originalSteadyPosition - 1;
      } else { // act as if TAILSIZE_MAX
         // tail = new int[width - 2 + 1];
    chan[channel].tailSize = chan[channel].width - 2;
      }
  } else if (chan[channel].originalTailSize == TAILSIZE_MAX || chan[channel].originalTailSize > chan[channel].width) {
      // tail = new int[width - 2 + 1];
      chan[channel].tailSize = chan[channel].width - 2;
  } else if (chan[channel].tailSize != chan[channel].originalTailSize) {
      // tail = new int[originalTailSize + 1];
      chan[channel].tailSize = chan[channel].originalTailSize;
  }

  // Transform the old tail. This is we want to see sort of the same form
  // after resize.
  int[] oldTail = chan[channel].tail;
  if (oldTail == null) {
      chan[channel].tail = new int[chan[channel].tailSize + 1];
  } else {
      chan[channel].tail = new int[chan[channel].tailSize + 1];
      if (chan[channel].tail.length >= oldTail.length) {
    for (int i = 0; i < oldTail.length; i++) {
        chan[channel].tail[chan[channel].tail.length - 1 - i] = oldTail[oldTail.length - 1 - i];
    }
      } else {
    for (int i = 0; i < chan[channel].tail.length; i++) {
        chan[channel].tail[chan[channel].tail.length - 1 - i] = oldTail[oldTail.length - 1 - i];
    }
      }
  }

  // System.out.println(Arrays.toString(tail));
  // System.out.println(Arrays.toString(oldTail));
    }

    @Override
    public Point computeSize(int wHint, int hHint, boolean changed) {
  checkWidget();

  int width;
  int height;

  if (wHint != SWT.DEFAULT)
      width = wHint;
  else
      width = DEFAULT_WIDTH;

  if (hHint != SWT.DEFAULT)
      height = hHint;
  else
      height = DEFAULT_HEIGHT;

  return new Point(width + 2, height + 2);
    }

    public boolean needsRedraw() {
  checkWidget();
  return isDisposed() ? false : true;
    }

    /**
     * Sets the line width. A value equal or below zero is ignored. The default width is 1.
     *
     * @param lineWidth
     */
    public void setLineWidth(int channel, int lineWidth) {
  checkWidget();
  if (lineWidth > 0)
      chan[channel].lineWidth = lineWidth;
    }

    /**
     * @return int, the width of the line.
     * @see #setLineWidth(int)
     */
    public int getLineWidth(int channel) {
  checkWidget();
  return chan[channel].lineWidth;
    }

    /**
     * If set to true then the values are treated as percentages of the available space rather than absolute values. This will scale the amplitudes if
     * the control is resized. Default is false.
     *
     * @param percentage
     *            true if percentages
     */
    public void setPercentage(int channel, boolean percentage) {
  checkWidget();
  chan[channel].percentage = percentage;
    }

    /**
     * @return boolean
     * @see #setPercentage(boolean)
     */
    public boolean isPercentage(int channel) {
  checkWidget();
  return chan[channel].percentage;
    }

    /**
     * If steady is true the graph will draw on a steady position instead of advancing.
     *
     * @param steady
     * @param steadyPosition
     */
    public void setSteady(int channel, boolean steady, int steadyPosition) {
  checkWidget();
  chan[channel].steady = steady;
  chan[channel].originalSteadyPosition = steadyPosition;
  if (steady)
      if (steadyPosition == STEADYPOSITION_75PERCENT)
    chan[channel].cursor = (int) ((double) chan[channel].width * (double) 0.75);
      else if (steadyPosition > 0 && steadyPosition < chan[channel].width)
    chan[channel].cursor = steadyPosition;
  // setTailSizeInternal();
    }

    /**
     * Sets the percentage of tail that must be faded out. If you supply 100 then the tail is faded out all the way to the top. The effect will become
     * increasingly less obvious.
     *
     * @param tailFade
     */
    public void setTailFade(int channel, int tailFade) {
  checkWidget();
  if (tailFade > 100)
      tailFade = 100;
  if (tailFade < 1)
      tailFade = 1;
  chan[channel].tailFade = tailFade;
    }

    /**
     * Adds a new stack listener to the collection of stack listeners. Adding the same listener twice will have no effect.
     *
     * @param listener
     */
    public synchronized void addStackListener(int channel, OscilloscopeStackAdapter listener) {
  checkWidget();
  if (chan[channel].stackListeners == null)
      chan[channel].stackListeners = new ArrayList<OscilloscopeStackAdapter>();
  if (!chan[channel].stackListeners.contains(listener))
      chan[channel].stackListeners.add(listener);
    }

    /**
     * Removes a stack listener from the collection of stack listeners.
     *
     * @param listener
     */
    public void removeStackListener(int channel, OscilloscopeStackAdapter listener) {
  checkWidget();
  if (chan[channel].stackListeners != null) {
      chan[channel].stackListeners.remove(listener);
      if (chan[channel].stackListeners.size() == 0)
    synchronized (chan[channel].stackListeners) {
        chan[channel].stackListeners = null;
    }
  }
    }

    public OscilloscopeDispatcher getDispatcher(int channel) {
  return chan[channel].dispatcher;
    }

    public void setDispatcher(int channel, OscilloscopeDispatcher dispatcher) {
  chan[channel].dispatcher = dispatcher;
    }

    @Override
    public void dispose() {
  // TODO Auto-generated method stub
  super.dispose();
    }

}
TOP

Related Classes of multichannel.Oscilloscope$Data

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.