Package org.mt4j.util.xml.svg

Source Code of org.mt4j.util.xml.svg.SVGLoader$SVGMesh

/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.util.xml.svg;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;
import javax.swing.JPanel;

import org.apache.batik.bridge.AbstractSVGGradientElementBridge;
import org.apache.batik.bridge.Bridge;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.BridgeException;
import org.apache.batik.bridge.CSSUtilities;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.PaintServer;
import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.bridge.SVGUtilities;
import org.apache.batik.bridge.TextUtilities;
import org.apache.batik.bridge.UnitProcessor;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.bridge.AbstractSVGGradientElementBridge.SVGStopElementBridge;
import org.apache.batik.bridge.AbstractSVGGradientElementBridge.Stop;
import org.apache.batik.css.engine.SVGCSSEngine;
import org.apache.batik.css.engine.value.ListValue;
import org.apache.batik.css.engine.value.Value;
import org.apache.batik.css.engine.value.svg.ICCColor;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGGraphicsElement;
import org.apache.batik.dom.svg.SVGOMCircleElement;
import org.apache.batik.dom.svg.SVGOMClipPathElement;
import org.apache.batik.dom.svg.SVGOMDefsElement;
import org.apache.batik.dom.svg.SVGOMEllipseElement;
import org.apache.batik.dom.svg.SVGOMForeignObjectElement;
import org.apache.batik.dom.svg.SVGOMGElement;
import org.apache.batik.dom.svg.SVGOMLineElement;
import org.apache.batik.dom.svg.SVGOMLinearGradientElement;
import org.apache.batik.dom.svg.SVGOMMaskElement;
import org.apache.batik.dom.svg.SVGOMPathElement;
import org.apache.batik.dom.svg.SVGOMPolygonElement;
import org.apache.batik.dom.svg.SVGOMPolylineElement;
import org.apache.batik.dom.svg.SVGOMRadialGradientElement;
import org.apache.batik.dom.svg.SVGOMRectElement;
import org.apache.batik.dom.svg.SVGOMSVGElement;
import org.apache.batik.dom.svg.SVGOMSwitchElement;
import org.apache.batik.dom.svg.SVGOMTSpanElement;
import org.apache.batik.dom.svg.SVGOMTextElement;
import org.apache.batik.dom.svg.SVGOMToBeImplementedElement;
import org.apache.batik.dom.svg.SVGTextContentSupport;
import org.apache.batik.dom.svg.SVGURIReferenceGraphicsElement;
import org.apache.batik.dom.svg12.BindableElement;
import org.apache.batik.dom.svg12.SVGOMFlowRootElement;
import org.apache.batik.dom.util.XLinkSupport;
import org.apache.batik.ext.awt.MultipleGradientPaint;
import org.apache.batik.ext.awt.MultipleGradientPaint.CycleMethodEnum;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.TextNode;
import org.apache.batik.gvt.font.AWTGVTFont;
import org.apache.batik.gvt.font.GVTFont;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.text.TextPaintInfo;
import org.apache.batik.parser.PathParser;
import org.apache.batik.parser.TransformListParser;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.util.XMLResourceDescriptor;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.mt4j.MTApplication;
import org.mt4j.components.MTComponent;
import org.mt4j.components.TransformSpace;
import org.mt4j.components.bounds.BoundsZPlaneRectangle;
import org.mt4j.components.bounds.IBoundingShape;
import org.mt4j.components.clipping.FillPaint;
import org.mt4j.components.visibleComponents.AbstractVisibleComponent;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.components.visibleComponents.font.FontManager;
import org.mt4j.components.visibleComponents.font.IFont;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.components.visibleComponents.shapes.MTEllipse;
import org.mt4j.components.visibleComponents.shapes.MTLine;
import org.mt4j.components.visibleComponents.shapes.MTPolygon;
import org.mt4j.components.visibleComponents.shapes.MTRectangle;
import org.mt4j.components.visibleComponents.shapes.MTRoundRectangle;
import org.mt4j.components.visibleComponents.shapes.MTStencilPolygon;
import org.mt4j.components.visibleComponents.shapes.MTRectangle.PositionAnchor;
import org.mt4j.components.visibleComponents.shapes.mesh.MTTriangleMesh;
import org.mt4j.components.visibleComponents.widgets.MTTextArea;
import org.mt4j.input.gestureAction.DefaultDragAction;
import org.mt4j.input.gestureAction.DefaultRotateAction;
import org.mt4j.input.gestureAction.DefaultScaleAction;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
import org.mt4j.util.HelperMethods;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.SwingTextureRenderer;
import org.mt4j.util.math.ConvexityUtil;
import org.mt4j.util.math.Matrix;
import org.mt4j.util.math.ToolsGeometry;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GluTrianglulator;
import org.mt4j.util.opengl.GLTexture;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSStyleDeclaration;
import org.w3c.dom.css.CSSValue;
import org.w3c.dom.svg.SVGAnimatedLength;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGElement;
import org.w3c.dom.svg.SVGLength;
import org.w3c.dom.svg.SVGLengthList;
import org.w3c.dom.svg.SVGMatrix;
import org.w3c.dom.svg.SVGPoint;
import org.w3c.dom.svg.SVGPointList;
import org.w3c.dom.svg.SVGSVGElement;

import processing.core.PApplet;
import processing.opengl.PGraphicsOpenGL;


/**
* This class can be used to load and display scalable vector graphics (svg) files.
*
* @author Christopher Ruff
*/
public class SVGLoader implements SVGConstants{
  private static final Logger logger = Logger.getLogger(SVGLoader.class.getName());
  static{
    logger.setLevel(Level.ERROR);
    SimpleLayout l = new SimpleLayout();
    ConsoleAppender ca = new ConsoleAppender(l);
    logger.addAppender(ca);
  }
 
  /** The svg doc. */
  private SVGDocument    svgDoc;
 
  /** The user agent. */
  private UserAgent      userAgent;
 
  /** The loader. */
  private DocumentLoader loader;
 
  /** The ctx. */
  private BridgeContext  ctx;
 
  /** The builder. */
  private GVTBuilder     builder;
 
  /** The root gn. */
  private GraphicsNode   rootGN;
 
  /** The css engine. */
  protected SVGCSSEngine cssEngine;
 
  /** The pa. */
  private PApplet pa;
 
  /** The opacity stack. */
  private Stack<Float> opacityStack;
 
  /** The default drag action. */
  private IGestureEventListener defaultDragAction;
 
  /** The default rotate action. */
  private IGestureEventListener defaultRotateAction;
 
  /** The default scale action. */
  private IGestureEventListener defaultScaleAction;
 
  /** The current local transform matrix. */
  private Matrix currentLocalTransformMatrix;

 
 
  /**
   * Instantiates a new batik svg parser.
   *
   * @param pa the pa
   */
  public SVGLoader(PApplet pa){
    this.pa = pa;
   
    opacityStack = new Stack<Float>();
   
    currentLocalTransformMatrix = new Matrix();
   
    defaultDragAction     = new DefaultDragAction();
    defaultRotateAction   = new DefaultRotateAction();
    defaultScaleAction     = new DefaultScaleAction();
  }
 
 
  /**
   * Loads a "*.svg" file, parses it, creates drawable components and returns the
   * toplevel component.
   *
   * @param filedescr the absolute path of the svg file as a string
   *
   * @return the MT base component
   *
   * the created top level component of the svg
   */
  public MTComponent loadSvg(String filedescr){
    return this.getCreatedSvgComponents(this.parseSvg(filedescr));
  }
 
 
  /**
   * Uses the batik parser to genererate an svg document from an svg file.
   * To create the components in that svg document, call <code>getCreatedSvgComponents(SVGDocument doc)</code>
   *
   * @param filedescr the filedescr
   *
   * @return the SVG document
   */
  public SVGDocument parseSvg(String filedescr){
        Document doc;
        try {
            String parser = XMLResourceDescriptor.getXMLParserClassName();
            SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
           
            File file = new File(filedescr);
            if (file.exists()){
                URI localFileAsUri = file.toURI();
                String uri = localFileAsUri.toASCIIString();
                doc = f.createDocument(uri);
            }else{
              InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filedescr);
              if (in == null){
                in = pa.getClass().getResourceAsStream(filedescr);
              }
              doc = f.createDocument(filedescr, in);
             
               /*on it (after casting it to  SVGOMDocument) to give it a
                URI of some sort.  If the document needs to be able to have relative
                reference to files on the local file system, give it a URI like
                "file:///some/where/file.svg";
                */
                //FIXME HACK! this seems to help the "org.apache.batik.bridge.BridgeException: Unable to make sense of URL for connection" error
                //occuring with windmill.svg if loading from inputstream instead of local file system file
                //FIXME but this might create errors when loading external file like images from the relative svg path?
              ((SVGDocument)doc).setDocumentURI("") ;
//                String sub = filedescr.substring(0, filedescr.lastIndexOf(MTApplication.separator));
//                System.out.println("F: " + filedescr + " sub; " + sub);
//                svgDoc.setDocumentURI(sub+ MTApplication.separator) ;
            }
            svgDoc = (SVGDocument)doc;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
         
        //Neccesary? For booting css
        try{
          userAgent = new UserAgentAdapter();
          loader    = new DocumentLoader(userAgent);
          ctx       = new BridgeContext(userAgent, loader);
          ctx.setDynamicState(BridgeContext.DYNAMIC); //TODO use static?
          builder   = new GVTBuilder();
          rootGN    = builder.build(ctx, svgDoc);
         
//          ctx.getCSSEngineForElement(null).
        }catch(Exception e){
          e.printStackTrace();
        }
        return svgDoc;
  }
 
  /**
   * Creates and returns components of the provided svg document for displaying.
   *
   * @param svgDoc the svg doc
   *
   * @return the created svg components
   */
  public MTComponent getCreatedSvgComponents(SVGDocument svgDoc){
    ArrayList<MTComponent> components = new ArrayList<MTComponent>();
    opacityStack.push(1.0f);
    traverseSVGDoc(svgDoc, components);
    opacityStack.pop();
   
    MTComponent[] comps = (MTComponent[])components.toArray(new MTComponent[components.size()]);
    //Only returning the 1st component, since this should be the top-level <svg> element and only 1!?
    return comps[0];
  }
 
 
 
  /**
   * Traverse svg doc.
   *
   * @param node the node
   * @param comps the comps
   */
  private void traverseSVGDoc(Node node, ArrayList<MTComponent> comps){
    logger.debug("Traversing: " + node.getNodeName());
   
    //Damit transformationen konsistent sind muss
    //jedes tag, da� eine transform attribut hat
    //behandelt werden!
    //Default
    currentLocalTransformMatrix = new Matrix();
   
    //If there is a TRANSFORM attribute parse that and set the
    //current transformation matrix to be used with the svg components created after
    NamedNodeMap atts = node.getAttributes();
    if (atts != null){
      for (int i = 0; i < atts.getLength(); i++) {
        Node att = atts.item(i);
        if (att.getNodeName().equals(SVG_TRANSFORM_ATTRIBUTE)){
          CustomTransformHandler transFormHandler = new CustomTransformHandler();
          TransformListParser transFormListParser = new TransformListParser();
          transFormListParser.setTransformListHandler(transFormHandler);
          transFormListParser.parse(att.getNodeValue());
          //Overwrite current default matrix if the element has its own
          //transform defined, will be used at gfx obj creation
          currentLocalTransformMatrix = transFormHandler.getResultMatrix();
        }
      }
    }
   
//    logger.debug("Node: " + node.getNodeName() + " Class: " + node.getClass());
   
   
    //For opacity inheritance
      if (node instanceof SVGGraphicsElement){
        SVGGraphicsElement svgGfx = (SVGGraphicsElement)node;
        //Handle inherited opacity settings
        float opac = queryPrimitiveFloatValue(svgGfx, "opacity", 1f);
        opacityStack.push(opac *= opacityStack.peek().floatValue());
      }

      // if G (GROUP) element, add all children to this element
      if node instanceof SVGOMGElement
       || node instanceof SVGSVGElement
       || node instanceof SVGOMSVGElement
      ){
//        SVGOMGElement gElem = (SVGOMGElement)node;
        SVGElement gElem = (SVGElement)node;
        MTComponent group = new MTComponent(pa);
        group.setName(gElem.getTagName());

//        Element viewPort = gElem.getViewportElement();
//        logger.debug("Viewport " + viewPort.getNodeName());

        //Set the <g> group to composite, meaning that it will
        //be returned at picking, when one of the children gets picked
        group.setComposite(true);

        group.setLocalMatrix(currentLocalTransformMatrix);
       
        //IF its <svg> element get the transform
        //(to honor the viewBox and the width/height attributes
        if (node instanceof SVGOMSVGElement ){
          SVGOMSVGElement svgGom = ((SVGOMSVGElement)node);
          Element viewPort = svgGom.getViewportElement();
          if (viewPort != null)
            logger.debug("Viewport " + viewPort.getNodeName());
         
//          SVGMatrix mat = svgGom.getScreenCTM();
         
          SVGAnimatedLength widthA = svgGom.getWidth();
          SVGAnimatedLength heightA = svgGom.getHeight();
         
          SVGLength w = widthA.getBaseVal();
          float width = w.getValue();
         
          SVGLength h = heightA.getBaseVal();
          float height = h.getValue();
         
          logger.debug("-> SVG Width: " + width + " Height: " + height);
         
         
          SVGMatrix mat = svgGom.getCTM();
          /*
          logger.debug("mat: " + mat.toString());
          logger.debug(mat.getA());
          logger.debug(mat.getB());
          logger.debug(mat.getC());
          logger.debug(mat.getD());
          logger.debug(mat.getE());
          logger.debug(mat.getF());
          SVGRect bbox = svgGom.getBBox();
          logger.debug("BBOx: X:" + bbox.getX() + " Y:" + bbox.getY() + " Width:" + bbox.getWidth() + " Height:" + bbox.getHeight());
          */
         
          //Hack, because if no width/height is specified default of 1.0
          //is assumed by batik -> things may get scaled too small
          if ( !(width == 1 && height == 1) ){
            currentLocalTransformMatrix = new Matrix(mat.getA(), mat.getC(),   0, mat.getE(),
                                   mat.getB(), mat.getD(),   0, mat.getF(),
                                   0,       0,       1,       0,
                                   0,       0,       0,       1
            );
            //logger.debug("Matrix: " + currentLocalTransformMatrix);
            group.setLocalMatrix(currentLocalTransformMatrix);
          }
        }

        //Make the group pickable and manipulatable
        group.setPickable(true);
       
        group.registerInputProcessor(new DragProcessor(pa));
        group.setGestureAllowance(DragProcessor.class, true);
        group.addGestureListener(DragProcessor.class, (IGestureEventListener)defaultDragAction);
       
        group.registerInputProcessor(new RotateProcessor(pa));
        group.addGestureListener(RotateProcessor.class, defaultRotateAction);
       
        group.registerInputProcessor(new ScaleProcessor(pa));
        group.addGestureListener(ScaleProcessor.class,  defaultScaleAction);
       
        ArrayList<MTComponent> groupChildren = new ArrayList<MTComponent>();
        //Traverse the children and add them to a new arraylist
        traverseChildren(gElem, groupChildren);
       
        MTComponent[] childComps = (MTComponent[])groupChildren.toArray(new MTComponent[groupChildren.size()]);
        //Add the children to the group
        group.addChildren(childComps);
        //Add the group to the arraylist of the parent
        comps.add(group);
      }else{//If NOT GROUP
        if (node instanceof SVGGraphicsElement){
          SVGGraphicsElement svgGfxElem = (SVGGraphicsElement)node;
          //IF node isnt a group node just add it to the passed in comps arraylist
          try{
            //Create a component from the graphicsnode and add it to the parents arraylist
            MTComponent liveComponent = handleGraphicsNode(svgGfxElem);
            if (liveComponent != null){
              comps.add(liveComponent);
            }
          }catch(Exception e){
            logger.error("Error handling svg node: " + svgGfxElem.getTagName());
            e.printStackTrace();
          }
        }

        //FIXME IMPLEMENT
        if (node instanceof SVGOMTSpanElement){
          SVGOMTSpanElement tSpanElement = (SVGOMTSpanElement)node;
         
        }

        //FIXME TEST
        if (node instanceof SVGOMTextElement){
          boolean useVectorFont = false;
         
          SVGOMTextElement textElement = (SVGOMTextElement)node;
          //Get <text> position values (can be a list)
          List<Float> xValues = getSVGLengthListAsFloat(textElement.getX().getBaseVal());
          List<Float> yValues = getSVGLengthListAsFloat(textElement.getY().getBaseVal());
//          /*//Not used
          String textContent = TextUtilities.getElementContent(textElement);
          textContent = textContent.replaceAll("\\n","");
          textContent = textContent.trim();
//           */
          /*
          //TODO USE?
          textElement.getTextLength();
          textElement.getRotate();
           */
          if (textElement.getSVGContext() instanceof SVGTextElementBridge){
            SVGTextElementBridge b = (SVGTextElementBridge)textElement.getSVGContext();
            GraphicsNode gr = b.createGraphicsNode(ctx, textElement);
            TextNode tNode = (TextNode)gr;
            b.buildGraphicsNode(ctx, textElement, tNode);
            List<?> textRuns = tNode.getTextRuns();
            logger.debug("Text runs: " + textRuns);
            //Get font size
            float fontSize = b.getFontSize();

            logger.debug("Text:" " x:" + xValues.get(0) + " y:" + yValues.get(0) + " FontSize: " + fontSize +  " Text: '" + textContent + "'");

            //Get font FILL
            Value fillOpacValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.FILL_OPACITY_INDEX);
            float computedfillOpac = PaintServer.convertOpacity(fillOpacValue);
            Value fillIndexValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.FILL_INDEX);
            Object fill = SVGLoader.getFillOrStroke(textElement, fillIndexValue, computedfillOpac, ctx);
            MTColor fillColor = new MTColor(150,150,150,255);
            if (fill instanceof java.awt.Color) {
              java.awt.Color color = (Color) fill;
              fillColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
            }

            //Get STROKE
            // Stroke Opacity \\
            Value strokeOpacValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.STROKE_OPACITY_INDEX);
            float computedStrokeOpacity = PaintServer.convertOpacity(strokeOpacValue);
            // Stroke java.awt.Color \\
            Value strokeIndexValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.STROKE_INDEX);
            Object stroke = SVGLoader.getFillOrStroke(textElement, strokeIndexValue, computedStrokeOpacity, ctx);
            MTColor strokeColor = new MTColor(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
            if (stroke instanceof java.awt.Color) {
              java.awt.Color color = (Color) stroke;
              strokeColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
            }

            //Get the font family
            Value fontFamilyValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.FONT_FAMILY_INDEX);
            String fontFamily = "arial"; //DEFAULT
            if (fontFamilyValue instanceof ListValue) {
              ListValue listValue = (ListValue) fontFamilyValue;
              Value firstValue = listValue.item(0); //Can be a List? -> take only the first one..
              if (firstValue != null)
                fontFamily = firstValue.getStringValue();
            }
            logger.debug("Font family: " + fontFamily);

            IFont font;
            if (useVectorFont)
              //Vector font
              font = FontManager.getInstance().createFont(pa,
                  "arial.ttf", Math.round(fontSize), fillColor, strokeColor);
            else
              //Bitmap font
              font = FontManager.getInstance().createFont(pa,
//                  "Arial", Math.round(fontSize),
                  fontFamily, Math.round(fontSize), fillColor, strokeColor);
//            /*

            IFont fontToUse = font;
            IFont lastUsedFont = fontToUse;
            List<MTTextArea> textAreas = new ArrayList<MTTextArea>();

            AttributedCharacterIterator iter = tNode.getAttributedCharacterIterator();
            if (font != null && iter != null){ //To avoid not loaded fonts or if text ist empty
              for (int i = iter.getBeginIndex(); i < iter.getEndIndex(); i++) {
                char currentChar = iter.setIndex(i);
                Set<Attribute> keys = iter.getAllAttributeKeys();
                Map<Attribute, Object> charAtts = iter.getAttributes();
               
                Object baseLineShift = charAtts.get(SVGTextElementBridge.BASELINE_SHIFT);
                Object paintInfo = charAtts.get(SVGTextElementBridge.PAINT_INFO);

                Object charX = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.X);
                Object charY = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.Y);
                Object charDX = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.DX);
                Object charDY = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.DY);
                Object charRotation = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.ROTATION);
                Object gvtFont = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
                Object gvtFonts = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.GVT_FONTS);
                Object gvtFontFamilies = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
                Object textCompoundDelimiter = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
                Object verticalOrientation = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
                logger.debug("Character: "  + currentChar + " CharX:" + charX + " CharY: " + charY + " CharDX: " +  charDX + " CharDY: " + charDY +  " Font: " + gvtFont +  " Fonts: " + gvtFonts +   " FontFamilies: " + gvtFontFamilies);
                AWTGVTFont awtGvtFont = (AWTGVTFont)gvtFont;
                if (awtGvtFont != null)
                  logger.debug("CharfontSize: " + awtGvtFont.getSize());

                //FIXME REMOVE, Not working always 0,0
                SVGPoint startPosOfChar = SVGTextContentSupport.getStartPositionOfChar(textElement, i);

                /////////////////////////////////////
                //Get the character information - font, colors
                String newFamilyName = new String(fontFamily);
                float newFontSize = fontSize;
                MTColor newFillColor = new MTColor(fillColor);
                MTColor newStrokeColor = new MTColor(strokeColor);
                boolean charHasColorInfo = false;
                boolean charHasFontInfo = false;
                //Get chars paint info
                if (paintInfo != null && paintInfo instanceof TextPaintInfo){
                  charHasColorInfo = true;
                  TextPaintInfo texInfo = (TextPaintInfo)paintInfo;
                  if (texInfo.fillPaint instanceof java.awt.Color){
                    java.awt.Color color = (Color)texInfo.fillPaint;
                    newFillColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
                  }
                  if (texInfo.strokePaint instanceof java.awt.Color){
                    java.awt.Color color = (Color)texInfo.strokePaint;
                    newStrokeColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
                  }
                }

                //Get the chars font family and size
                GVTFont aGvtFont = null;
                if (gvtFonts!=null){
                  if (gvtFonts instanceof List) {
                    List<?> fonts = (List<?>) gvtFonts;
                    for (Iterator<?> iterator = fonts.iterator(); iterator.hasNext();) {
                      Object o = (Object) iterator.next();
                      if (o instanceof GVTFont) {
                        aGvtFont = (GVTFont) o;
                        //logger.debug("Char font family: " + aGvtFont.getFamilyName() + " Size:" + aGvtFont.getSize());
                      }
                    }
                  }
                }
                if (aGvtFont != null){
                  charHasFontInfo = true;
                  newFamilyName = aGvtFont.getFamilyName();
                  newFontSize = aGvtFont.getSize();
                }else{
                  logger.error("Character: " + currentChar + " has no font attached.");
                }

                if (charHasColorInfo && charHasFontInfo){
                  logger.debug("Character '" + currentChar + "'-> has font info -> load font!" +
                      " Family: " + newFamilyName +
                      " Fontsize: " + Math.round(newFontSize) +
                      " FillColor: " + newFillColor +
                      " StrokeColor: " + newStrokeColor);
                 
                  if (useVectorFont)
                    fontToUse = FontManager.getInstance().createFont(pa,
                        "arial.ttf", Math.round(newFontSize), newFillColor, newStrokeColor);
                  else
                    fontToUse = FontManager.getInstance().createFont(pa, //uses cached font if available
//                        "Arial", Math.round(fontSize),
                        newFamilyName, Math.round(newFontSize), newFillColor, newStrokeColor);
                  if (fontToUse == null){
                    fontToUse = font;
                  }
                }else{
                  fontToUse = font;
                }
                boolean fontChanged = !FontManager.isFontsAreEqual(fontToUse, lastUsedFont);
                lastUsedFont = fontToUse;

//                //FIXME REMOVE TEST
//                fontChanged = true;
                ///////////////////////////////////////
                boolean textPositionChanged = charX != null || charY != null  || charDX != null || charDY != null;

                //TODO if we forceAnewTextarea because of font change but ther is NO NEW POSITION, we
                //have to set the textareas anchor to the lower left
                //TODO problem if we have a tspan centered and a next tspan without new position
                //-> the first tspan textarea gets centered on the position
                //but we would have to treat them (all in the same line) as 1 textarea when center positioning!
               
                //FIXME there are slight differences because we use a different SPACE character length and no font KERNING!
               
                //FIXME DO WIDHTOUT USERDATA
                //FIXME bitmap font has no top border, vector has.. why?
                //TODO -> eventuell doch in handleSvgNode machen?
                //-> statt graphicsnode /stylable node �bergeben? - SVGOMTextElement is nicht instanceof graphicsnode..
               
                // we have to check font/color etc at every character, not only at new positon because
                //pos doesent change at tspans without new posinfo
                //check if equal to last used font and if equal original text font
                if ( fontChanged || textPositionChanged         
                ){ //Make a new textarea if the text position changed or if the font changed at the current character
                  MTTextArea previousTextArea = null;
                  if (!textAreas.isEmpty()){
                    previousTextArea = textAreas.get(textAreas.size()-1);
                  }

                  float newXPos = 0;
                  float newYPos = 0 ;

                  //If there is a previous text, get its ending coordinates
                  //for the DX and DY shift info for the next text area
                  if (previousTextArea != null){
                    PositionAnchor oldAnchor = previousTextArea.getAnchor();
//                    previousTextArea.setAnchor(PositionAnchor.LOWER_RIGHT);
                    previousTextArea.setAnchor(PositionAnchor.UPPER_LEFT);
                    //Calculate last/current textposition for DX and DY use
                    //add up the last textareas start position end position(width)
                    Vector3D lastPos = previousTextArea.getPosition(TransformSpace.LOCAL);
//                    lastPos.addLocal(new Vector3D(previousTextArea.getWidthXY(TransformSpace.LOCAL) - 1 * previousTextArea.getInnerPaddingLeft(),0));
                    lastPos.addLocal(new Vector3D(previousTextArea.getWidthXY(TransformSpace.LOCAL) - 2 * previousTextArea.getInnerPaddingLeft(),0));
//                    newXPos = lastPos.x - previousTextArea.getInnerPaddingLeft();
                    newXPos = lastPos.x;
                    newXPos += (Float)previousTextArea.getUserData("XPos");

                    newYPos = lastPos.y;
//                    newYPos -= previousTextArea.getInnerPaddingTop();
//                    newYPos += fontToUse.getFontMaxDescent(); //FIXME WHY NEVESSARY?
                    newYPos += (Float)previousTextArea.getUserData("YPos");
                    previousTextArea.setAnchor(oldAnchor);
                  }

                  //IF absolute x or y is present overwrite the position values from the last textarea
                  if (charX != null)
                    newXPos = (Float)charX;
                  if (charY != null)
                    newYPos = (Float)charY;
                  if (charDX != null)
                    newXPos += (Float)charDX;
                  if (charDY != null)
                    newYPos += (Float)charDY;
                 
                  // Create the text area \\
                  MTTextArea t = new MTTextArea(pa, fontToUse);
                  t.setNoFill(true);
                  t.setNoStroke(true);
                  textAreas.add(t);
                  try{
                    t.setLocalMatrix(new Matrix(currentLocalTransformMatrix));
                  }catch(Exception e){
                    logger.error(e.getMessage());
                  }

                  //FIXME TEST
//                  if (previousTextArea != null && !textPositionChange){
//                  t.setAnchor(PositionAnchor.LOWER_LEFT);
//                  t.setUserData("posRelParent", new Vector3D(newXPos , newYPos - fontToUse.getFontMaxDescent() , 0));
//                  logger.debug("Character '" + currentChar + "' -> Anchor: LOWER_LEFT");
//                  }else{
                  Value v = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.TEXT_ANCHOR_INDEX);
                  //INFO: we have to move the BASELINE of the text to the svg position
                  //The textarea is usually fontmaxascent+fontmaxdescent+2*innerPadding big!
                  switch (v.getStringValue().charAt(0)) {
                  case 'e':
                    t.setAnchor(PositionAnchor.LOWER_RIGHT);
                    t.setUserData("posRelParent", new Vector3D((newXPos + t.getInnerPaddingLeft()) , newYPos - fontToUse.getFontMaxDescent()  +  t.getInnerPaddingTop() , 0));
//                    t.setPositionRelativeToParent(new Vector3D(newXPos, newYPos - font.getFontMaxDescent() , 0));
                    logger.debug("Character '" + currentChar + "' -> Anchor: LOWER_RIGHT");
                    break;
                  case 'm': //text-anchor="middle"
                    t.setAnchor(PositionAnchor.CENTER);
//                    t.setUserData("posRelParent", new Vector3D(newXPos, newYPos - fontToUse.getFontMaxAscent()*0.5f - fontToUse.getFontMaxDescent()*0.5f , 0));
//                    t.setUserData("posRelParent", new Vector3D(newXPos, newYPos - fontToUse.getFontAbsoluteHeight()*0.5f + t.getInnerPaddingTop() , 0));
//                    t.setPositionRelativeToParent(new Vector3D(newXPos, newYPos - font.getFontMaxAscent()*0.5f - font.getFontMaxDescent()*0.5f, 0)); //- font.getFontMaxAscent()*0.5f
                    logger.debug("Character '" + currentChar + "' -> Anchor: CENTER");
                    t.setUserData("posRelParent", new Vector3D((newXPos), (newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop()) - t.getHeightXY(TransformSpace.LOCAL)/2f , 0));
                    break;
                  default: //text-anchor="start" //default!
                    t.setAnchor(PositionAnchor.LOWER_LEFT);
//                    t.setUserData("posRelParent", new Vector3D(newXPos -t.getInnerPaddingLeft(), newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop() , 0));
                    t.setUserData("posRelParent", new Vector3D(newXPos -t.getInnerPaddingLeft(), newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop() , 0));
                   
//                    t.setAnchor(PositionAnchor.UPPER_LEFT);
//                    t.setUserData("posRelParent", new Vector3D(newXPos -t.getInnerPaddingLeft(), newYPos, 0));
//                  t.setPositionRelativeToParent(new Vector3D(newXPos, newYPos - font.getFontMaxDescent() , 0));
                  logger.debug("Character '" + currentChar + "' -> Anchor: LOWER_LEFT");
                  }
                  t.setUserData("XPos", newXPos);
                  t.setUserData("YPos", newYPos);
//                  }
                }
                //Add character to the current textarea in the list
                if (!textAreas.isEmpty()){
                  textAreas.get(textAreas.size()-1).appendCharByUnicode(new Character(currentChar).toString());             
                }
              }
              //Set the positions of the textareas
              for (Iterator<MTTextArea> iterator = textAreas.iterator(); iterator.hasNext();) {
                MTTextArea textArea = (MTTextArea) iterator.next();
                logger.debug("Adding text area at: " + (Vector3D) textArea.getUserData("posRelParent"));
                textArea.setPositionRelativeToParent((Vector3D) textArea.getUserData("posRelParent"));
              }
              comps.addAll(textAreas);
            }

            /*
            //This gets only the text of this hierarchy level
            StringBuffer result = new StringBuffer();
            for (Node n = textElement.getFirstChild();
            n != null;
            n = n.getNextSibling()) {
              switch (n.getNodeType()) {
              case Node.ELEMENT_NODE:
                break;
              case Node.CDATA_SECTION_NODE:
              case Node.TEXT_NODE:
                result.append(n.getNodeValue());
              }
            }
            logger.debug("TEXTTTT2: " + result);
             */
//            */////////////////////

          }
        }
      }


    if (node instanceof SVGGraphicsElement){
        //Remove inherited opacity attribute from stack
        opacityStack.pop();
    }
   
    //Traverse the children, not if it was a group element
    //because then the children are already
    //traversed in the if (group) block above
    if (   !(node instanceof SVGOMGElement)
      && !(node instanceof SVGSVGElement)
      && !(node instanceof SVGOMSVGElement)
    ){
      traverseChildren(node, comps);
    }
  }
 
 
 
  /**
   * Traverse children.
   *
   * @param node the node
   * @param comps the comps
   */
  private void traverseChildren(Node node, ArrayList<MTComponent> comps){
       
    //Check the children
    NodeList nl = node.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
      Node currentNode = nl.item(i);
      traverseSVGDoc(currentNode, comps);
    }
  }
 
 
  /**
   * Handle graphics node.
   *
   * @param gfxElem the gfx elem
   *
   * @return the mT base component
   */
  private MTComponent handleGraphicsNode(SVGGraphicsElement gfxElem){
      MTComponent returnComp = null;
//      logger.debug("Handle Element: " + gfxElem.getTagName());
     
      //Print all css properties and values
//      logger.debug("Style Css Text: " + style.getCssText());
     
      // SVG Defaults \\
      float fillR       = 255;
      float fillG       = 255;
      float fillB       = 255;
      boolean noFill     = false;
      float strokeR     = 0;
      float strokeG     = 0;
      float strokeB     = 0;
      float strokeWidth   = 1.0f;
      boolean noStroke     = false;
      float strokeOpacity   = 1;
      float fillOpacity     = 1;
      int windingRule     = GLU.GLU_TESS_WINDING_NONZERO;
      // SVG Defaults \\
     
     
      // Opacity, not as a style attribute but a separate
      // as group opacity doesnt get computed right, so we
      // mannually track it on a stack
      float opacity = opacityStack.peek().floatValue();
      //logger.debug("INHERITED OPACITY: " + opacity);
     
     
      // FILL-RULE \\
      Value fillRuleValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.FILL_RULE_INDEX);
      String fillRule = fillRuleValue.getStringValue();
      if (fillRule.equalsIgnoreCase("nonzero")){
        windingRule = GLU.GLU_TESS_WINDING_NONZERO;
      }else if (fillRule.equalsIgnoreCase("evenodd")){
        windingRule = GLU.GLU_TESS_WINDING_ODD;
      }else{
        windingRule = GLU.GLU_TESS_WINDING_NONZERO;
      }
      //logger.debug("fillRule: " + fillRule);
     
     
      // Fill Opacity \\
      Value fillOpacValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.FILL_OPACITY_INDEX);
      float computedfillOpac = PaintServer.convertOpacity(fillOpacValue);
      fillOpacity = computedfillOpac;
      //Multiplicate inherited opacity with this components opacities
      fillOpacity   *= opacity;
      //Save for eventual lineargradient creation later that needs the not interpolated value
      float originalFillOpacity = fillOpacity;
      //logger.debug("fill opacity unnormalized: " + fillOpacity);
     
     
      // Fill java.awt.Color \\
      Value fillIndexValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.FILL_INDEX);
      Object fill = SVGLoader.getFillOrStroke(gfxElem, fillIndexValue, fillOpacity, ctx);
      SVGOMLinearGradientElement linearGradient = null;
      SVGOMRadialGradientElement radialGradient = null;
      if (fill instanceof java.awt.Color) {
        java.awt.Color color = (Color) fill;
        fillR = color.getRed();
        fillG = color.getGreen();
        fillB = color.getBlue();
        fillOpacity = color.getAlpha();
        noFill = false;
        //logger.debug("Fill: " + color +  " a=" + fillOpacity);
      }else if (fill instanceof SVGOMLinearGradientElement) {
            //TODO cache gradients so dass man nicht immer neu den gleichen
            //machen muss!
        linearGradient = (SVGOMLinearGradientElement) fill;
        noFill = false;
      }else if (fill instanceof SVGOMRadialGradientElement) {
      //TODO!! //FIXME TEST
        radialGradient = (SVGOMRadialGradientElement)fill;
        noFill = false;
      }else{
        noFill = true;
      }
     
     
      // Stroke Opacity \\
      Value strokeOpacValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.STROKE_OPACITY_INDEX);
      float computedStrokeOpacity = PaintServer.convertOpacity(strokeOpacValue);
      strokeOpacity = computedStrokeOpacity;
      // Multiplicate inherited opacity with this components group opacities
      strokeOpacity *= opacity;
     
     
      // Stroke java.awt.Color \\
      Value strokeIndexValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.STROKE_INDEX);
      Object stroke = SVGLoader.getFillOrStroke(gfxElem, strokeIndexValue, strokeOpacity, ctx);
      if (stroke instanceof java.awt.Color) {
        java.awt.Color color = (Color) stroke;
        strokeR = color.getRed();
        strokeG = color.getGreen();
        strokeB = color.getBlue();
        strokeOpacity = color.getAlpha();
        noStroke = false;
      }else{
        noStroke = true;
        strokeR = fillR;
        strokeG = fillG;
        strokeB = fillB;
      }
     
     
      // Stroke Width \\
      Stroke s = PaintServer.convertStroke(gfxElem);
      if (s != null){
        if (s instanceof BasicStroke) {
          BasicStroke basicStroke = (BasicStroke) s;
          strokeWidth = basicStroke.getLineWidth();
        }
      }else{
        strokeWidth = 0.0f;
        noStroke = true;
      }
     
     /*
     logger.debug("Fill COL: " + fillR + " " + fillG + " " + fillB + " " fillopacity);
     logger.debug("STROKE COL: " + strokeR + " " + strokeG + " " + strokeB + " " strokeOpacity);
     */
     
      // CHECK WHAT KIND OF GRAPHICS ELEMENT IT IS AND CREATE IT \\
      if (gfxElem instanceof SVGOMPathElement){
        SVGOMPathElement pathElem = (SVGOMPathElement)gfxElem;
       
        //FIXME handle clip-paths in the future
        if (isUnderClipPath(pathElem)){
          logger.error("Discarding clip-path path element. Not implemented.");
          return null;
        }
       
        //Create the shape
        AbstractShape pathComp = getLivePathComponent(pathElem, noFill, windingRule);
       
        try{
          pathComp.setLocalMatrix(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
        returnComp = pathComp;
      }else if (gfxElem instanceof SVGOMPolygonElement){
        SVGOMPolygonElement polygonElem = (SVGOMPolygonElement)gfxElem;
       
        //Create the shape
        AbstractShape comp = getLivePolygonComponent(polygonElem, noFill, windingRule);
               
        try{
          comp.setLocalMatrix(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
        returnComp = comp;
      }else if (gfxElem instanceof SVGOMPolylineElement){
        SVGOMPolylineElement polyLineElem = (SVGOMPolylineElement)gfxElem;
       
        //Create Vertex[] from points
        SVGPointList pointList = polyLineElem.getPoints();
        Vertex[] vertices = new Vertex[pointList.getNumberOfItems()];
        for (int i = 0; i < pointList.getNumberOfItems(); i++) {
        SVGPoint p = pointList.getItem(i);
        vertices[i] = new Vertex(p.getX(), p.getY(),0);
        }
       
        //Create the shape
        AbstractShape comp = createPoly(vertices);
       
        try{
          comp.setLocalMatrix(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
        returnComp = comp;
      }else if (gfxElem instanceof SVGOMRectElement){
        SVGOMRectElement rectElem = (SVGOMRectElement)gfxElem;
        if (isUnderClipPath(rectElem)){
          logger.error("discarding clip-path Rect");
          return null;
        }
       
        float x     = rectElem.getX().getBaseVal().getValue();
        float y     = rectElem.getY().getBaseVal().getValue();
        float width   = rectElem.getWidth().getBaseVal().getValue();
        float height   = rectElem.getHeight().getBaseVal().getValue();
        float rx     = rectElem.getRx().getBaseVal().getValue();
        float ry     = rectElem.getRy().getBaseVal().getValue();
       
        AbstractShape comp;
        //Create a normal rectangle or a round rectangle
        if (rx != 0.0f || ry != 0.0f){
          if (rx > width/2 )
            rx = width/2;
          if (ry > height/2 )
            ry = height/2;
          comp = new MTRoundRectangle(x,y,0, width,height,rx, ry, pa);
        }else{
          comp = new MTRectangle(x,y, width,height, pa);
        }
       
        try{
          comp.setLocalMatrix(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
        returnComp = comp;
      }else if (gfxElem instanceof SVGOMEllipseElement){
        SVGOMEllipseElement ellipseElem = (SVGOMEllipseElement)gfxElem;
        float cx = ellipseElem.getCx().getBaseVal().getValue();
        float cy = ellipseElem.getCy().getBaseVal().getValue();
        float r  = ellipseElem.getRx().getBaseVal().getValue();
        float r2 = ellipseElem.getRy().getBaseVal().getValue();
       
        Vertex middlePoint = new Vertex(cx,cy,0);
        //Apply transformation, transform centerpoint and the radii
        try{
          middlePoint.transform(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
       
        //somehow the circle radii need to be doubled
        //or else theyre too small => processing bug?
//        r*=2;
//        r2*=2;
        MTEllipse comp = new MTEllipse(pa, middlePoint, r, r2);
        returnComp = comp;
      }else if (gfxElem instanceof SVGOMCircleElement){
        SVGOMCircleElement circleElem = (SVGOMCircleElement)gfxElem;
        float cx = circleElem.getCx().getBaseVal().getValue();
        float cy = circleElem.getCy().getBaseVal().getValue();
        float r = circleElem.getR().getBaseVal().getValue();
        float r2 = circleElem.getR().getBaseVal().getValue();
       
        Vertex middlePoint = new Vertex(cx,cy,0);
        //Apply transformation, transform centerpoint and the radii
        try{
          middlePoint.transform(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
       
        //somehow the circle radii need to be doubled
        //or else theyre too small => processing bug?
//        r*=2;
//        r2*=2;
        MTEllipse comp = new MTEllipse(pa, middlePoint, r, r2);
        returnComp = comp;
      }else if (gfxElem instanceof SVGOMLineElement){
        SVGOMLineElement line = (SVGOMLineElement)gfxElem;
        float x1 = line.getX1().getBaseVal().getValue();
        float y1 = line.getY1().getBaseVal().getValue();
        float x2 = line.getX2().getBaseVal().getValue();
        float y2 = line.getY2().getBaseVal().getValue();
        //logger.debug("Line x1: " + x1 + ",y1:" + y1 + ",x2:" + x2 + ",y2:" + y2);
       
        MTLine comp = new MTLine(pa, x1,y1 ,x2,y2);
      
        try{
          comp.setLocalMatrix(currentLocalTransformMatrix);
        }catch(Exception e){
          logger.error(e.getMessage());
        }
        returnComp = comp;
      }else if (gfxElem instanceof SVGOMClipPathElement){
      }else if (gfxElem instanceof SVGOMDefsElement){
      }else if (gfxElem instanceof SVGOMMaskElement){
      }else if (gfxElem instanceof SVGOMSwitchElement){
      }else if (gfxElem instanceof SVGOMFlowRootElement){
      }else if (gfxElem instanceof SVGURIReferenceGraphicsElement){
      }else if (gfxElem instanceof BindableElement){
      }else if (gfxElem instanceof SVGOMForeignObjectElement){
      }else if (gfxElem instanceof SVGOMToBeImplementedElement){
      }
     
      //Do the finishing touch of the svg graphics element
      if (returnComp != null){
        returnComp.setName(gfxElem.getTagName());
       
        //Set style infos
        if (returnComp instanceof AbstractVisibleComponent){
          AbstractVisibleComponent comp = (AbstractVisibleComponent)returnComp;
          //Set Fill
          comp.setFillColor(new MTColor(fillR, fillG, fillB, fillOpacity));
          comp.setNoFill(noFill);
          //Set Stroke
          comp.setStrokeColor(new MTColor(strokeR, strokeG, strokeB, strokeOpacity));
          //Opengl cant handle big lines well
          //So cap at width 3
          if (strokeWidth > 2.0f)
            strokeWidth = 2.0f;
          comp.setStrokeWeight(strokeWidth);
          comp.setNoStroke(noStroke);
          //Other
          comp.setDrawSmooth(true);
          comp.setPickable(false);
         
          //Hack for smoothing non stroked components with a stroke same as fillcolor
          if (comp.isNoStroke()
           && linearGradient == null
          ){ 
            comp.setStrokeColor(new MTColor(fillR, fillG, fillB, fillOpacity)); //fillOpacity
            comp.setStrokeWeight(0.6f);
            //Ellipse doesent smooth right with 0.1f strokeweight
            if (comp instanceof MTEllipse){
              comp.setStrokeWeight(1.0f);
            }
            comp.setNoStroke(false);
          }

          //Some settings for Geometric shapes (actually should all be)
          if (comp instanceof AbstractShape ){
            AbstractShape shape = (AbstractShape)comp;
            //Set a bounding rectangle to check first at picking
            if (shape.getVerticesLocal().length >= 3){
              shape.setBoundsBehaviour(AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK);
              //shape.setBoundingShape(new BoundsZPlaneRectangle(shape)); //already done by override, (ie svgpoly)
             
              //Create amd apply the linear gradient if existant and if we are in opengl rendering mode
              if (MT4jSettings.getInstance().isOpenGlMode()){
                if (linearGradient != null){
                  FillPaint gradient = this.createLinearGradient(linearGradient, gfxElem, originalFillOpacity, shape);
                  if (gradient != null){
                    shape.setFillPaint(gradient);
                  }
                }
                if (radialGradient != null){
                  FillPaint gradient = this.createRadialGradient(radialGradient, gfxElem, opacity, shape);
                  if (gradient != null){
                    shape.setFillPaint(gradient);
                  }
                }
              //Per default use direct gl drawing and displaylists in OGL mode
              if (pa instanceof MTApplication) {
                MTApplication app = (MTApplication) pa;
                app.invokeLater(new InvokeLaterAction(shape));
              }
              }
              //IF shape has no or only 1 vertex return null
            }else if (shape.getVerticesLocal().length < 2){
              return null;
            }else{
              shape.setBoundsBehaviour(AbstractShape.BOUNDS_DONT_USE);
              shape.setBounds(null);
//              shape.setUseDirectGL(false);
            }
           
            //Allow for picking the shape
            shape.setPickable(true);
           
            //Assign default gestures
//            shape.assignGestureClassAndAction(DragGestureAnalyzer.class, defaultDragAction);
//            shape.registerInputAnalyzer(new DragDetector(pa));
//            shape.setGestureAllowance(DragDetector.class, true);
//            shape.addGestureListener(DragDetector.class, (IGestureEventListener)defaultDragAction);
//            shape.registerInputAnalyzer(new RotationDetector(pa));
//            shape.addGestureListener(RotationDetector.class, new DefaultRotateAction());
//            shape.registerInputAnalyzer(new ScaleDetector(pa));
//            shape.addGestureListener(ScaleDetector.class,  new DefaultScaleAction());
          }
        }
      }
      return returnComp;
  }
 
 
  private class InvokeLaterAction implements Runnable{
    private AbstractShape shape;
    public InvokeLaterAction(AbstractShape shape) {
      super();
      this.shape = shape;
    }
    //@Override 
    public void run() {
      shape.setUseDirectGL(true);
        shape.generateAndUseDisplayLists();
    }
   
  }
 
  /**
     *
     * @param paintedElement the element interested in a Paint
     * @param paintDef the paint definition
     * @param opacity the opacity to consider for the Paint
     * @param ctx the bridge context
     */
    public static Object getFillOrStroke(Element paintedElement,
                                        Value paintDef,
                                        float opacity,
                                        BridgeContext ctx) {
        if (paintDef.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
            switch (paintDef.getPrimitiveType()) {
            case CSSPrimitiveValue.CSS_IDENT:
                return null; // none
            case CSSPrimitiveValue.CSS_RGBCOLOR:
                return PaintServer.convertColor(paintDef, opacity);
            case CSSPrimitiveValue.CSS_URI:
               String uri = paintDef.getStringValue();
                  Element paintElement = ctx.getReferencedElement(paintedElement, uri);
                  //logger.debug("Fill -> Uri: \"" + uri + "\" -> Referenced Element: \"" + paintElement.getNodeName() + "\" Class: \"" + paintElement.getClass() + "\"");
                  if (paintElement instanceof SVGOMLinearGradientElement){
                    SVGOMLinearGradientElement linearGradient = (SVGOMLinearGradientElement)paintElement;
                    return linearGradient;
//                    Bridge bridge = ctx.getBridge(paintElement);
//                    logger.debug("Bridge: " + bridge.getLocalName() + " Class: " + bridge.getClass());
//                    SVGLinearGradientElementBridge l = (SVGLinearGradientElementBridge)bridge;
//                    SVGOMLinearGradientElement linearGradient = (SVGOMLinearGradientElement)paintElement;
//                    SVGAnimatedEnumeration spreadMethod = linearGradient.getSpreadMethod();
                  }else  if (paintElement instanceof SVGOMRadialGradientElement){
                    SVGOMRadialGradientElement radialGradElement = (SVGOMRadialGradientElement)paintElement;
//                    logger.error("Radial gradient encountered -> Not supported yet.");
                    return radialGradElement;
                  }else{
                    logger.error("Couldnt read referenced Fill or Stroke from URI.");
                    return null;
                  }
              /*
                return PaintServer.convertURIPaint(paintedElement,
                                       paintedNode,
                                       paintDef,
                                       opacity,
                                       ctx);
               */
            default:
                throw new IllegalArgumentException
                    ("Paint argument is not an appropriate CSS value");
            }
        } else { // List
            Value v = paintDef.item(0);
            switch (v.getPrimitiveType()) {
            case CSSPrimitiveValue.CSS_RGBCOLOR:
                return PaintServer.convertRGBICCColor(paintedElement, v,
                                          (ICCColor)paintDef.item(1),
                                          opacity, ctx);

            case CSSPrimitiveValue.CSS_URI: {
//                Paint result = PaintServer.silentConvertURIPaint(paintedElement,
//                                                     paintedNode,
//                                                     v, opacity, ctx);
//                if (result != null)
//                  return result;
             
              String uri = v.getStringValue();
                Element paintElement = ctx.getReferencedElement(paintedElement, uri);
                //logger.debug("Fill -> Uri: \"" + uri + "\" -> Referenced Element: \"" + paintElement.getNodeName() + "\" Class: \"" + paintElement.getClass() + "\"");
               
                if (paintElement instanceof SVGOMLinearGradientElement){
                  SVGOMLinearGradientElement linearGradient = (SVGOMLinearGradientElement)paintElement;
                  return linearGradient;
                }else  if (paintElement instanceof SVGOMRadialGradientElement){
                  SVGOMRadialGradientElement radialGradElement = (SVGOMRadialGradientElement)paintElement;
//                  logger.error("Radial gradient encountered -> Not supported yet.");
                  return radialGradElement;
                }

                v = paintDef.item(1);
                switch (v.getPrimitiveType()) {
                case CSSPrimitiveValue.CSS_IDENT:
                    return null; // none
                case CSSPrimitiveValue.CSS_RGBCOLOR:
                    if (paintDef.getLength() == 2) {
                        return PaintServer.convertColor(v, opacity);
                    } else {
                        return PaintServer.convertRGBICCColor(paintedElement, v,
                                                  (ICCColor)paintDef.item(2),
                                                  opacity, ctx);
                    }
                default:
                    throw new IllegalArgumentException
                        ("Paint argument is not an appropriate CSS value");
                }
            }
            default:
                // can't be reached
                throw new IllegalArgumentException
                    ("Paint argument is not an appropriate CSS value");
            }
        }
    }

   
    //TODO
  // - spreadMethod implement linear
    // - linearGradient klasse machen mit der man ein gradient erstellen kann
    //   mit stops[] offsets[] colors[] xy, bbox/userSpace
    // - wie shapes ohne stroke mit outline= gradient zeichnen f�r antialiasing?
    //   evtl gradientshape normal zeichnen, aber mit realshape clipmasken?
   
    private FillPaint createRadialGradient(Element paintElement, SVGGraphicsElement gfxElem, float opacity, AbstractShape shape){
      //Get the <stop> elements
        List<Stop> stops = this.extractStops(paintElement, opacity, ctx);
        // if no stops are defined, painting is the same as 'none'
        if (stops == null) {
            return null;
        }
        int stopLength = stops.size();
        // if one stops is defined, painting is the same as a single color
        if (stopLength == 1) {
          return null;
        }

        float [] offsets = new float[stopLength];
        java.awt.Color [] colors  = new java.awt.Color[stopLength];
        Iterator<Stop> iter = stops.iterator();
        for (int i=0; iter.hasNext(); ++i) {
          Stop stop = (Stop)iter.next();
          offsets[i] = stop.offset;
          colors[i] = stop.color;
        }
       
        //Get the spread method of the gradient
        MultipleGradientPaint.CycleMethodEnum spreadMethod = getSpreadMethod(paintElement);
       
        //'color-interpolation' CSS property
        MultipleGradientPaint.ColorSpaceEnum colorSpace = CSSUtilities.convertColorInterpolation(paintElement);

        //Get the gradient transform - //'gradientTransform' attribute - default is an Identity matrix
        AffineTransform transform = getGradientTransform(paintElement);
       
        //////////////////////////////////buildgradient function
        // 'cx' attribute - default is 50%
        String cxStr = SVGUtilities.getChainableAttributeNS
            (paintElement, null, SVG_CX_ATTRIBUTE, ctx);
        if (cxStr.length() == 0) {
            cxStr = SVG_RADIAL_GRADIENT_CX_DEFAULT_VALUE;
        }
        // 'cy' attribute - default is 50%
        String cyStr = SVGUtilities.getChainableAttributeNS
            (paintElement, null, SVG_CY_ATTRIBUTE, ctx);
        if (cyStr.length() == 0) {
            cyStr = SVG_RADIAL_GRADIENT_CY_DEFAULT_VALUE;
        }
        // 'r' attribute - default is 50%
        String rStr = SVGUtilities.getChainableAttributeNS
            (paintElement, null, SVG_R_ATTRIBUTE, ctx);
        if (rStr.length() == 0) {
            rStr = SVG_RADIAL_GRADIENT_R_DEFAULT_VALUE;
        }
        // 'fx' attribute - default is same as cx
        String fxStr = SVGUtilities.getChainableAttributeNS
            (paintElement, null, SVG_FX_ATTRIBUTE, ctx);
        if (fxStr.length() == 0) {
            fxStr = cxStr;
        }
        // 'fy' attribute - default is same as cy
        String fyStr = SVGUtilities.getChainableAttributeNS
            (paintElement, null, SVG_FY_ATTRIBUTE, ctx);
        if (fyStr.length() == 0) {
            fyStr = cyStr;
        }

        // 'gradientUnits' attribute - default is objectBoundingBox
        short coordSystemType;
        String s = SVGUtilities.getChainableAttributeNS
            (paintElement, null, SVG_GRADIENT_UNITS_ATTRIBUTE, ctx);
        if (s.length() == 0) {
            coordSystemType = SVGUtilities.OBJECT_BOUNDING_BOX;
        } else {
            coordSystemType = SVGUtilities.parseCoordinateSystem(paintElement, SVG_GRADIENT_UNITS_ATTRIBUTE, s, ctx);
        }
       
        UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, paintElement);

        float r = SVGUtilities.convertLength(rStr,
                                             SVG_R_ATTRIBUTE,
                                             coordSystemType,
                                             uctx);
        // A value of zero will cause the area to be painted as a single color
        // using the color and opacity of the last gradient stop.
        if (r == 0) {
          return null;
        }

        Point2D c = SVGUtilities.convertPoint(cxStr,
            SVG_CX_ATTRIBUTE,
            cyStr,
            SVG_CY_ATTRIBUTE,
            coordSystemType,
            uctx);

        Point2D f = SVGUtilities.convertPoint(fxStr,
            SVG_FX_ATTRIBUTE,
            fyStr,
            SVG_FY_ATTRIBUTE,
            coordSystemType,
            uctx);
       
        //Get gradient vector
        logger.debug("C: " + c + " F: " +f);
       
        CycleMethod awtCycleMethod = CycleMethod.NO_CYCLE;
        if (spreadMethod == MultipleGradientPaint.REPEAT){
          awtCycleMethod = CycleMethod.REPEAT;
        }else if(spreadMethod ==  MultipleGradientPaint.REFLECT){
          awtCycleMethod = CycleMethod.REFLECT;
        }

        if (pa instanceof MTApplication) {
          MTApplication app = (MTApplication) pa;

          //Calculate a bounding rectangle from the rotated shape
          BoundsZPlaneRectangle boundsZ = new BoundsZPlaneRectangle(shape, shape.getVerticesLocal());
          Vector3D[] boundsVecs = boundsZ.getVectorsLocal();
          float bBoxWidth  = boundsZ.getWidthXY(TransformSpace.LOCAL);//boundsVecs[1].x - boundsVecs[0].x;
          float bBoxHeight = boundsZ.getHeightXY(TransformSpace.LOCAL);//boundsVecs[2].y - boundsVecs[1].y;

          SwingTextureRenderer swingTex;
          final MTRectangle rectangle;

          //Trial to make the texture as big as the bigger side of the bounding rectangle of the shape
          //to allow for automatic texture stretching to fit when texture is applied
          int size = -1;
          if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX){
            if (bBoxWidth >= bBoxHeight){
              size = Math.round(bBoxWidth);
              r *= bBoxWidth;
            }else{
              size = Math.round(bBoxHeight);
              r *= bBoxHeight;
            }

            AffineTransform Mx = new AffineTransform();
            Rectangle2D bounds = new Rectangle(Math.round(boundsVecs[0].x), Math.round(boundsVecs[0].y), size, size);
            if (bounds != null) {
              //we dont translate the center and focal point
              //instead we create the gradient mask shape at that position
//            Mx.translate(bounds.getX(), bounds.getY());
              Mx.scale(bounds.getWidth(), bounds.getHeight());
            }
            Mx.concatenate(transform);
            transform = Mx;

            //Transform gradient vector points with gradientTransform
            transform.transform(c, c);
            transform.transform(f, f);

            GradientPanel gradPanel = new GradientPanel(size, size, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY(), awtCycleMethod);
//          GradientPanel gradPanel = new GradientPanel(bBoxWidth, bBoxHeight, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY());
            swingTex = new SwingTextureRenderer(app, gradPanel);
            swingTex.scheduleRefresh();
            rectangle = new MTRectangle(new Vertex(boundsVecs[0]), bBoxWidth, bBoxHeight, pa);
            rectangle.setName("Swing texture rendering");
            rectangle.setTexture(swingTex.getTextureToRenderTo());
            rectangle.setNoStroke(true);
            rectangle.setPickable(false);
            rectangle.setFillDrawMode(GL.GL_QUADS);

            //Use displaylist by default for gradientshape
            if (MT4jSettings.getInstance().isOpenGlMode()){
              app.invokeLater(new InvokeLaterAction(rectangle));
            }

            //FIXME REMOVE TEST
            /*//Draw the shape we draw in swing
          MTRectangle rectanglePaintedComp = new MTRectangle(new Vertex(boundsVecs[0]), size, size, pa);
          rectanglePaintedComp.setName("rectanglePaintedComp");
          rectanglePaintedComp.setTexture(swingTex.getTextureToRenderTo());
          rectanglePaintedComp.setFillColor(255, 255, 255, 150);
          shape.addChild(rectanglePaintedComp);
             */
          }else{
            //coordsystemtype = userSpaceOnUse!

            //FIXME Problem at userOnSpace with proportional length (%)
            //seems we have to take the width/height from the viewbox then!? and use bounding box code above? but we have to recalculate absoulte values then..

            //Since we draw the gradient at 0,0 we have to transform the gradient points to there
            AffineTransform Mx = new AffineTransform();
            Mx.translate(-boundsVecs[0].x, -boundsVecs[0].y);
            Mx.concatenate(transform);
            transform = Mx;

            //Transform gradient points with gradientTransform
            transform.transform(c, c);
            transform.transform(f, f);

//          GradientPanel gradPanel = new GradientPanel(size, size, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY());
            GradientPanel gradPanel = new GradientPanel(bBoxWidth, bBoxHeight, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY(), awtCycleMethod);
            swingTex = new SwingTextureRenderer(app, gradPanel);
            swingTex.scheduleRefresh();
            rectangle = new MTRectangle(new Vertex(boundsVecs[0]), bBoxWidth, bBoxHeight, pa);
            final GLTexture tex = swingTex.getTextureToRenderTo();
            rectangle.setName("Swing texture rendering");
            rectangle.setTexture(tex);
            rectangle.setNoStroke(true);
            rectangle.setPickable(false);

//          /*//
            if (MT4jSettings.getInstance().isOpenGlMode()){
              app.invokeLater(new InvokeLaterAction(rectangle));
            }
//          */
            //FIXME REMOVE TEST
            /*//Draw the shape we draw in swing
          MTRectangle rectanglePaintedComp = new MTRectangle(new Vertex(boundsVecs[0]), bBoxWidth, bBoxHeight, pa);
          rectanglePaintedComp.setName("rectanglePaintedComp");
          rectanglePaintedComp.setTexture(swingTex.getTextureToRenderTo());
          rectanglePaintedComp.setFillColor(255, 255, 255, 150);
          shape.addChild(rectanglePaintedComp);
             */
          }
          FillPaint gradStencil = new FillPaint(((PGraphicsOpenGL)pa.g).gl, rectangle);
          return gradStencil;
//        return null;
        }
        return null;
    }


   
    /**
     * Helper class to paint a radial gradient with java2D into a texture.
     */
    private class GradientPanel extends JPanel{
      private float width;
      private float height;
     
      private float[] offsets;
      private java.awt.Color[] colors;
      private float cx;
      private float cy;
      private float fx;
      private float fy;
      private float radius;
     
      private CycleMethod cycleMethod;
     
    public GradientPanel(float width, float height, float radius, float[] offsets,
        Color[] colors, float cx, float cy, float fx, float fy,
        CycleMethod cycleMethod
      ) {
      super();
      this.width = width;
      this.height = height;
      this.radius = radius;
      this.offsets = offsets;
      this.colors = colors;
      this.cx = cx;
      this.cy = cy;
      this.fx = fx;
      this.fy = fy;
      this.cycleMethod = cycleMethod;
      this.setSize(Math.round(width), Math.round(height));
    }

    protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          Graphics2D g2 = (Graphics2D)g;
          int w = getWidth();
          int h = getHeight();
          Rectangle r = new Rectangle(0, 0, w, h); //original
          RadialGradientPaint rgp = new RadialGradientPaint(
              cx, cy,
//              400, 250,
          radius,
//              100,
          fx, fy,
//          400, 250,
                    offsets,
                    colors,
          cycleMethod);
          g2.setPaint(rgp);
          g2.fill(r);
      }
  }

  private FillPaint createLinearGradient(Element paintElement, SVGGraphicsElement gfxElem, float opacity, AbstractShape shape){
        // stop elements
        List<Stop> stops = this.extractStops(paintElement, opacity, ctx);
        // if no stops are defined, painting is the same as 'none'
        if (stops == null) {
            return null;
        }
        int stopLength = stops.size();
        // if one stops is defined, painting is the same as a single color
        if (stopLength == 1) {
          return null;
        }
       
        //Get the spread method of the gradient
        MultipleGradientPaint.CycleMethodEnum spreadMethod = getSpreadMethod(paintElement);

        //'color-interpolation' CSS property
        MultipleGradientPaint.ColorSpaceEnum colorSpace = CSSUtilities.convertColorInterpolation(paintElement);

        //Get the gradient transform - //'gradientTransform' attribute - default is an Identity matrix
        AffineTransform transform = getGradientTransform(paintElement);

        //logger.debug("Gradienttransform: " + transform);
   
    //////////////////////////////////buildgradient function
    // 'x1' attribute - default is 0%
    String x1Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_X1_ATTRIBUTE, ctx);
    if (x1Str.length() == 0) {
      x1Str = SVG_LINEAR_GRADIENT_X1_DEFAULT_VALUE;
    }
    // 'y1' attribute - default is 0%
    String y1Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_Y1_ATTRIBUTE, ctx);
    if (y1Str.length() == 0) {
      y1Str = SVG_LINEAR_GRADIENT_Y1_DEFAULT_VALUE;
    }
    // 'x2' attribute - default is 100%
    String x2Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_X2_ATTRIBUTE, ctx);
    if (x2Str.length() == 0) {
      x2Str = SVG_LINEAR_GRADIENT_X2_DEFAULT_VALUE;
    }
    // 'y2' attribute - default is 0%
    String y2Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_Y2_ATTRIBUTE, ctx);
    if (y2Str.length() == 0) {
      y2Str = SVG_LINEAR_GRADIENT_Y2_DEFAULT_VALUE;
    }

    // 'gradientUnits' attribute - default is objectBoundingBox
    short coordSystemType;
    String s2 = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_GRADIENT_UNITS_ATTRIBUTE, ctx);
    if (s2.length() == 0) {
      coordSystemType = SVGUtilities.OBJECT_BOUNDING_BOX;
    } else {
      coordSystemType = SVGUtilities.parseCoordinateSystem(paintElement, SVG_GRADIENT_UNITS_ATTRIBUTE, s2, ctx);
    }

    // additional transform to move to objectBoundingBox coordinate system
    //TODO gradienttransform
//    if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX) {
//      transform = SVGUtilities.toObjectBBox(transform, gfxElem);
//    }
    UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, gfxElem);

    Point2D p1 = SVGUtilities.convertPoint(x1Str,
        SVG_X1_ATTRIBUTE,
        y1Str,
        SVG_Y1_ATTRIBUTE,
        coordSystemType,
        uctx);

    Point2D p2 = SVGUtilities.convertPoint(x2Str,
        SVG_X2_ATTRIBUTE,
        y2Str,
        SVG_Y2_ATTRIBUTE,
        coordSystemType,
        uctx);
   
   
    //Transform gradient vector points with gradientTransform
    Point2D tp1 = null;
    tp1 = transform.transform(p1, tp1);
    p1 = tp1;
    Point2D tp2 = null;
    tp2 = transform.transform(p2, tp2);
    p2 = tp2;
   
   
    //Get gradient vector
    logger.debug("P1: " + p1 + " P2: " + p2);
    Vector3D ref = new Vector3D(1,0,0);
    Vector3D vP1 = new Vector3D((float)p1.getX(), (float)p1.getY(), 0);
    Vector3D vP2 = new Vector3D((float)p2.getX(), (float)p2.getY(), 0);
    Vector3D gradVect = vP2.getSubtracted(vP1);
    //logger.debug("Gradient vector: " + gradVect);
   
    //Get gradient vector angle to rotate the shape to be gradiented to the horizontal vector 1,0,0
    //Algorithm for linear gradients used here:
    //1. rotate the shape so that the gradient vector in the shape is parallel to 1,0,0
    //2. calc bounding rectangle of shape + gradient rectangle (made of the x1,y1 x2,y2 gradient endpoints)
    //3. create quads with colored vertices at each (now horizontal) stop along the gradient
    //4. rotate the gradient shape with the quads back so it faces the original gradient vector direction
   
    float gradAngle = Vector3D.angleBetween(ref, gradVect);
    gradAngle = PApplet.degrees(gradAngle);
   
    Vector3D cross = ref.getCross(gradVect);
    //Get the direction of rotation
    if (cross.getZ() < 0){
      gradAngle*=-1;
    }
   
    logger.debug("Gradient angle: " + gradAngle + "�");
   
    logger.debug("Stops:");
    for (Stop stop : stops)
      logger.debug(" Stop -> Offset: " + stop.offset + " java.awt.Color: " + stop.color);
   
    if (coordSystemType == SVGUtilities.USER_SPACE_ON_USE){
      return this.setUpRotatedGradientUserSpace(shape, gradAngle, stops, p1, p2);
    }else{
      return this.setUpRotatedGradientBBox(shape, gradAngle, stops);
    }
   
    // If x1 = x2 and y1 = y2, then the area to be painted will be painted
    // as a single color using the color and opacity of the last gradient
    // stop.
//    if (p1.getX() == p2.getX() && p1.getY() == p2.getY()) {
//      return colors[colors.length-1];
//    } else {
//      return new LinearGradientPaint(p1,
//          p2,
//          offsets,
//          colors,
//          spreadMethod,
//          colorSpace,
//          transform);
//    }
  }
 

  private FillPaint setUpRotatedGradientUserSpace(AbstractShape testShape, float angle, List<Stop> stops, Point2D p1, Point2D p2){
    GL gl = ((PGraphicsOpenGL)pa.g).gl;
    float gradAngle = angle;
   
    float invAngle = angle*-1;
   
    //Get copy of shapes vertices
    Vertex[] shapeVertsCopy = Vertex.getDeepVertexArrayCopy(testShape.getGeometryInfo().getVertices());
    //Rotate the vertices in the inverse direction of the gradients vector angle
    shapeVertsCopy = (Vertex[]) Vertex.rotateZVectorArray(shapeVertsCopy, testShape.getCenterPointLocal(), invAngle);
   
    Vertex vP1 = new Vertex((float)p1.getX(), (float)p1.getY(), 0);
    Vertex vP2 = new Vertex((float)p2.getX(), (float)p2.getY(), 0);
    float gradientRectWidth = vP2.getSubtracted(vP1).length();
   
    Vertex[] gradientRectVerts = new Vertex[]{
        vP1,
        new Vertex((float)p2.getX(), (float)p1.getY(),0),
        vP2,
        new Vertex((float)p1.getX(), (float)p2.getY(),0)
        };
    //Rotate the vertices in the inverse direction of the gradients vector angle
    gradientRectVerts = (Vertex[]) Vertex.rotateZVectorArray(gradientRectVerts, testShape.getCenterPointLocal(), invAngle);
   
    //Copy the rotated bounding shape vertices and the rotated gradient rectangle vertices into one array
    Vertex[] shapeAndGradVerts = new Vertex[shapeVertsCopy.length + gradientRectVerts.length];
    System.arraycopy(shapeVertsCopy, 0, shapeAndGradVerts, 0, shapeVertsCopy.length);
    System.arraycopy(gradientRectVerts, 0, shapeAndGradVerts, shapeVertsCopy.length, gradientRectVerts.length);
   
    //Create a temporary polygon with the roated vertices to calc BBox
    MTPolygon inverseRotatedShape = new MTPolygon(shapeAndGradVerts, pa);
    //Calculate a bounding rectangle from the rotated shape
    BoundsZPlaneRectangle inverseRotatedBounds = new BoundsZPlaneRectangle(inverseRotatedShape);
    Vector3D[] invBoundsVecs = inverseRotatedBounds.getVectorsLocal();
   
    //logger.debug("Gradient Rectangle width: " + gradientRectWidth);
   
    //Get the positions where the offsets are on the gradient vector
//    float bBoxWidth  = invBoundsVecs[1].x - invBoundsVecs[0].x;
//    logger.debug("BBox width: " + bBoxWidth);
//    float w = bBoxWidth/*/100*/;
    List<Float> xStops = new ArrayList<Float>();
   
    //- Go through stops
    //- multiply stop offsets with bbox width to get the position on gradient vector
    //logger.debug("->Gradient Vector stop positions:");
    for(Stop stop : stops){
      float offsetStopPosition = gradientRectWidth * stop.offset; //position auf gradient vector, stop(0) = vP1.x + offest
      xStops.add(offsetStopPosition);
      //logger.debug(" Offset-Stop-Position: " + offsetStopPosition);
    }
   
    //Calc new gradient polygon vertices with vertices at the stop locations
    Vertex[] newBounds = new Vertex[(xStops.size()-1) * 4];
    for (int i = 0; i < xStops.size()-1; i++) {
      float offset = xStops.get(i);
      Color stopColor = stops.get(i).color;
     
      float nextOffset = xStops.get(i+1);
      Color nextStopColor = stops.get(i+1).color;
     
      newBounds[i*4]     = new Vertex(vP1.x + offset,     invBoundsVecs[0].y,0,   stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
      newBounds[i*4+1= new Vertex(vP1.x + nextOffset, invBoundsVecs[0].y,0,   nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
      newBounds[i*4+2]   = new Vertex(vP1.x + nextOffset, invBoundsVecs[2].y,0,   nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
      newBounds[i*4+3= new Vertex(vP1.x + offset,    invBoundsVecs[2].y,0,   stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
    }
   
    //Put gradient rectangle quads into a list
    List<Vertex> gradientRectQuads = new ArrayList<Vertex>();
    for (int i = 0; i < newBounds.length; i++) {
      Vertex vertex = newBounds[i];
      gradientRectQuads.add(vertex);
    }
   
    /* Bounding shape with gradient rectangle inside (can also overlap outlines)
     invBoundsVecs[0]       invBoundsVecs[1]
            | _______________ |
           |   |_____|    |
           |   |  G  |     |
           | vp1|____>|vp2 |
           |____|_____|____|
     */
    //Calc rectangle bands (quads) to fill the gradient shape with the gradVect end colors if the gradient vector is smaller than the shape to draw
    List<Vertex> leftQuad = new ArrayList<Vertex>();
    if (vP1.x > invBoundsVecs[0].x){
      //upper left of bounding rect
      Vertex v1 = new Vertex(invBoundsVecs[0].x, invBoundsVecs[0].y, 0, newBounds[0].getR(), newBounds[0].getG(), newBounds[0].getB(), newBounds[0].getA());
      //first stop on gradient vector upper
      Vertex v2 = new Vertex(newBounds[0].x, newBounds[0].y, 0, newBounds[0].getR(), newBounds[0].getG(), newBounds[0].getB(), newBounds[0].getA());
      //first stop on gradient vector lower
      Vertex v3 = new Vertex(newBounds[3].x, newBounds[3].y, 0, newBounds[3].getR(), newBounds[3].getG(), newBounds[3].getB(), newBounds[3].getA());
      //down left of bounding rect
      Vertex v4 = new Vertex(invBoundsVecs[3].x, invBoundsVecs[3].y, 0, newBounds[0].getR(), newBounds[0].getG(), newBounds[0].getB(), newBounds[0].getA());
     
      leftQuad.add(v1);
      leftQuad.add(v2);
      leftQuad.add(v3);
      leftQuad.add(v4);
    }
   
    //Add Right quad if gradient rectangle is smaler than overall bounds
    List<Vertex> rightQuad = new ArrayList<Vertex>();
    if (vP2.x < invBoundsVecs[1].x){
      Vertex gradientRectUpperRight = newBounds[newBounds.length-3];
      Vertex gradientRectLowerRight = newBounds[newBounds.length-2];
     
      Vertex v1 = new Vertex(gradientRectUpperRight.x, gradientRectUpperRight.y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
      Vertex v2 = new Vertex(invBoundsVecs[1].x, invBoundsVecs[1].y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
      Vertex v3 = new Vertex(invBoundsVecs[2].x, invBoundsVecs[2].y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
      Vertex v4 = new Vertex(gradientRectLowerRight.x, gradientRectLowerRight.y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
     
      rightQuad.add(v1);
      rightQuad.add(v2);
      rightQuad.add(v3);
      rightQuad.add(v4);
    }
   
    //Create new array for gradient shape with all quads inside
    List<Vertex> allGradientShapeVerts = new ArrayList<Vertex>();
    allGradientShapeVerts.addAll(leftQuad);
    allGradientShapeVerts.addAll(gradientRectQuads);
    allGradientShapeVerts.addAll(rightQuad);
    newBounds = allGradientShapeVerts.toArray(new Vertex[allGradientShapeVerts.size()]);
   
    //Rotate the vectors of the calculated bounding rect back to the original angle
    newBounds = (Vertex[]) Vector3D.rotateZVectorArray(newBounds, testShape.getCenterPointLocal(), gradAngle);
   
    //Create gradient shape to paint over the real shape
    MTPolygon p = new MTPolygon(newBounds,pa);
        p.setNoStroke(true);
        p.setPickable(false);
        p.setStrokeWeight(testShape.getStrokeWeight());
        p.setFillDrawMode(GL.GL_QUADS);
        //Use displaylist by default for gradientshape
        p.generateAndUseDisplayLists();
       
        FillPaint gradStencil = new FillPaint(gl, p);
    return gradStencil;
  }

 
  private FillPaint setUpRotatedGradientBBox(AbstractShape testShape, float angle, List<Stop> stops){
      GL gl = ((PGraphicsOpenGL)pa.g).gl;
      float gradAngle = angle;
     
      //Get copy of shapes vertices
      Vertex[] shapeVertsCopy = Vertex.getDeepVertexArrayCopy(testShape.getGeometryInfo().getVertices());
      //Rotate the vertices in the inverse direction of the gradients vector angle
      shapeVertsCopy = (Vertex[]) Vertex.rotateZVectorArray(shapeVertsCopy, testShape.getCenterPointLocal(), -gradAngle);
     
      //Create a temporary polygon with the roated vertices to calc BBox
      MTPolygon inverseRotatedShape = new MTPolygon(shapeVertsCopy, pa);
      //Calculate a bounding rectangle from the rotated shape
      BoundsZPlaneRectangle inverseRotatedBounds = new BoundsZPlaneRectangle(inverseRotatedShape);
      Vector3D[] invBoundsVecs = inverseRotatedBounds.getVectorsLocal();
     
      //Get the positions where the offsets are on the gradient vector
      float bBoxWidth  = invBoundsVecs[1].x - invBoundsVecs[0].x;
      logger.debug("BBox width: " + bBoxWidth);
      float w = bBoxWidth/*/100*/;
      List<Float> xStops = new ArrayList<Float>();
      //Go through stops and multiply stop offset with bbox width to get the position
      for(Stop stop : stops){
        float offsetStopPosition = w * stop.offset;
        xStops.add(offsetStopPosition);
        logger.debug("OffsetStopPosition: " + offsetStopPosition);
      }
     
      //Calc new gradient polygon vertices with vertices at the stop locations
      Vertex[] newBounds = new Vertex[(xStops.size()-1) * 4];
      for (int i = 0; i < xStops.size()-1; i++) {
        float offset = xStops.get(i);
        Color stopColor = stops.get(i).color;
        float nextOffset = xStops.get(i+1);
        Color nextStopColor = stops.get(i+1).color;
        newBounds[i*4]     = new Vertex(invBoundsVecs[0].x + offset,     invBoundsVecs[0].y,0,   stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
        newBounds[i*4+1= new Vertex(invBoundsVecs[0].x + nextOffset, invBoundsVecs[0].y,0,   nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
        newBounds[i*4+2]   = new Vertex(invBoundsVecs[0].x + nextOffset, invBoundsVecs[2].y,0,   nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
        newBounds[i*4+3= new Vertex(invBoundsVecs[0].x + offset,     invBoundsVecs[2].y,0,   stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
      }
     
      /*
      logger.debug("->New bounds:");
      for (int i = 0; i < newBounds.length; i++) {
        Vertex vertex = newBounds[i];
        logger.debug(vertex);
      }
      */
     
      //Rotate the vectors of the calculated bounding rect back to the original angle
      newBounds = (Vertex[]) Vector3D.rotateZVectorArray(newBounds, testShape.getCenterPointLocal(), gradAngle);
     
      //Create gradient shape to paint over the real shape
      MTPolygon p = new MTPolygon(newBounds,pa);
          p.setNoStroke(true);
          p.setPickable(false);
          p.setFillDrawMode(GL.GL_QUADS);
          p.setStrokeWeight(testShape.getStrokeWeight());
          //Use displaylist by default for gradientshape
          p.generateAndUseDisplayLists();
     
          FillPaint gradStencil = new FillPaint(gl, p);
          return gradStencil;
    }
     
     
 
    private CycleMethodEnum getSpreadMethod(Element paintElement){
      String s = new String();
        //SPREADMETHOD 'spreadMethod' attribute - default is pad
        CycleMethodEnum spreadMethod = MultipleGradientPaint.NO_CYCLE;
        s = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_SPREAD_METHOD_ATTRIBUTE, ctx);
        if (s.length() != 0) {
//            spreadMethod = AbstractSVGGradientElementBridge.convertSpreadMethod(paintElement, s, ctx);
            if (SVG_REPEAT_VALUE.equals(s)) {
              spreadMethod =  MultipleGradientPaint.REPEAT;
            }else
              if (SVG_REFLECT_VALUE.equals(s)) {
                spreadMethod =  MultipleGradientPaint.REFLECT;
            }else
              if (SVG_PAD_VALUE.equals(s)) {
                spreadMethod =  MultipleGradientPaint.NO_CYCLE;
            }else
              throw new BridgeException(ctx, paintElement, "ERR_ATTRIBUTE_VALUE_MALFORMED", new Object[] {SVG_SPREAD_METHOD_ATTRIBUTE, s});
        }
        return spreadMethod;
    }
   
   
    private AffineTransform getGradientTransform(Element paintElement){
      String s = new String();
        //'gradientTransform' attribute - default is an Identity matrix
        AffineTransform transform;
        s = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_GRADIENT_TRANSFORM_ATTRIBUTE, ctx);
        if (s.length() != 0) {
            transform = SVGUtilities.convertTransform(paintElement, SVG_GRADIENT_TRANSFORM_ATTRIBUTE, s, ctx);
        } else {
            transform = new AffineTransform();
        }
        return transform;
    }
     
   /**
     * Returns the stops elements of the specified gradient
     * element. Stops can be children of the gradients or defined on
     * one of its 'ancestor' (linked with the xlink:href attribute).
     *
     * @param paintElement the gradient element
     * @param opacity the opacity
     * @param ctx the bridge context to use
     */
    protected List<Stop> extractStops(Element paintElement, float opacity, BridgeContext ctx) {
        //List<Object> refs = new LinkedList<Object>();
        for (;;) {
            List<Stop> stops = extractLocalStop(paintElement, opacity, ctx);
            if (stops != null) {
              boolean zeroOffset = false;
              boolean oneOffset = false;
              boolean zeroOffsetAdded = false;
              boolean oneOffsetAdded = false;
              //Stops in svg dont have to have ending stops at 0.0 and 1.0
              //but we need them so we add them ourselves if not present
              for (Stop stop :stops){
                if (stop.offset == 0.0){
                  zeroOffset = true;
                }
                if (stop.offset == 1.0){
                  oneOffset  = true;
                }
              }
              //Add a stop for beginning and end if not existant
              if (!zeroOffset){
                logger.debug("No offset at 0.0 location -> adding it.");
                stops.add(0, new AbstractSVGGradientElementBridge.Stop(new java.awt.Color(0,0,0,0), 0.0f));
                zeroOffsetAdded = true;
              }
              if (!oneOffset){
                logger.debug("No offset at 1.0 location -> adding it.");
                stops.add(stops.size(), new AbstractSVGGradientElementBridge.Stop(new java.awt.Color(0,0,0,0), 1.0f));
                oneOffsetAdded = true;
              }
             
              //Sort stops by offset position from 0.0 to 1.0
              List<GradientStop> gradientStops = new ArrayList<GradientStop>();
              for (Stop stop :stops){
                gradientStops.add(new GradientStop(stop.offset, stop.color));
              }
              GradientStop[] gradStopArr = gradientStops.toArray(new GradientStop[gradientStops.size()]);
              HelperMethods.quicksort(gradStopArr);
              //Create new, sorted stop list, clamp color from self created zero/one offsets to nearest color
              List<Stop> sortedStopList = new ArrayList<Stop>();
              for (int i = 0; i < gradStopArr.length; i++) {
          GradientStop gradientStop = gradStopArr[i];
          if (zeroOffsetAdded
            && i == 0
            && gradStopArr.length >= i+
            && gradStopArr[i+1] != null
          ){
            gradientStop.color = gradStopArr[i+1].color;
          }
          if (oneOffsetAdded
            && i == gradStopArr.length-1
            //&& gradStopArr.length >= i+1 
            && gradStopArr[i-1] != null
          ){
              gradientStop.color = gradStopArr[i-1].color;
          }
          sortedStopList.add(new AbstractSVGGradientElementBridge.Stop(gradientStop.color, gradientStop.offset));
        }
              return sortedStopList;
             
//                return stops; // stop elements found, exit
            }
            String uri = XLinkSupport.getXLinkHref(paintElement);
            if (uri.length() == 0) {
                return null; // no xlink:href found, exit
            }
            // check if there is circular dependencies
            /*
            String baseURI = XMLBaseSupport.getCascadedXMLBase(paintElement);
            ParsedURL purl = new ParsedURL(baseURI, uri);
            if (contains(refs, purl)) {
                throw new BridgeException(paintElement,
                                          ERR_XLINK_HREF_CIRCULAR_DEPENDENCIES,
                                          new Object[] {uri});
            }
            refs.add(purl);
             */
            paintElement = ctx.getReferencedElement(paintElement, uri);
        }
    }
   

    /**
     * To compare stop offsets
     * @author Chris
     */
    private class GradientStop implements Comparable<GradientStop>{
      float offset;
      Color color;

      public GradientStop(float offset, java.awt.Color color2){
        this.offset = offset;
        this.color = color2;
      }

      //@Override
      public int compareTo(GradientStop o) {
        if (this.offset < o.offset){
          return -1;
        }
        else if(offset == o.offset){
          return 0;
        }
        else if(this.offset > o.offset){
          return 1;
        }else{
          return 0;
        }
      }
    }


    /**
     * Returns a list of <tt>Stop</tt> elements, children of the
     * specified paintElement can have or null if any.
     *
     * @param gradientElement the paint element
     * @param opacity the opacity
     * @param ctx the bridge context
     */
    protected static List<Stop> extractLocalStop(Element gradientElement, float opacity, BridgeContext ctx) {
      LinkedList<Stop> stops = null;
      Stop previous = null;
      for (Node n = gradientElement.getFirstChild(); n != null; n = n.getNextSibling()){
        if ((n.getNodeType() != Node.ELEMENT_NODE)) {
          continue;
        }

        Element e = (Element)n;
        Bridge bridge = ctx.getBridge(e);
        if (bridge == null || !(bridge instanceof SVGStopElementBridge)) {
          continue;
        }
        Stop stop = ((SVGStopElementBridge)bridge).createStop(ctx, gradientElement, e, opacity);
        if (stops == null) {
          stops = new LinkedList<Stop>();
        }
        if (previous != null) {
          if (stop.offset < previous.offset) {
            stop.offset = previous.offset;
          }
        }
        stops.add(stop);
        previous = stop;
      }
      return stops;
    }

  
  private List<Float> getSVGLengthListAsFloat(SVGLengthList valueList){
      List<Float> values = new ArrayList<Float>();
      for (int i = 0; i < valueList.getNumberOfItems(); i++) {
        values.add(valueList.getItem(i).getValue());
      }
      if (values.isEmpty()){
        values.add(0f);
      }
      return values;
  }
 
 
  /**
   * Tries to retrieve a css property as a float number.
   * If it fails, it returns the provided defaultvalue.
   *
   * @param gfxElem the gfx elem
   * @param queryProperty the query property
   * @param defaultValue the default value
   *
   * @return the float
   */
  private float queryPrimitiveFloatValue(SVGGraphicsElement gfxElem, String queryProperty, float defaultValue){
    float returnValue = defaultValue;
    CSSStyleDeclaration style = gfxElem.getOwnerSVGElement().getComputedStyle(gfxElem, "");
    CSSValue cssValue = (CSSValue) style.getPropertyCSSValue(queryProperty);
//      logger.debug("CSSValue.getCssText() of proerty " + queryProperty + ": " + cssValue.getCssText());
    if (cssValue != null){
      if (cssValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE){
        try{
          org.w3c.dom.css.CSSPrimitiveValue v = (org.w3c.dom.css.CSSPrimitiveValue)cssValue;
          if (v.getCssValueType() == CSSPrimitiveValue.CSS_NUMBER){
            returnValue = v.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
//            logger.debug(queryProperty + ": " + returnValue);
          }
        }catch(Exception e){
          logger.error(e.getMessage());
        }
      }
    }
    return returnValue;
  }
 

 
  /**
   * Creates a polygon from an SVGOMPolygonElement element.
   *
   * @param polyElem the poly elem
   * @param noFill the no fill
   * @param windingRule the winding rule
   *
   * @return the live polygon component
   */
  private AbstractShape getLivePolygonComponent(SVGOMPolygonElement polyElem, boolean noFill, int windingRule){
    AbstractShape returnComponent = null;
   
    //Create Vertex array from points
      SVGPointList pointList = polyElem.getPoints();
      Vertex[] vertices = new Vertex[pointList.getNumberOfItems()];
      for (int i = 0; i < pointList.getNumberOfItems(); i++) {
      SVGPoint p = pointList.getItem(i);
      vertices[i] = new Vertex(p.getX(), p.getY(),0);
      }
     
      //If polygon isnt closed, close it with the first vertex
      if (!vertices[0].equalsVector(vertices[vertices.length-1])){
        Vertex[] closedVertices = new Vertex[vertices.length+1];
        System.arraycopy(vertices, 0, closedVertices, 0, vertices.length);
        closedVertices[closedVertices.length-1] = (Vertex)vertices[0].getCopy();
        vertices = closedVertices;
      }
     
      int convexity = ConvexityUtil.classifyPolygon2(vertices.length, vertices);
      switch (convexity) {
        case ConvexityUtil.NotConvexDegenerate:
        case ConvexityUtil.NotConvex:
          //If not filled, we dont worry about non-simple polygons
          if (noFill){
            returnComponent = createPoly(vertices);
          }else{
            ArrayList<Vertex[]> contours = new ArrayList<Vertex[]>();
            contours.add(vertices);
//            returnComponent = createStencilPoly(vertices, contours);
            returnComponent = createComplexPoly(contours, windingRule);
          }
          break;
        case ConvexityUtil.ConvexDegenerate:
        case ConvexityUtil.ConvexCW:
        case ConvexityUtil.ConvexCCW:
          returnComponent = createPoly(vertices);
          break;
        default:
          break;
      }
    return returnComponent;
  }
 
  /**
   * Creates a polygon from a SVGOMPathElement.
   *
   * @param pathElem the path elem
   * @param noFill the no fill
   * @param windingRule the winding rule
   *
   * @return the live path component
   */
  private AbstractShape getLivePathComponent(SVGOMPathElement pathElem, boolean noFill, int windingRule){
      AbstractShape returnComponent   = null;
      CustomPathHandler pathHandler   = new CustomPathHandler();
      PathParser pathParser       = new PathParser();
      //pathHandler.setVerbose(true);
     
      /*
      SVGPathSegList pathSegList = pathElem.getPathSegList();
      SVGPathSeg seg = pathSegList.getItem(pathSegList.getNumberOfItems()-1);
      logger.debug(seg.getPathSegTypeAsLetter());
      */
     
      //Parse the "d" attribute
      String dAttValue = pathElem.getAttribute("d");
      pathParser.setPathHandler(pathHandler);
      pathParser.parse(dAttValue);
     
     
        //Get the Vertices of the path
      Vertex[] originalPointsArray = pathHandler.getPathPointsArray();
     
      //Get Sub-Paths
      ArrayList<Vertex[]> contours = pathHandler.getContours();
     
      // For stencil-trick-polygons!! \\
//      /*
      //Get path vertices points
      LinkedList<Vertex> pathPoints = pathHandler.getPathPoints();
     
      if (pathHandler.getReverseMoveToStack().size() <= 1){
        //nicht adden
      }else{
        pathPoints.addAll(pathHandler.getReverseMoveToStack());
      }
      Vertex[] pathVertsStencilPrepared = (Vertex[])pathPoints.toArray(new Vertex[pathPoints.size()]);
//      */
     
      //Check if path vertices are empty
      if (originalPointsArray.length == 0){
        logger.debug("Empty path vertex array -> aborting");
        return null;
      }
     
      //TODO actually should calculate the real vertices from the vezier ones and then check
      //for convexity..else there might be false positives
      Vertex[] v;
//      if (containsBeziers(v)){
//        v = Tools3D.createVertexArrFromBezierArr(originalPointsArray, 11);
//      }
      v = originalPointsArray;
     
      //Check for convexity
      int convexity = ConvexityUtil.classifyPolygon2(v.length, v);
      switch (convexity) {
        case ConvexityUtil.NotConvexDegenerate:
//          logger.debug("not Convex Degenerate");
        case ConvexityUtil.NotConvex:
//          logger.debug("not convex");
          //If not filled, we can createa non stenciled polygon with no filling for better
          //performance
          if (noFill){
            returnComponent = createPoly(originalPointsArray);
          }else{
            returnComponent = createComplexPoly(contours, windingRule);
          }
          break;
        case ConvexityUtil.ConvexDegenerate:
//          logger.debug("convex degenerate");
        case ConvexityUtil.ConvexCW:
//          logger.debug("convex clockwise");
        case ConvexityUtil.ConvexCCW:
//          logger.debug("convex counterclockwise");
          returnComponent = createPoly(originalPointsArray);
          break;
        default:
          break;
      }
     
    return returnComponent;
  }
 
  /**
   * Creates a Stencil-Trick-Polygon.
   *
   * @param stencilPreparedVerts the stencil prepared verts
   * @param subPaths the sub paths
   *
   * @return the abstract shape
   */
  private AbstractShape createStencilPoly(Vertex[] stencilPreparedVerts, ArrayList<Vertex[]> subPaths) {
//    logger.debug("Create stencil poly");
    //Blow up vertex array, that will be used for picking etc
    //to at least be of size == 3 for generating normals
    if (stencilPreparedVerts.length <3){
      Vertex[] newVerts = new Vertex[3];
      if (stencilPreparedVerts.length == 2){
        newVerts[0] = stencilPreparedVerts[0];
        newVerts[1] = stencilPreparedVerts[1];
        newVerts[2] = (Vertex)stencilPreparedVerts[1].getCopy();
        stencilPreparedVerts = newVerts;
      }else if (stencilPreparedVerts.length == 1){
        newVerts[0] = stencilPreparedVerts[0];
        newVerts[1] = (Vertex)stencilPreparedVerts[1].getCopy();
        newVerts[2] = (Vertex)stencilPreparedVerts[1].getCopy();
        stencilPreparedVerts = newVerts;
      }else{
        //ERROR
      }
    }
   
    MTStencilPolygon newShape = new MTStencilPolygon(stencilPreparedVerts, subPaths, pa);
    return newShape;
  }
 
 
  /**
   * Creates a Complex (tesselated) polygon.
   *
   * @param contours the contours
   * @param windingRule the winding rule
   *
   * @return the abstract shape
   */
  private AbstractShape createComplexPoly(ArrayList<Vertex[]> contours, int windingRule) {
    int segments = 10;
    List<Vertex[]> bezierContours = ToolsGeometry.createVertexArrFromBezierVertexArrays(contours, segments);
   
    GluTrianglulator triangulator = new GluTrianglulator(pa);
   
//    MTTriangleMesh mesh = triangulator.toTriangleMesh(bezierContours, windingRule);
   
    triangulator.tesselate(bezierContours, windingRule);
    List<Vertex> tris = triangulator.getTriList();
    Vertex[] verts = tris.toArray(new Vertex[tris.size()]);
    GeometryInfo geom = new GeometryInfo(pa, verts);
   
//    MTTriangleMesh mesh = new MTTriangleMesh(pa, geom);
    MTTriangleMesh mesh = new SVGMesh(pa, geom);
   
    //TODO put outline contourse in own class SVGMesh!
    //not belonging in general mesh class
    mesh.setOutlineContours(bezierContours);
   
    triangulator.deleteTess(); //Delete triangulator (C++ object)
    return mesh;
  }
 

 
 
  private class SvgPolygon extends MTPolygon{
    public SvgPolygon(Vertex[] vertices, PApplet applet) {
      super(vertices, applet);
    }
   
    protected IBoundingShape computeDefaultBounds() {
      //Use z plane bounding rect instead default boundingsphere for svg!
      return new BoundsZPlaneRectangle(this);
    }
   
    @Override
    protected void setDefaultGestureActions() {
     
    }
  }
 
  private class SVGMesh extends MTTriangleMesh{
    public SVGMesh(PApplet applet, GeometryInfo geometryInfo) {
      super(applet, geometryInfo, false);
    }
   
    @Override
    protected IBoundingShape computeDefaultBounds() {
      //Use z plane bounding rect instead default boundingsphere for svg!
      return new BoundsZPlaneRectangle(this);
    }
   
    @Override
    protected void setDefaultGestureActions() {
     
    }
  }
 
 
 
 
//  /**
//   * Creates a Complex (tesselated) polygon
//   * @param contours
//   * @param windingRule
//   * @return
//   */
//  private AbstractShape createComplexPoly(ArrayList<Vertex[]> contours, int windingRule) {
////    logger.debug("Create createComplexPoly poly");
////    if (contours.get(0).length <3)
////      logger.error("<3");
//   
//    //Blow up first contour, that will be used for picking etc
//    //to at least be of size == 3 for generating normals
//    if (contours.get(0).length <3){
//      if (contours.get(0).length == 2){
//        Vertex[] v = new Vertex[3];
//        v[0] = contours.get(0)[0];
//        v[1] = contours.get(0)[1];
//        v[2] = (Vertex)contours.get(0)[1].getCopy();
//        Vertex[] c = contours.get(0);
//        c = v;
//      }else if (contours.get(0).length == 1){
//        Vertex[] v = new Vertex[3];
//        v[0] = contours.get(0)[0];
//        v[1] = (Vertex)contours.get(0)[0].getCopy();
//        v[2] = (Vertex)contours.get(0)[0].getCopy();
//        Vertex[] c = contours.get(0);
//        c = v;
//      }else{
//        //ERROR
//      }
//    }
//   
//    MTComplexPolygon newShape = new MTComplexPolygon(contours, pa);
//    newShape.setWindingRule(windingRule);
//    return newShape;
//  }

  /**
   * Creates a plain, normal polygon.
   *
   * @param vertices the vertices
   *
   * @return the abstract shape
   */
  private AbstractShape createPoly(Vertex[] vertices) {
//    logger.debug("Create poly");
    Vertex[] verts = vertices;

    if (ToolsGeometry.containsBezierVertices(verts))
      verts = ToolsGeometry.createVertexArrFromBezierArr(verts, 13);
   
    //Blow up vertex array, that will be used for picking etc
    //to at least be of size == 3 for generating normals
    if (verts.length <3){
      Vertex[] newVerts = new Vertex[3];
      if (verts.length == 2){
        newVerts[0] = verts[0];
        newVerts[1] = verts[1];
        newVerts[2] = (Vertex)verts[1].getCopy();
        verts = newVerts;
      }else if (verts.length == 1){
        newVerts[0] = verts[0];
        newVerts[1] = (Vertex)verts[0].getCopy();
        newVerts[2] = (Vertex)verts[0].getCopy();
        verts = newVerts;
      }else{
        //ERROR
      }
    }
   
    //For lines or polygons do this
//    MTPolygon poly = new MTPolygon(verts , pa);
    MTPolygon poly = new SvgPolygon(verts,pa);
    return poly;
  }
 
  /**
   * Checks whether the given element is located inside a clip-path element.
   * Used to determine whether to draw the component or not.
   *
   * @param element the element
   *
   * @return true, if checks if is under clip path
   */
  private boolean isUnderClipPath(Node element){
    if (element.getParentNode() == null)
      return false;
    while (element.getParentNode() != null ) {
      Node parent = element.getParentNode();
      if (parent.getNodeName().equals(SVG_CLIP_PATH_TAG))
        return true;
      element = parent;
    }
    return false;
  }
 
 
 
  /**
   * Gets the inherited opacity.
   *
   * @param svgElem the svg elem
   * @param element the element
   *
   * @return the inherited opacity
   */
  private float getInheritedOpacity(SVGSVGElement svgElem, Node element){
    float returnOpactiy = 1.0f;
    if (element.getParentNode() == null)
      return returnOpactiy;
   
    //Get attribute of this element
    try{
      if (element instanceof SVGGraphicsElement){
        SVGGraphicsElement gfx = (SVGGraphicsElement)element;
        float opacity = ((CSSPrimitiveValue)svgElem.getComputedStyle(gfx, "").getPropertyCSSValue(("opacity"))).getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
        returnOpactiy *= opacity;
        logger.debug(gfx.getTagName() + ": found opacity: " + opacity);
      }
    }catch(Exception e){
      e.printStackTrace();
    }
    while (element.getParentNode() != null ) {
      Node parent = element.getParentNode();
      try{
        if (parent instanceof SVGGraphicsElement){
          SVGGraphicsElement gfx = (SVGGraphicsElement)parent;
          float opacity = ((CSSPrimitiveValue)svgElem.getComputedStyle(gfx, "").getPropertyCSSValue(("opacity"))).getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
          returnOpactiy *= opacity;
          logger.debug(gfx.getTagName() + ": found opacity: " + opacity);
        }
      }catch(Exception e){
        e.printStackTrace();
      }
      element = parent;
    }
    return returnOpactiy;
  }
 
 

}
TOP

Related Classes of org.mt4j.util.xml.svg.SVGLoader$SVGMesh

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.