Package plotter.xy

Source Code of plotter.xy.DefaultCompressor$DefaultStreamingCompressor

/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* The MCT platform is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
package plotter.xy;

import java.util.Iterator;
import java.util.NavigableMap;
import java.util.Map.Entry;

import plotter.DoubleData;
import plotter.internal.RangeSet;

/**
* Default implementation of {@link Compressor}.
* Optimized for mostly continuous data.
* Works best when, on the small scale, the data is either non-increasing or non-decreasing.
* @author Adam Crume
*/
public class DefaultCompressor implements Compressor {
  @Override
  public void compress(PointData input, PointData output, double offset, double scale) {
    assert !Double.isNaN(offset);
    assert !Double.isInfinite(offset);
    assert !Double.isNaN(scale);
    assert !Double.isInfinite(scale);
    assert scale != 0;
    DoubleData inx = input.getX();
    DoubleData iny = input.getY();
    int size = inx.getLength();
    if(size == 0) {
      return;
    }
    long bucket = (long) ((inx.get(0) - offset) / scale);
    double nextx = (bucket + 1) * scale + offset;
    int i = 0;
    double oldy = Double.NaN;
    RangeSet r = new RangeSet();
    long bucketSize = 0;
    double firsty = Double.NaN;
    while(true) {
      double x = inx.get(i);
      double y = iny.get(i);
      if(x >= nextx) {
        double bucketx = bucket * scale + offset;
        flushBucket(output, bucketx, firsty, r, oldy, bucketSize);
        r.clear();
        bucket = (int) ((x - offset) / scale);
        nextx = (bucket + 1) * scale + offset;
        bucketSize = 0;
        firsty = y;
        if(!Double.isNaN(y)) {
          r.add(y, y);
        }
      } else {
        if(bucketSize == 0) {
          firsty = y;
        }
        if(!Double.isNaN(y)) {
          if(Double.isNaN(oldy)) {
            r.add(y, y);
          } else {
            double min, max;
            if(oldy > y) {
              max = oldy;
              min = y;
            } else {
              max = y;
              min = oldy;
            }
            r.add(min, max);
          }
        }
      }
      oldy = y;
      i++;
      bucketSize++;
      if(i >= size) {
        double bucketx = bucket * scale + offset;
        flushBucket(output, bucketx, firsty, r, oldy, bucketSize);
        return;
      }
    }
  }


  private void flushBucket(CompressionOutput out, double bucketx, double firsty, RangeSet r, double lasty,
      long bucketSize) {
    out.add(bucketx, firsty);
    double prevy = firsty;
    NavigableMap<Double, Double> data2 = r.getData();
    if(data2 == null) {
      double min = r.getMin();
      double max = r.getMax();
      if(min != Double.POSITIVE_INFINITY) {
        boolean wroteMin = false;
        // Since we always draw the first and last, it would be redundant to draw the min or max if it is equal to either.
        if(!(min == firsty || min == lasty)) {
          out.add(bucketx, min);
          prevy = min;
          wroteMin = true;
        }
        if(!(max == firsty || max == lasty || (wroteMin && max == min))) {
          out.add(bucketx, max);
          prevy = max;
        }
      } else if(bucketSize > 1 && !Double.isNaN(lasty) && !Double.isNaN(firsty)) {
        out.add(bucketx, Double.NaN);
        prevy = Double.NaN;
      }
    } else {
      boolean first = true;
      NavigableMap<Double, Double> m = data2;
      if(firsty > lasty) {
        // If the line is descending (based on the first and last Y coordinates),
        // draw the segments in decreasing order.  This improves the odds that
        // we will be able to merge the first and last points into line segments.
        m = data2.descendingMap();
      }
      for(Iterator<Entry<Double, Double>> itr = m.entrySet().iterator(); itr.hasNext();) {
        Entry<Double, Double> e = itr.next();
        double min = e.getKey();
        double max = e.getValue();
        boolean last = !itr.hasNext();
        boolean wroteSomething = true;
        // Since RangeSet will not return a map if there is only one segment, the current segment cannot be first and last.
        if(first) {
          if(firsty > max || firsty < min) {
            out.add(bucketx, Double.NaN);
            prevy = Double.NaN;
          }
          boolean wroteMin = false;
          if(!(min == firsty)) {
            out.add(bucketx, min);
            prevy = min;
            wroteMin = true;
          }
          if(!(max == firsty || (wroteMin && min == max))) {
            out.add(bucketx, max);
            prevy = max;
          }
        } else if(last) {
          boolean wroteMin = false;
          if(!(min == lasty)) {
            out.add(bucketx, min);
            prevy = min;
            wroteMin = true;
          }
          if(!(max == lasty || (wroteMin && min == max))) {
            out.add(bucketx, max);
            prevy = max;
          }
        } else {
          if(min == max) {
            if(!(min == firsty || min == lasty)) {
              out.add(bucketx, min);
              prevy = min;
            } else {
              wroteSomething = false;
            }
          } else {
            out.add(bucketx, min);
            out.add(bucketx, max);
            prevy = max;
          }
        }
        if(wroteSomething) {
          if(!last || lasty < min || lasty > max) {
            out.add(bucketx, Double.NaN);
            prevy = Double.NaN;
          }
        }
        first = false;
      }
    }
    if(!equal(lasty, prevy)) {
      out.add(bucketx, lasty);
    }
  }


  private static boolean equal(double a, double b) {
    return a == b || (Double.isNaN(a) && Double.isNaN(b));
  }


  @Override
  public StreamingCompressor createStreamingCompressor(CompressionOutput output, double offset, double scale) {
    assert !Double.isNaN(offset) : "Offset cannot be NaN";
    assert !Double.isInfinite(offset) : "Offset cannot be infinite";
    assert !Double.isNaN(scale) : "Scale cannot be NaN";
    assert !Double.isInfinite(scale) : "Scale cannot be infinite";
    assert scale != 0 : "Scale cannot be zero";
    return new DefaultStreamingCompressor(output, offset, scale);
  }


  /**
   * Default implementation of {@link Compressor.StreamingCompressor}.
   * @author Adam Crume
   */
  public class DefaultStreamingCompressor implements StreamingCompressor {
    private CompressionOutput out;

    private double oldy = Double.NaN;

    private RangeSet r = new RangeSet();

    private long bucketSize;

    private double nextx;

    private double firsty;

    private long bucket;

    private boolean first = true;

    private double offset;

    private double scale;

    private int tmpPoints;


    /**
     * Creates a streaming compressor.
     * @param out receives the compressed data
     * @param offset compression offset
     * @param scale compression scale
     */
    public DefaultStreamingCompressor(CompressionOutput out, double offset, double scale) {
      this.out = out;
      this.offset = offset;
      this.scale = scale;
    }


    @Override
    public int add(double x, double y) {
      if(first) {
        bucket = (long) ((x - offset) / scale);
        nextx = (bucket + 1) * scale + offset;
        first = false;
      }
      out.removeLast(tmpPoints);
      int sizeBeforeModification = out.getPointCount();
      if(x >= nextx) {
        double bucketx = bucket * scale + offset;
        flushBucket(out, bucketx, firsty, r, oldy, bucketSize);
        r.clear();
        bucket = (int) ((x - offset) / scale);
        nextx = (bucket + 1) * scale + offset;
        bucketSize = 0;
        firsty = y;
        if(!Double.isNaN(y)) {
          r.add(y, y);
        }
      } else {
        if(bucketSize == 0) {
          firsty = y;
        }
        if(!Double.isNaN(y)) {
          if(Double.isNaN(oldy)) {
            r.add(y, y);
          } else {
            double min, max;
            if(oldy > y) {
              max = oldy;
              min = y;
            } else {
              max = y;
              min = oldy;
            }
            r.add(min, max);
          }
        }
      }
      oldy = y;
      bucketSize++;
      int sizeWithoutTempArea = out.getPointCount();
      double bucketx = bucket * scale + offset;
      flushBucket(out, bucketx, firsty, r, y, bucketSize);
      tmpPoints = out.getPointCount() - sizeWithoutTempArea;
      return out.getPointCount() - sizeBeforeModification;
    }
  }
}
TOP

Related Classes of plotter.xy.DefaultCompressor$DefaultStreamingCompressor

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.