Package org.timepedia.chronoscope.client.render

Source Code of org.timepedia.chronoscope.client.render.DatasetRenderer

package org.timepedia.chronoscope.client.render;

import org.timepedia.chronoscope.client.Cursor;
import org.timepedia.chronoscope.client.Dataset;
import org.timepedia.chronoscope.client.XYPlot;
import org.timepedia.chronoscope.client.canvas.Bounds;
import org.timepedia.chronoscope.client.canvas.Color;
import org.timepedia.chronoscope.client.canvas.Layer;
import org.timepedia.chronoscope.client.canvas.View;
import org.timepedia.chronoscope.client.data.AbstractDataset;
import org.timepedia.chronoscope.client.data.FlyweightTuple;
import org.timepedia.chronoscope.client.data.MipMap;
import org.timepedia.chronoscope.client.data.tuple.Tuple2D;
import org.timepedia.chronoscope.client.data.RenderedPoint;
import org.timepedia.chronoscope.client.gss.GssElement;
import org.timepedia.chronoscope.client.gss.GssProperties;
import org.timepedia.chronoscope.client.plot.DefaultXYPlot;
import org.timepedia.chronoscope.client.util.Array1D;
import org.timepedia.chronoscope.client.util.DateFormatter;
import org.timepedia.chronoscope.client.util.Interval;
import org.timepedia.chronoscope.client.util.JavaArray2D;
import org.timepedia.chronoscope.client.util.date.DateFormatterFactory;
import org.timepedia.exporter.client.Exportable;

import java.util.HashMap;
import java.util.HashSet;

import static org.timepedia.chronoscope.client.render.DatasetLegendPanel.LEGEND_ICON_PAD;
import static org.timepedia.chronoscope.client.render.DatasetLegendPanel.LEGEND_ICON_SIZE;


/**
* Responsible for visually rendering a {@link Dataset} onto a {@link Layer}.
*/
public abstract class DatasetRenderer<T extends Tuple2D>
    implements GssElement, Exportable {

  protected boolean isGssInitialized = false;

  private boolean customInstalled = false;

  private DateFormatter guideLineDateFmt;

  // protected HashMap<String, FlyweightTuple> regions = new HashMap<String, FlyweightTuple>();  // for hit detection of points, features, etc

  protected HashMap<String, HashSet<RenderedPoint>> regions = new HashMap<String, HashSet<RenderedPoint>>()// for hit detection of points, features, etc

  protected double DOMAIN_REGIONS = 32d;
  protected double RANGE_REGIONS = 8d;

  // hit detection range =~  org.timepedia.chronoscope.client.plot.DefaultXYPlot.MAX_FOCUS_DIST;
  protected double HIT_DISTANCE = 8d;

  private String[] scratchRegions;

  private HashSet<String> scratchX = new HashSet<String>(3);
  private HashSet<String> scratchY = new HashSet<String>(3);

  protected int focusDimension = 0;
  // private int focus = 0;

  public void clearRegions() {
    regions.clear();
  }


  protected String[] getRegions(double plotX, double plotY) {
    Bounds b = plot.getBounds();
    if (plotX < 0) { plotX = 0; }
    if (plotX > b.width) { plotX = b.width; }
    if (plotY < 0) { plotY = 0; }
    if (plotY > b.height) { plotY = b.height; }
    scratchX.clear();
    scratchY.clear();


    scratchX.add(naturalize((plotX / b.width) * DOMAIN_REGIONS));

    double px = plotX - HIT_DISTANCE;
    px = Math.max(0, px);
    scratchX.add(naturalize((px / b.width) * DOMAIN_REGIONS));

    px = plotX + HIT_DISTANCE;
    px = Math.min(b.width, px);
    scratchX.add(naturalize((px / b.width) * DOMAIN_REGIONS));


    scratchY.add(naturalize((plotY / b.height) * RANGE_REGIONS));

    double py = plotY - HIT_DISTANCE;
    py  = Math.max(0, py);
    scratchY.add(naturalize((py / b.height) * RANGE_REGIONS));

    py = plotY + HIT_DISTANCE;
    py = Math.min(b.height, py);
    scratchY.add(naturalize((py / b.height) * RANGE_REGIONS));

    String[] X = scratchX.toArray(new String[scratchX.size()]);
    String[] Y = scratchY.toArray(new String[scratchY.size()]);
    scratchRegions = new String[X.length * Y.length];
    int l = 0;
    for (int i=0; i < X.length; i++) {
      for (int j=0; j < Y.length; j++) {
        scratchRegions[l] = X[i] + "," + Y[j];
        l++;
      }
    }

    return scratchRegions;
  }

  protected void regionalize(int datasetIndex, int domainIndex, int dim, double domain, double range, double plotX, double plotY) {
    String[] re = getRegions(plotX, plotY);
    for (int i=0; i<re.length; i++) {
      if (null == regions.get(re[i])) {
        regions.put(re[i], new HashSet<RenderedPoint>());
      }
      regions.get(re[i]).add(new RenderedPoint(datasetIndex, domainIndex, dim, domain, range, plotX, plotY));
    }
  }

  private String naturalize(double d) {
    String s = String.valueOf(Math.floor(d));
    int i = s.indexOf('.');
    if ( i > -1) {
      s = s.substring(0,i);
    }
    return s;
  }

  public void addClickable(int datasetIndex, int domainIndex, int dim, double domain, double range, double plotX, double plotY) {
    regionalize(datasetIndex, domainIndex, dim, domain, range, plotX, plotY);
  }

  public HashSet<RenderedPoint> getClickable(int plotX, int plotY) {
    Bounds b = plot.getBounds();
    String d = naturalize((plotX / b.width) * DOMAIN_REGIONS);
    String r = naturalize((plotY / b.height) * RANGE_REGIONS);

    return regions.get(d+","+r);
  }

  public boolean isCustomInstalled() {
    return customInstalled;
  }

  public void setCustomInstalled(boolean customInstalled) {
    this.customInstalled = customInstalled;
  }

  protected GssElement parentGssElement;

  protected GssProperties gssDisabledFillProps, gssDisabledLineProps, gssDisabledPointProps,
                          gssFillProps, gssLineProps, gssPointProps,
                          gssFocusFillProps, gssFocusLineProps, gssFocusPointProps,
                          gssActiveFillProps, gssActiveLineProps, gssActivePointProps,
                          gssFocusGuidelineProps,
                          gssHoverProps,
                          gssLegendProps;

  protected int datasetIndex;

  protected XYPlot<T> plot;

  /**
   * Called before first data is plotted, typically drawing state is setup, or a
   * path is begun.
   */
  public abstract void beginCurve(Layer layer, RenderState renderState);

  /**
   * Called before points are plotted, typically to setup drawing state (colors,
   * etc).
   */
  public abstract void beginPoints(Layer layer, RenderState renderState);

  public void clear() {
    parentGssElement = null;

    gssDisabledFillProps = null;
    gssDisabledLineProps = null;
    gssDisabledPointProps = null;

    gssFillProps = null;
    gssLineProps = null;
    gssPointProps = null;

    gssFocusFillProps = null;
    gssFocusLineProps = null;
    gssFocusPointProps = null;

    gssActiveFillProps = null;
    gssActiveLineProps = null;
    gssActivePointProps = null;

    gssFocusGuidelineProps = null;
    gssHoverProps = null;
    gssLegendProps = null;

    plot = null;
  }
  /**
   * Called for each visible data point, typically a segment is added to the
   * current drawing path, unless a more sophisticated shape like a bar chart is
   * being rendered.
   */
  public abstract void drawCurvePart(int datasetIndex, int domainIndex, Layer layer, T tuplDataPoint, int methodCallCount, RenderState renderState);


  public void drawFocusPointGuideLine(Layer layer, int x) {
      layer.save();
      String textLayer = "overlays";
      layer.clearTextLayer(textLayer);

      // layer.setFillColor(gssFocusGuidelineProps.color);

      // layer.setFillColor(gssFocusPointProps.color);

      double lt = Math.max(gssFocusGuidelineProps.lineThickness, 1);
      int coffset = (int) Math.floor(lt / 2.0);

      layer.fillRect(x - coffset, 0, lt, layer.getBounds().height);

      // for now, don't bother drawing label on point guidelines
      /*
      if (gssFocusGuidelineProps.dateFormat != null) {
        layer.setStrokeColor(Color.BLACK);
        int hx = x;
        double dx = ((DefaultXYPlot) plot).windowXtoDomain(hx + ((DefaultXYPlot) plot).getBounds().x);
        String label = guideLineDateFmt.format(dx);
        hx += dx < plot.getDomain().midpoint() ? 1.0 : -1 - layer.stringWidth(label, "Helvetica", "", "8pt");

        layer.drawText(hx, plot.getInnerBounds().y + 10, label, "Helvetica", "", "8pt", textLayer, Cursor.CONTRASTED);
      }
      */
      layer.restore();
  }

  /**
   * Draws the hover point for each dataset managed by the plot.
   */
  public abstract void drawHoverPoint(Layer layer, T point, int datasetIndex);

  /**
   * Render a small icon or sparkline representing this curve at the given x,y
   * screen coordinates, and return the the Bounds of the icon.
   */
  public void drawLegendIcon(Layer layer, double x, double y, double w, double h, int dim) {
      layer.save();

      GssProperties alineProp;
      if (plot.getFocus() != null
          && plot.getFocus().getDatasetIndex() != this.datasetIndex) {
        alineProp = gssDisabledLineProps;
      } else {
        alineProp = gssLineProps;
      }

      // layer.setShadowBlur(alineProp.shadowBlur);
      // layer.setShadowColor(alineProp.shadowColor);
      // layer.setShadowOffsetX(alineProp.shadowOffsetX);
      // layer.setShadowOffsetY(alineProp.shadowOffsetY);

      layer.setTransparency((float) alineProp.transparency);
      layer.setFillColor(alineProp.color);
      layer.fillRect(x, y, w, h);
      layer.restore();
    }


  /**
   * Draw an individual point of the given tuple.
   */
  public abstract void drawPoint(int datasetIndex, int domainIndex, Layer layer, T tupleDataPoint, RenderState renderState);

  /**
   * Called after last data is plotted (last call to drawCurvePart), typically
   * when stroke() or fill() is invoked.
   */
  public abstract void endCurve(Layer layer, RenderState renderState);

  /**
   * Called after all points are plotted, typically to cleanup state (restore()
   * after a save() ).
   */
  public abstract void endPoints(Layer layer, RenderState renderState);

  /**
   * The maximum number of datapoints that should be drawn in the view and
   * maintain interactive framerates for this renderer.
   */
  public int getMaxDrawableDatapoints() {
    return plot.getMaxDrawableDataPoints();
  }

  /**
   * Subclasses can override this method to return a domain span that's larger
   * than the maximum according to current mipmapped domain associated with the
   * {@link DrawableDataset}.  This is sometimes necessary depending on how each
   * datapoint is rendered (e.g. barchart requires domain padding to avoid
   * cropping of the end point bars).
   */
  protected Interval getDrawableDomain(Array1D mipmappedDomain) {
    return new Interval(mipmappedDomain.get(0), mipmappedDomain.getLast());
  }

  public final GssElement getParentGssElement() {
    return this.parentGssElement;
  }

  public final void setDatasetIndex(int datasetIndex) {
    this.datasetIndex = datasetIndex;
  }

  public final void setParentGssElement(GssElement parentGssElement) {
    this.parentGssElement = parentGssElement;
    isGssInitialized = false;
  }

  public final void setPlot(XYPlot<T> plot) {
    this.plot = plot;
  }

  public void initGss(View view) {
    if (isGssInitialized) {
      return;
    }

    GssElement fillElement = new GssElementImpl("fill", parentGssElement);
    GssElement pointElement = new GssElementImpl("point", parentGssElement);

    gssFillProps = view.getGssProperties(fillElement, "");
    gssLineProps = view.getGssProperties(this, "");
    gssPointProps = view.getGssProperties(pointElement, "");

    gssDisabledFillProps = view.getGssProperties(fillElement, "disabled");
    gssDisabledLineProps = view.getGssProperties(this, "disabled");
    gssDisabledPointProps = view.getGssProperties(pointElement, "disabled");

    gssFocusFillProps = view.getGssProperties(fillElement, "focus");
    gssFocusLineProps = view.getGssProperties(this, "focus");
    gssFocusPointProps = view.getGssProperties(pointElement, "focus");

    gssFocusGuidelineProps = view.getGssProperties(new GssElementImpl("guideline", pointElement), "focus");
    if (gssFocusGuidelineProps.dateFormat != null) {
      this.guideLineDateFmt = DateFormatterFactory.getInstance()
          .getDateFormatter(gssFocusGuidelineProps.dateFormat);
    }

    gssHoverProps = view.getGssProperties(pointElement, "hover");

    gssLegendProps = view.getGssProperties(new GssElementImpl("axislegend" , parentGssElement), "");

    isGssInitialized = true;
  }

  public GssProperties getLegendProperties(int dim, RenderState rs) {
    if (plot.getFocus() != null
        && plot.getFocus().getDatasetIndex() != this.datasetIndex) {
      return gssDisabledLineProps;
    } else {
      return gssLineProps;
    }
  }

  public GssProperties getCurveProperties() {
    return gssLineProps;
  }

  public int getNumPasses(Dataset dataset) {
    return 1;
  }

  public double getRange(Tuple2D tuple2D) {
    return tuple2D.getRange0();
  }

  public Interval getRangeExtrema(Dataset ds) {
    return ds.getRangeExtrema(0);
  }

  public Interval getRangeExtrema(MipMap ds) {
    return ds.getRangeExtrema(0);
  }

  public int getFocusDimension() {
    return focusDimension;
  }

  public int setFocusDimension(int focusDimension) {
    return this.focusDimension = focusDimension;
  }

  public int[] getLegendEntries(Dataset dataset) {
    return getPassOrder(dataset);
  }

  public double getRangeValue(Tuple2D tuple, int dimension) {
    return tuple.getRange0();
  }

  public int[] getPassOrder(Dataset<T> dataSet) {
    return new int[]{0};
  }
}
TOP

Related Classes of org.timepedia.chronoscope.client.render.DatasetRenderer

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.