Package com.sencha.gxt.chart.client.draw.engine

Source Code of com.sencha.gxt.chart.client.draw.engine.SVG

/**
* 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.draw.engine;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.sencha.gxt.chart.client.draw.Gradient;
import com.sencha.gxt.chart.client.draw.Matrix;
import com.sencha.gxt.chart.client.draw.Rotation;
import com.sencha.gxt.chart.client.draw.Scaling;
import com.sencha.gxt.chart.client.draw.Stop;
import com.sencha.gxt.chart.client.draw.Translation;
import com.sencha.gxt.chart.client.draw.path.PathSprite;
import com.sencha.gxt.chart.client.draw.sprite.CircleSprite;
import com.sencha.gxt.chart.client.draw.sprite.EllipseSprite;
import com.sencha.gxt.chart.client.draw.sprite.ImageSprite;
import com.sencha.gxt.chart.client.draw.sprite.RectangleSprite;
import com.sencha.gxt.chart.client.draw.sprite.Sprite;
import com.sencha.gxt.chart.client.draw.sprite.TextSprite;
import com.sencha.gxt.chart.client.draw.sprite.TextSprite.TextAnchor;
import com.sencha.gxt.chart.client.draw.sprite.TextSprite.TextBaseline;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.PreciseRectangle;

/**
* Provides specific methods to draw with SVG.
*/
public class SVG extends DomSurface {
  /**
   * {@link JavaScriptObject} representing bounding box results of an
   * {@link SVG} text element.
   */
  public static final class TextBBox extends JavaScriptObject {

    protected TextBBox() {
    }

    /**
     * Returns the height of the SVG text element
     *
     * @return the height of the SVG text element
     */
    public native double getHeight() /*-{
                                     return this.height;
                                     }-*/;

    /**
     * Returns the width of the SVG text element
     *
     * @return the width of the SVG text element
     */
    public native double getWidth() /*-{
                                    return this.width;
                                    }-*/;

    /**
     * Returns the x-coordinate of the SVG text element
     *
     * @return the x-coordinate of the SVG text element
     */
    public native double getX() /*-{
                                return this.x;
                                }-*/;

    /**
     * Returns the y-coordinate of the SVG text element
     *
     * @return the y-coordinate of the SVG text element
     */
    public native double getY() /*-{
                                return this.y;
                                }-*/;

  }

  private static int clipId = 0;
  private Element defs;

  @Override
  public void addGradient(Gradient gradient) {
    this.gradients.add(gradient);
    Element gradientElement;
    double radAngle = Math.toRadians(gradient.getAngle());
    double[] vector = {0, 0, Math.cos(radAngle), Math.sin(radAngle)};
    double temp = Math.max(Math.abs(vector[2]), Math.abs(vector[3]));
    if (temp == 0) temp = 1;
    double max = 1 / temp;
    vector[2] *= max;
    vector[3] *= max;
    if (vector[2] < 0) {
      vector[0] = -vector[2];
      vector[2] = 0;
    }
    if (vector[3] < 0) {
      vector[1] = -vector[3];
      vector[3] = 0;
    }
    gradientElement = this.createSVGElement("linearGradient");
    gradientElement.setAttribute("x1", String.valueOf(vector[0]));
    gradientElement.setAttribute("y1", String.valueOf(vector[1]));
    gradientElement.setAttribute("x2", String.valueOf(vector[2]));
    gradientElement.setAttribute("y2", String.valueOf(vector[3]));
    gradientElement.setId(gradient.getId());
    getDefinitions().appendChild(gradientElement);
    for (int i = 0; i < gradient.getStops().size(); i++) {
      Stop stop = gradient.getStops().get(i);
      Element stopEl = createSVGElement("stop");
      stopEl.setAttribute("offset", String.valueOf(stop.getOffset()) + "%");
      stopEl.setAttribute("stop-color", stop.getColor().toString());
      stopEl.setAttribute("stop-opacity", String.valueOf(stop.getOpacity()));
      gradientElement.appendChild(stopEl);
    }

  }

  @Override
  public void deleteSprite(Sprite sprite) {
    surfaceElement.removeChild(getElement(sprite));
    elements.remove(sprite);
    sprites.remove(sprite);
  }

  @Override
  public void draw() {
    super.draw();
    if (surfaceElement == null) {
      surfaceElement = this.createSVGElement("svg");
      surfaceElement.setAttribute("xmlns", "http://www.w3.org/2000/svg");
      surfaceElement.setAttribute("version", "1.1");
      surfaceElement.setAttribute("width", String.valueOf(width));
      surfaceElement.setAttribute("height", String.valueOf(height));
      Element bgRect = this.createSVGElement("rect");
      bgRect.setAttribute("width", "100%");
      bgRect.setAttribute("height", "100%");
      bgRect.setAttribute("fill", "#000");
      bgRect.setAttribute("stroke", "none");
      bgRect.setAttribute("opacity", "0");

      surfaceElement.appendChild(getDefinitions());
      surfaceElement.appendChild(bgRect);
      container.appendChild(surfaceElement);
    }

    Collections.sort(sprites, zIndexComparator());

    renderAll();
  }

  @Override
  public void renderSprite(Sprite sprite) {
    if (surfaceElement == null) {
      return;
    }
    if (getElement(sprite) == null) {
      createSprite(sprite);
    }
    if (!sprite.isDirty()) {
      return;
    }
    applyZIndex(sprite);

    applyAttributes(sprite);
    if (sprite.isTransformDirty()) {
      transform(sprite);
    }
    sprite.clearDirtyFlags();
  }

  @Override
  public void setCursor(Sprite sprite, String property) {
    XElement element = getElement(sprite);
    if (element != null) {
      element.getStyle().setProperty("cursor", property);
    }
  }

  @Override
  public void setViewBox(double x, double y, double width, double height) {
    surfaceElement.setAttribute(
        "viewBox",
        new StringBuilder().append(x).append(", ").append(y).append(", ").append(width).append(", ").append(height).toString());
  }

  @Override
  protected PreciseRectangle getBBoxText(TextSprite sprite) {
    PreciseRectangle bbox = new PreciseRectangle();
    XElement element = getElement(sprite);

    if (element != null && element.isVisible(true)) {
      TextBBox box = getTextBBox(element);
      bbox.setX(box.getX());
      bbox.setY(box.getY());
      bbox.setWidth(box.getWidth());
      bbox.setHeight(box.getHeight());
    }

    return bbox;
  }

  /**
   * Applies the attributes of the given sprite to its SVG element.
   *
   * @param sprite the sprite to have its attributes set
   */
  private void applyAttributes(Sprite sprite) {
    XElement element = getElement(sprite);

    if (sprite instanceof PathSprite) {
      PathSprite path = (PathSprite) sprite;
      if (path.size() > 0) {
        element.setAttribute("d", path.toString());
      }
      if (path.isStrokeLineCapDirty()) {
        setAttribute(element, "stroke-linecap", path.getStrokeLineCap());
      }
      if (path.isStrokeLineJoinDirty()) {
        setAttribute(element, "stroke-linejoin", path.getStrokeLineJoin());
      }
      if (path.isMiterLimitDirty()) {
        setAttribute(element, "stroke-miterlimit", path.getMiterLimit());
      }
    } else if (sprite instanceof TextSprite) {
      TextSprite text = (TextSprite) sprite;
      if (text.isTextDirty() || text.isXDirty()) {
        tuneText(text);
      }
      if (text.isFontSizeDirty()) {
        if (text.getFontSize() > 0) {
          element.getStyle().setFontSize(text.getFontSize(), Unit.PX);
        } else {
          element.getStyle().clearFontSize();
        }
      }
      if (text.isFontStyleDirty()) {
        if (text.getFontStyle() != null) {
          element.getStyle().setFontStyle(text.getFontStyle());
        } else {
          element.getStyle().clearFontStyle();
        }
      }
      if (text.isFontWeightDirty()) {
        if (text.getFontWeight() != null) {
          element.getStyle().setFontWeight(text.getFontWeight());
        } else {
          element.getStyle().clearFontWeight();
        }
      }
      if (text.isFontDirty()) {
        setAttribute(element, "font-family", text.getFont());
      }
      if (text.isTextAnchorDirty()) {
        if (text.getTextAnchor() == TextAnchor.START) {
          element.setAttribute("text-anchor", "start");
        } else if (text.getTextAnchor() == TextAnchor.MIDDLE) {
          element.setAttribute("text-anchor", "middle");
        } else if (text.getTextAnchor() == TextAnchor.END) {
          element.setAttribute("text-anchor", "end");
        } else {
          element.removeAttribute("text-anchor");
        }
      }
      if (text.isXDirty()) {
        setAttribute(element, "x", text.getX());
      }
      if (text.isYDirty()) {
        setAttribute(element, "y", text.getY());
      }
    } else if (sprite instanceof RectangleSprite) {
      RectangleSprite rect = (RectangleSprite) sprite;
      if (rect.isXDirty()) {
        setAttribute(element, "x", rect.getX());
      }
      if (rect.isYDirty()) {
        setAttribute(element, "y", rect.getY());
      }
      if (rect.isWidthDirty()) {
        setAttribute(element, "width", rect.getWidth());

      }
      if (rect.isHeightDirty()) {
        setAttribute(element, "height", rect.getHeight());
      }
      if (rect.isRadiusDirty()) {
        setAttribute(element, "rx", rect.getRadius());
        setAttribute(element, "ry", rect.getRadius());

      }
    } else if (sprite instanceof CircleSprite) {
      CircleSprite circle = (CircleSprite) sprite;
      if (circle.isCenterXDirty()) {
        setAttribute(element, "cx", circle.getCenterX());
      }
      if (circle.isCenterYDirty()) {
        setAttribute(element, "cy", circle.getCenterY());
      }
      if (circle.isRadiusDirty()) {
        setAttribute(element, "r", circle.getRadius());
      }
    } else if (sprite instanceof EllipseSprite) {
      EllipseSprite ellipse = (EllipseSprite) sprite;
      if (ellipse.isCenterXDirty()) {
        setAttribute(element, "cx", ellipse.getCenterX());
      }
      if (ellipse.isCenterYDirty()) {
        setAttribute(element, "cy", ellipse.getCenterY());
      }
      if (ellipse.isRadiusXDirty()) {
        setAttribute(element, "rx", ellipse.getRadiusX());
      }
      if (ellipse.isRadiusYDirty()) {
        setAttribute(element, "ry", ellipse.getRadiusY());
      }
    } else if (sprite instanceof ImageSprite) {
      ImageSprite image = (ImageSprite) sprite;
      if (image.isResourceDirty() && image.getResource() != null) {
        element.setAttributeNS("http://www.w3.org/1999/xlink", "href", image.getResource().getSafeUri().asString());
      }
      if (image.isXDirty()) {
        setAttribute(element, "x", image.getX());
      }
      if (image.isYDirty()) {
        setAttribute(element, "y", image.getY());
      }
      if (image.isHeightDirty() && !Double.isNaN(image.getHeight())) {
        element.setAttribute("height", image.getHeight() + "px");
      }
      if (image.isWidthDirty() && !Double.isNaN(image.getWidth())) {
        element.setAttribute("width", image.getWidth() + "px");
      }
    }

    if (sprite.isStrokeDirty()) {
      setAttribute(element, "stroke", sprite.getStroke());
    }
    if (sprite.isStrokeWidthDirty()) {
      setAttribute(element, "stroke-width", sprite.getStrokeWidth());
    }
    if (sprite.isFillDirty()) {
      setAttribute(element, "fill", sprite.getFill());
    }
    if (sprite.isFillOpacityDirty()) {
      setAttribute(element, "fill-opacity", sprite.getFillOpacity());
    }
    if (sprite.isStrokeOpacityDirty()) {
      setAttribute(element, "stroke-opacity", sprite.getStrokeOpacity());
    }
    if (sprite.isOpacityDirty()) {
      setAttribute(element, "opacity", sprite.getOpacity());
    }
    String id = spriteIds.get(sprite);
    if (id != null) {
      element.setId(id);
    }

    // Hide or show the sprite
    if (sprite.isHiddenDirty()) {
      if (sprite.isHidden()) {
        element.setAttribute("visibility", "hidden");
      } else {
        element.removeAttribute("visibility");
      }
    }

    // Apply clip rectangle to the sprite
    if (sprite.isClipRectangleDirty()) {
      if (sprite.getClipRectangle() != null) {
        applyClip(sprite);
      } else {
        removeClip(sprite);
      }
    }
  }

  /**
   * Applies the clipping rectangle of the given sprite
   *
   * @param sprite the sprite to apply its clipping rectangle
   */
  private void applyClip(Sprite sprite) {
    PreciseRectangle rect = sprite.getClipRectangle();
    XElement clipPath = getClipElement(sprite);

    if (clipPath != null) {
      Element clipParent = clipPath.getParentElement();
      clipParent.getParentElement().removeChild(clipParent);
    }

    Element clipElement = createSVGElement("clipPath");
    clipPath = createSVGElement("rect");
    clipElement.setId("ext-clip-" + ++clipId);
    clipPath.setAttribute("x", String.valueOf(rect.getX()));
    clipPath.setAttribute("y", String.valueOf(rect.getY()));
    clipPath.setAttribute("width", String.valueOf(rect.getWidth()));
    clipPath.setAttribute("height", String.valueOf(rect.getHeight()));
    clipElement.appendChild(clipPath);
    getDefinitions().appendChild(clipElement);
    getElement(sprite).setAttribute("clip-path", "url(#" + clipElement.getId() + ")");
    setClipElement(sprite, clipPath);
  }

  /**
   * Insert or move a given {@link Sprite}'s element to the correct place in the
   * DOM list for its zIndex.
   *
   * @param sprite - the {@link Sprite} to insert into the dom
   */
  private void applyZIndex(Sprite sprite) {
    int index = sprites.indexOf(sprite);
    int zIndex = sprite.getZIndex();
    int leftZIndex = 0;
    int rightZIndex = 0;
    if (index >= 1) {
      leftZIndex = sprites.get(index - 1).getZIndex();
    }
    if (index < sprites.size() - 1) {
      rightZIndex = sprites.get(index + 1).getZIndex();
    }
    if (leftZIndex != zIndex && rightZIndex != zIndex) {
      if (index >= 0) {
        sprites.remove(index);
      }
      index = Collections.binarySearch(sprites, sprite, zIndexComparator());
      if (index < 0) {
        index = Math.abs(index) - 1;
      }
      sprites.add(index, sprite);
    }
    XElement element = getElement(sprite);
    XElement previousElement = element.getPreviousSibling().cast();
    if (index == 0) {
      if (surfaceElement.getChildIndex(element) != 2) {
        surfaceElement.insertChild(element, 2);
      }
    } else {
      Sprite previousSprite = sprites.get(index - 1);
      XElement previousSpriteElement = getElement(previousSprite);
      if (previousSpriteElement == null) {
        previousSprite.redraw();
        previousSpriteElement = getElement(previousSprite);
      }
      if (previousElement != previousSpriteElement) {
        surfaceElement.insertAfter(element, previousSpriteElement);
      }
    }
  }

  /**
   * Generates an SVG element for the given sprite and inserts it into the DOM
   * based on its z-index.
   *
   * @param sprite the sprite to have its element generated
   * @return the generated element
   */
  private Element createSprite(Sprite sprite) {
    // Create svg element and append to the DOM.
    final XElement element;
    if (sprite instanceof CircleSprite) {
      element = createSVGElement("circle");
    } else if (sprite instanceof EllipseSprite) {
      element = createSVGElement("ellipse");
    } else if (sprite instanceof ImageSprite) {
      element = createSVGElement("image");
    } else if (sprite instanceof PathSprite) {
      element = createSVGElement("path");
    } else if (sprite instanceof RectangleSprite) {
      element = createSVGElement("rect");
    } else if (sprite instanceof TextSprite) {
      element = createSVGElement("text");
    } else {
      element = null;
    }
    setElement(sprite, element);
    element.getStyle().setProperty("webkitTapHighlightColor", "rgba(0,0,0,0)");
    applyZIndex(sprite); // performs the insertion
    return element;
  }

  /**
   * Creates an SVG element using the give type.
   *
   * @param type the type of element to create
   * @return the created SVG element
   */
  private XElement createSVGElement(String type) {
    return XElement.as(XDOM.createElementNS("http://www.w3.org/2000/svg", type));
  }

  /**
   * Returns the definitions element of the SVG element. If not already created
   * it will be instantiated.
   *
   * @return the definitions element of the SVG element
   */
  private Element getDefinitions() {
    if (defs == null) {
      defs = createSVGElement("defs");
    }
    return defs;
  }

  /**
   * Returns the {@link TextBBox} for the given text element.
   *
   * @param text the text element to get the bounding box
   * @return the bounding box
   */
  private native TextBBox getTextBBox(Element text) /*-{
                                                    return text.getBBox();
                                                    }-*/;

  /**
   * Returns the height of an SVG Text Element.
   *
   * @param text the element to have its height calculated
   * @return the height of an SVG Text Element
   */
  private native double getTextHeight(Element text) /*-{
                                                    return text.getBBox().height;
                                                    }-*/;

  /**
   * Returns the width of an SVG Text Element.
   *
   * @param text the element to have its width calculated
   * @return the width of an SVG Text Element
   */
  private native double getTextWidth(Element text) /*-{
                                                   return text.getBBox().width;
                                                   }-*/;

  /**
   * Returns the x coordinate of an SVG Text Element.
   *
   * @param text the element to have its x coordinate calculated
   * @return the x coordinate of an SVG Text Element
   */
  private native double getTextX(Element text) /*-{
                                               return text.getBBox().x;
                                               }-*/;

  /**
   * Returns the y coordinate of an SVG Text Element.
   *
   * @param text the element to have its y coordinate calculated
   * @return the y coordinate of an SVG Text Element
   */
  private native double getTextY(Element text) /*-{
                                               return text.getBBox().y;
                                               }-*/;

  private void removeClip(Sprite sprite) {
    XElement clipElement = getClipElement(sprite);
    setClipElement(sprite, null);
    getElement(sprite).removeAttribute("clip-path");
    getDefinitions().removeChild(clipElement);
  }

  private void setAttribute(XElement element, String attribute, double value) {
    if (!(Double.isNaN(value))) {
      element.setAttribute(attribute, String.valueOf(value));
    } else {
      element.removeAttribute(attribute);
    }
  }

  private void setAttribute(XElement element, String attribute, Object value) {
    if (value != null) {
      element.setAttribute(attribute, value.toString());
    } else {
      element.removeAttribute(attribute);
    }
  }

  /**
   * Divides up the text of the given {@link TextSprite}. Tspans are created
   * based on the new line character. The resultant tspans are added to the text
   * element.
   *
   * @param sprite the sprite to have its text used
   * @return the generated tspans
   */
  private List<Element> setText(TextSprite sprite) {
    List<Element> tspans = new ArrayList<Element>();
    XElement spriteElement = getElement(sprite);

    while (spriteElement.hasChildNodes()) {
      spriteElement.removeChild(spriteElement.getFirstChild());
    }

    String parentX = String.valueOf(sprite.getX());

    // Wrap each row into tspan to emulate rows
    String[] texts = sprite.getText().split("\n");
    for (int i = 0; i < texts.length; i++) {
      if (texts[i] != null) {
        Element tspan = createSVGElement("tspan");
        tspan.appendChild(XDOM.createTextNode(texts[i]));
        tspan.setAttribute("x", parentX);
        spriteElement.appendChild(tspan);
        tspans.add(tspan);
      }
    }

    return tspans;
  }

  /**
   * Applies the transformations of the given sprite to its SVG element. The
   * transformations applied include {@link Rotation}, {@link Scaling} and
   * {@link Translation}.
   *
   * @param sprite the sprite to have its transformations applied
   */
  private void transform(Sprite sprite) {
    Matrix matrix = sprite.transformMatrix();
    getElement(sprite).setAttribute(
        "transform",
        new StringBuilder().append("matrix( ").append(matrix.get(0, 0)).append(", ").append(matrix.get(1, 0)).append(
            ", ").append(matrix.get(0, 1)).append(", ").append(matrix.get(1, 1)).append(", ").append(matrix.get(0, 2)).append(
            ", ").append(matrix.get(1, 2)).append(")").toString());
  }

  /**
   * Wrap SVG text inside a tspan to allow for line wrapping. In addition this
   * normalizes the baseline for text the vertical middle of the text to be the
   * same as VML.
   *
   * @param sprite - the sprite to have its text tuned
   */
  private void tuneText(TextSprite sprite) {
    List<Element> tspans = new ArrayList<Element>();

    if (sprite.getText() != null) {
      tspans = setText(sprite);
    }

    // Normalize baseline via a DY shift of first tspan.
    if (tspans.size() > 0) {
      double height = sprite.getFontSize();
      double initialHeight = height;
      if (sprite.getTextBaseline() == TextBaseline.BOTTOM) {
        double totalHeight = height * (tspans.size() - 1) * 1.2 + height;
        initialHeight = height - (totalHeight);
      } else if (sprite.getTextBaseline() == TextBaseline.MIDDLE) {
        double totalHeight = height * (tspans.size() - 1) * 1.2 + height;
        initialHeight = height - (totalHeight / 2.0);
      }
      for (int i = 0; i < tspans.size(); i++) {
        tspans.get(i).setAttribute("dy", String.valueOf(i != 0 ? height * 1.2 : initialHeight));
      }
    }
  }

  /**
   * Generates a {@link Comparator} for use in sorting sprites by their z-index.
   *
   * @return the generated comparator
   */
  private Comparator<Sprite> zIndexComparator() {
    return new Comparator<Sprite>() {
      @Override
      public int compare(Sprite o1, Sprite o2) {
        return o1.getZIndex() - o2.getZIndex();
      }
    };
  }
}
TOP

Related Classes of com.sencha.gxt.chart.client.draw.engine.SVG

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.