Package com.sencha.gxt.chart.client.chart.series

Source Code of com.sencha.gxt.chart.client.chart.series.LineSeries

/**
* Sencha GXT 3.0.0 - Sencha for GWT
* Copyright(c) 2007-2012, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.chart.client.chart.series;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.sencha.gxt.chart.client.draw.Color;
import com.sencha.gxt.chart.client.draw.DrawFx;
import com.sencha.gxt.chart.client.draw.RGB;
import com.sencha.gxt.chart.client.draw.Translation;
import com.sencha.gxt.chart.client.draw.path.LineTo;
import com.sencha.gxt.chart.client.draw.path.MoveTo;
import com.sencha.gxt.chart.client.draw.path.PathCommand;
import com.sencha.gxt.chart.client.draw.path.PathSprite;
import com.sencha.gxt.chart.client.draw.sprite.Sprite;
import com.sencha.gxt.chart.client.draw.sprite.SpriteList;
import com.sencha.gxt.core.client.util.PrecisePoint;
import com.sencha.gxt.core.client.util.PreciseRectangle;
import com.sencha.gxt.core.shared.FastMap;
import com.sencha.gxt.data.shared.ListStore;

/**
* Creates a Line Chart. A Line Chart is a useful visualization technique to
* display quantitative information for different categories or other real
* values (as opposed to the {@link BarSeries}), that can show some progression
* (or regression) in the data set.
*
* @param <M> the data type used by the series
*/
public class LineSeries<M> extends ScatterSeries<M> {

  private PathSprite line;
  private PathSprite fillSprite;
  private SpriteList<PathSprite> lineShadows = new SpriteList<PathSprite>();
  private boolean showMarkers = false;
  private boolean smooth = false;
  private int segments = 3;
  private Color fill = RGB.NONE;
  private List<PathCommand> lineCommands = new ArrayList<PathCommand>();
  private List<PathCommand> previousCommands;
  private double markerIndex = 0;
  private SeriesRenderer<M> lineRenderer;
  private SeriesRenderer<M> fillRenderer;
  private boolean gapless = true;
  private SeriesHighlighter lineHighlighter = new LineHighlighter();
  private FastMap<Integer> gapPosition = new FastMap<Integer>();

  /**
   * Creates a line {@link Series}.
   */
  public LineSeries() {
    // setup shadow attributes
    shadowAttributes = new ArrayList<Sprite>();
    Sprite config = new PathSprite();
    config.setStrokeWidth(6);
    config.setStrokeOpacity(0.05);
    config.setStroke(RGB.BLACK);
    config.setTranslation(1, 1);
    shadowAttributes.add(config);
    config = new PathSprite();
    config.setStrokeWidth(4);
    config.setStrokeOpacity(0.1);
    config.setStroke(RGB.BLACK);
    config.setTranslation(1, 1);
    shadowAttributes.add(config);
    config = new PathSprite();
    config.setStrokeWidth(2);
    config.setStrokeOpacity(0.15);
    config.setStroke(RGB.BLACK);
    config.setTranslation(1, 1);
    shadowAttributes.add(config);

    // initialize shadow groups
    if (shadowGroups.size() == 0) {
      for (int i = 0; i < shadowAttributes.size(); i++) {
        shadowGroups.add(new SpriteList<Sprite>());
      }
    }

    setHighlighter(new ScatterHighlighter());
    strokeWidth = 0.5;
  }

  @Override
  public void clear() {
    super.clear();
    if (line != null) {
      line.remove();
      line = null;
    }
    if (fillSprite != null) {
      fillSprite.remove();
      fillSprite = null;
    }
    lineShadows.clear();
  }

  @Override
  public void drawSeries() {
    List<PathCommand> fillCommands = new ArrayList<PathCommand>();
    boolean onbreak = false;
    double firstY = Double.NaN;
    Sprite marker;
    ListStore<M> store = chart.getCurrentStore();
    lineCommands.clear();
    gapPosition.clear();

    if (store == null || store.size() == 0) {
      return;
    }

    if (markerIndex > 0 && sprites.size() > 0) {
      for (int i = 0; i < markerIndex; i++) {
        marker = sprites.get(i);
        sprites.remove(i);
        sprites.add(marker);
        Sprite markerTemp = sprites.get(sprites.size() - 2);
        marker.setTranslation(new Translation(markerTemp.getTranslation()));
        marker.redraw();
      }
    }

    calculateBounds();

    double x = 0;
    double y = 0;
    boolean gap = false;
    for (int i = 0; i < store.size(); i++) {
      PrecisePoint point = coordinates.get(i);
      if (point != null) {
        x = point.getX();
        y = point.getY();
      } else {
        x = y = Double.NaN;
      }
      if (!Double.isNaN(x) && !Double.isNaN(y)) {
        if (onbreak) {
          onbreak = false;
        }
        if (lineCommands.size() > 0 && !gap) {
          lineCommands.add(new LineTo(x, y));
        } else {
          lineCommands.add(new MoveTo(x, y));
          gap = false;
        }
        gapPosition.put(String.valueOf(lineCommands.size() - 1), i);
      } else if (!gapless) {
        gap = true;
      }

      if (Double.isNaN(firstY) && !Double.isNaN(y)) {
        firstY = y;
      }
    }

    List<PathCommand> renderCommands;

    if (smooth && lineCommands.size() > 0) {
      PathSprite smooth = new PathSprite();
      smooth.setCommands(lineCommands);
      renderCommands = smooth.copy().toSmooth(segments).getCommands();
    } else {
      renderCommands = PathSprite.copyCommands(lineCommands);
    }

    // Correct path if we're animating timeAxis intervals
    if (markerIndex > 0 && previousCommands != null && previousCommands.size() > 1) {
      previousCommands.remove(1);
      line.setCommands(previousCommands);
      if (chart.hasShadows()) {
        for (int i = 0; i < lineShadows.size(); i++) {
          PathSprite shadow = lineShadows.get(i);
          shadow.setCommands(previousCommands);
        }
      }
    }

    List<PathCommand> dummyCommands = new ArrayList<PathCommand>();
    dummyCommands.add(new MoveTo(bbox.getX(), bbox.getY() + bbox.getHeight() / 2.0));
    for (int k = 1; k < lineCommands.size(); k++) {
      dummyCommands.add(new LineTo(bbox.getX() + bbox.getWidth() / k, bbox.getY() + bbox.getHeight() / 2.0));
    }

    // Only create a line if one doesn't exist.
    if (line == null) {
      line = new PathSprite();
      line.setStroke(stroke);
      chart.addSprite(line);
      line.setCommands(dummyCommands);

      if (chart.hasShadows()) {
        // create shadows
        for (int i = 0; i < shadowGroups.size(); i++) {
          PathSprite shadow = new PathSprite();
          Sprite shadowAttr = shadowAttributes.get(i);
          shadow.setStrokeWidth(shadowAttr.getStrokeWidth());
          shadow.setStrokeOpacity(shadowAttr.getStrokeOpacity());
          shadow.setStroke(shadowAttr.getStroke());
          shadow.setTranslation(new Translation(shadowAttr.getTranslation()));
          shadow.setFill(Color.NONE);
          shadow.setCommands(line.getCommands());
          chart.addSprite(shadow);
          lineShadows.add(shadow);
        }
      }
    }
    if (stroke != null) {
      line.setStroke(stroke);
    }
    if (!Double.isNaN(strokeWidth)) {
      line.setStrokeWidth(strokeWidth);
    }
    line.setFill(Color.NONE);

    if (chart.isAnimated() && line.size() > 0) {
      if (markerIndex > 0) {
        if (smooth) {
          renderCommands.remove(1);
        } else {
          MoveTo move = (MoveTo) renderCommands.get(0);
          renderCommands.add(1, new LineTo(move.getX(), move.getY()));
        }
        previousCommands = renderCommands;
      }
      DrawFx.createCommandsAnimator(line, renderCommands).run(500);
    } else {
      line.setCommands(renderCommands);
      line.redraw();
    }

    if (lineRenderer != null) {
      lineRenderer.spriteRenderer(line, 0, chart.getStore());
    }

    if (fill != Color.NONE && fill != null) {
      fillCommands.addAll(renderCommands);
      fillCommands.add(new LineTo(x, bbox.getY() + bbox.getHeight()));
      fillCommands.add(new LineTo(bbox.getX(), bbox.getY() + bbox.getHeight()));
      fillCommands.add(new LineTo(bbox.getX(), firstY));

      if (fillSprite == null) {
        fillSprite = new PathSprite();
        fillSprite.setOpacity(0.3);
        fillSprite.setFill(fill);
        fillSprite.setCommands(dummyCommands);
        chart.addSprite(fillSprite);
      }
      if (chart.isAnimated() && fillSprite.size() > 0) {
        DrawFx.createCommandsAnimator(fillSprite, fillCommands).run(chart.getAnimationDuration(),
            chart.getAnimationEasing());
      } else {
        fillSprite.setCommands(fillCommands);
        fillSprite.redraw();
      }
      if (fillRenderer != null) {
        fillRenderer.spriteRenderer(fillSprite, 0, chart.getStore());
      }
    }

    if (chart.hasShadows()) {
      for (int i = 0; i < lineShadows.size(); i++) {
        PathSprite shadow = lineShadows.get(i);
        if (!hidden) {
          shadow.setHidden(false);
        }
        if (chart.isAnimated()) {
          DrawFx.createCommandsAnimator(shadow, renderCommands).run(chart.getAnimationDuration(),
              chart.getAnimationEasing());
        } else {
          shadow.setCommands(renderCommands);
          shadow.redraw();
        }
        if (shadowRenderer != null) {
          shadowRenderer.spriteRenderer(shadow, i, chart.getCurrentStore());
        }
      }
      shadowed = true;
    } else {
      hideShadows();
    }

    if (showMarkers) {
      for (int i = 0; i < lineCommands.size(); i++) {
        int index = i;
        PrecisePoint point = getPointFromCommand(lineCommands.get(index));
        if (index < sprites.size()) {
          marker = sprites.get(index);
          marker.setHidden(false);
        } else {
          marker = markerConfig.copy();
          if (i == 0) {
            marker.setTranslation(bbox.getX(), bbox.getY() + bbox.getHeight() / 2.0);
          } else {
            marker.setTranslation(bbox.getX() + bbox.getWidth() / i, bbox.getY() + bbox.getHeight() / 2.0);
          }
          sprites.add(marker);
          chart.addSprite(marker);
        }
        if (hidden) {
          marker.setHidden(true);
        }
        if (chart.isAnimated() && marker.getTranslation() != null
            && ((markerIndex <= 0) || (markerIndex > 0 && index != sprites.size() - 1))) {
          DrawFx.createTranslationAnimator(marker, point.getX(), point.getY()).run(chart.getAnimationDuration(),
              chart.getAnimationEasing());
        } else {
          marker.setTranslation(point.getX(), point.getY());
          marker.redraw();
        }
        if (renderer != null) {
          renderer.spriteRenderer(marker, index, store);
        }
      }
    }

    for (int j = lineCommands.size(); j < sprites.size(); j++) {
      sprites.get(j).setHidden(true);
      sprites.get(j).redraw();
    }
    drawLabels();
  }

  /**
   * Returns the fill color of the line.
   *
   * @return the fill color of the line
   */
  public Color getFill() {
    return fill;
  }

  /**
   * Returns the {@link SeriesRenderer} used on the fill sprite.
   *
   * @return the series renderer used on the fill sprite
   */
  public SeriesRenderer<M> getFillRenderer() {
    return fillRenderer;
  }

  public SeriesHighlighter getLineHighlighter() {
    return lineHighlighter;
  }

  /**
   * Returns the {@link SeriesRenderer} used on the line sprite.
   *
   * @return the series renderer used on the line sprite
   */
  public SeriesRenderer<M> getLineRenderer() {
    return lineRenderer;
  }

  /**
   * Returns the marker index of the series. Determines the number of markers to
   * animate to simulate progression.
   *
   * @return the marker index of the series
   */
  public double getMarkerIndex() {
    return markerIndex;
  }

  /**
   * Returns the number of segments of the smoothed line.
   *
   * @return the number of segments of the smoothed line
   */
  public int getSegments() {
    return segments;
  }

  @Override
  public void hide(int yFieldIndex) {
    toggle(true);
  }

  @Override
  public void highlight(int yFieldIndex) {
    if (showMarkers) {
      highlighter.highlight(sprites.get(yFieldIndex));
    }
  }

  @Override
  public void highlightAll(int index) {
    if (line != null) {
      lineHighlighter.highlight(line);
    }
    for (int i = 0; i < sprites.size(); i++) {
      highlighter.highlight(sprites.get(i));
    }
  }

  /**
   * Returns whether the line is drawn over gaps in field.
   *
   * @return whether the line is drawn over gaps in field
   */
  public boolean isGapless() {
    return gapless;
  }

  /**
   * Returns whether or not markers are shown.
   *
   * @return whether or not markers are shown
   */
  public boolean isShowMarkers() {
    return showMarkers;
  }

  /**
   * Returns whether or not the line is smoothed or straight.
   *
   * @return true if smooth
   */
  public boolean isSmooth() {
    return smooth;
  }

  /**
   * Sets the fill color of the line. If none the fill will not be drawn.
   *
   * @param fill the color of the fill
   */
  public void setFill(Color fill) {
    this.fill = fill;
  }

  /**
   * Sets the {@link SeriesRenderer} used on the fill sprite.
   *
   * @param fillRenderer the series renderer used on the fill sprite
   */
  public void setFillRenderer(SeriesRenderer<M> fillRenderer) {
    this.fillRenderer = fillRenderer;
  }

  /**
   * Sets whether the line is drawn over gaps in field. Gaps do not work with
   * {@link #setFill(Color)}.
   *
   * @param gapless whether the line is drawn over gaps in field
   */
  public void setGapless(boolean gapless) {
    this.gapless = gapless;
  }

  public void setLineHighlighter(SeriesHighlighter lineHighlighter) {
    this.lineHighlighter = lineHighlighter;
  }

  /**
   * Sets the {@link SeriesRenderer} used on the line sprite
   *
   * @param lineRenderer the series renderer used on the line sprite
   */
  public void setLineRenderer(SeriesRenderer<M> lineRenderer) {
    this.lineRenderer = lineRenderer;
  }

  /**
   * Sets the marker index. Determines the number of markers to animate to
   * simulate progression.
   *
   * @param markerIndex the marker index
   */
  public void setMarkerIndex(double markerIndex) {
    this.markerIndex = markerIndex;
  }

  /**
   * Sets the number of segments of the smoothed line. Smooth line enabled using
   * {@link #setSmooth(boolean)}.
   *
   * @param segments the number of segments of the smoothed line
   */
  public void setSegments(int segments) {
    this.segments = segments;
  }

  /**
   * Sets whether or not to show markers.
   *
   * @param showMarkers whether or not to show markers
   */
  public void setShowMarkers(boolean showMarkers) {
    if (this.showMarkers != showMarkers) {
      this.showMarkers = showMarkers;
      sprites.clear();
      for (int i = 0; i < shadowGroups.size(); i++) {
        shadowGroups.get(i).clear();
      }
    }
  }

  /**
   * Sets whether or not the line is smoothed or straight.
   *
   * @param smooth true if smooth
   */
  public void setSmooth(boolean smooth) {
    this.smooth = smooth;
  }

  @Override
  public void show(int yFieldIndex) {
    toggle(false);
  }

  @Override
  public void unHighlight(int yFieldIndex) {
    if (showMarkers) {
      highlighter.unHighlight(sprites.get(yFieldIndex));
    }
  }

  @Override
  public void unHighlightAll(int index) {
    if (line != null) {
      lineHighlighter.unHighlight(line);
    }
    for (int i = 0; i < sprites.size(); i++) {
      highlighter.unHighlight(sprites.get(i));
    }
  }

  @Override
  public boolean visibleInLegend(int index) {
    if (line == null) {
      return true;
    } else {
      return !line.isHidden();
    }
  }

  /**
   * Draws the labels on the series.
   */
  protected void drawLabels() {
    for (int j = lineCommands.size(); j < labels.size(); j++) {
      labels.get(j).setHidden(true);
    }
    if (labelConfig != null) {
      for (int i = 0; i < lineCommands.size(); i++) {
        final Sprite sprite;
        if (labels.get(i) != null) {
          sprite = labels.get(i);
          if (!hidden) {
            sprite.setHidden(false);
          }
        } else {
          sprite = labelConfig.getSpriteConfig().copy();
          labels.put(i, sprite);
          chart.addSprite(sprite);
        }
        setLabelText(sprite, i);
        sprite.redraw();
        if (labelConfig.isLabelContrast()) {
          final Sprite back = sprites.get(i);
          if (chart.isAnimated()) {
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
              @Override
              public void execute() {
                setLabelContrast(sprite, labelConfig, back);
              }
            });
          } else {
            setLabelContrast(sprite, labelConfig, back);
          }
        }
        PrecisePoint point = getPointFromCommand(lineCommands.get(i));
        double x = point.getX();
        double y = point.getY();
        if (showMarkers) {
          PreciseRectangle textBox = sprite.getBBox();

          y -= textBox.getHeight() / 2.0;
        }
        if (chart.isAnimated() && sprite.getTranslation() != null) {
          DrawFx.createTranslationAnimator(sprite, x, y).run(chart.getAnimationDuration(), chart.getAnimationEasing());
        } else {
          sprite.setTranslation(x, y);
          sprite.redraw();
        }
      }
    }
  }

  @Override
  protected int getIndex(PrecisePoint point) {
    for (int i = 0; i < lineCommands.size(); i++) {
      PrecisePoint pointCommand = getPointFromCommand(lineCommands.get(i));
      if (point.equalsNoPrecision(new PrecisePoint(pointCommand.getX(), pointCommand.getY()), selectionTolerance)) {
        return gapPosition.get(String.valueOf(i));
      }
    }
    return -1;
  }

  @Override
  protected void hideShadows() {
    if (shadowed) {
      for (int i = 0; i < lineShadows.size(); i++) {
        lineShadows.get(i).setHidden(true);
        lineShadows.get(i).redraw();
      }
    }
    super.hideShadows();
  }

  /**
   * Toggles all the sprites in the series to be hidden or shown.
   *
   * @param hide if true hides
   */
  private void toggle(boolean hide) {
    hidden = hide;
    if (line != null) {
      line.setHidden(hide);
      line.redraw();
      if (chart.hasShadows()) {
        for (int i = 0; i < lineShadows.size(); i++) {
          Sprite shadow = lineShadows.get(i);
          shadow.setHidden(hide);
          shadow.redraw();
        }
      }
    }
    if (fillSprite != null) {
      fillSprite.setHidden(hide);
      fillSprite.redraw();
    }
    if (sprites.size() > 0) {
      for (int i = 0; i < sprites.size(); i++) {
        sprites.get(i).setHidden(hide);
        sprites.get(i).redraw();
      }
      // Series needs to be redrawn if shown and there are gaps in the data
      if (hide == false && lineCommands.size() != chart.getCurrentStore().size()) {
        drawSeries();
      }
    }

  }

}
TOP

Related Classes of com.sencha.gxt.chart.client.chart.series.LineSeries

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.