/***********************************************************************
* 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.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import org.apache.batik.parser.ParseException;
import org.apache.batik.parser.PathHandler;
import org.mt4j.util.math.BezierVertex;
import org.mt4j.util.math.ToolsGeometry;
import org.mt4j.util.math.Vertex;
/**
* The Class CustomPathHandler.
*/
public class CustomPathHandler implements PathHandler {
/** The verbose. */
private boolean verbose;
/** The cubic bez vert to quadric control point. */
private HashMap<BezierVertex, Vertex> cubicBezVertTOQuadricControlPoint;
/** The reverse move to stack. */
private Stack<Vertex> reverseMoveToStack;
/** The sub paths. */
private ArrayList<Vertex[]> subPaths;
/** The current sub path. */
private ArrayList<Vertex> currentSubPath;
/** The path points. */
private LinkedList<Vertex> pathPoints;
/**
* Instantiates a new custom path handler.
*/
public CustomPathHandler(){
pathPoints = new LinkedList<Vertex>();
currentSubPath = new ArrayList<Vertex>();
subPaths = new ArrayList<Vertex[]>();
reverseMoveToStack = new Stack<Vertex>();
verbose = false;
//Because all quadric curves get converted to cubics,
//but the original quadric controlpoint is needed for "T" tag for reflecting
cubicBezVertTOQuadricControlPoint = new HashMap<BezierVertex, Vertex>();
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#startPath()
*/
public void startPath() throws ParseException {
if (verbose)
System.out.println("Start Path");
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#movetoAbs(float, float)
*/
public void movetoAbs(float x, float y) throws ParseException {
if (verbose)
System.out.println("movetoAbs: x:" + x + " y:" + y);
//If the last contour wasnt closed with Z,
//we save the last contour here,but without closing it
if (!currentSubPath.isEmpty()){
Vertex[] currentSplitPathArr = (Vertex[])currentSubPath.toArray(new Vertex[currentSubPath.size()]);
subPaths.add(currentSplitPathArr);
currentSubPath.clear();
}
Vertex moveTo = new Vertex(x,y,0);
pathPoints.add(moveTo);
currentSubPath.add(moveTo);
reverseMoveToStack.push((Vertex)moveTo.getCopy());
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#movetoRel(float, float)
*/
public void movetoRel(float x, float y) throws ParseException {
if (verbose)
System.out.println("movetoRel: " + x + "," + y);
//If the last contour wasnt closed with Z,
//we save the last contour here, without closing it
if (!currentSubPath.isEmpty()){
Vertex[] currentSplitPathArr = (Vertex[])currentSubPath.toArray(new Vertex[currentSubPath.size()]);
subPaths.add(currentSplitPathArr);
currentSubPath.clear();
}
Vertex moveTo;
if (!pathPoints.isEmpty() && pathPoints.getLast() != null){
moveTo = new Vertex(pathPoints.getLast().getX() + x, pathPoints.getLast().getY() + y, 0);
}else{
moveTo = new Vertex(x, y, 0);
}
pathPoints.add(moveTo);
currentSubPath.add(moveTo);
reverseMoveToStack.push((Vertex)moveTo.getCopy());
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#arcAbs(float, float, float, boolean, boolean, float, float)
*/
public void arcAbs(float rx, float ry, float phi, boolean large_arc, boolean sweep, float x, float y) throws ParseException {
if (verbose)
System.out.println("arcAbs: " + rx + " " + ry + " " + phi + " " + large_arc + " " + sweep + " " + x + " " + y);
Vertex lastPoint = pathPoints.getLast();
List<Vertex> arcVertices = ToolsGeometry.arcTo(lastPoint.x, lastPoint.y, rx, ry, phi, large_arc, sweep, x, y, 40);
//Prevent odd picking behavour, in which the normal is
//not correctly computed, because the 2 points are the same
if (!arcVertices.isEmpty()
&& lastPoint != null
&& arcVertices.get(0).equalsVector(lastPoint)
){
arcVertices.remove(0);
}
pathPoints.addAll(arcVertices);
currentSubPath.addAll(arcVertices);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#arcRel(float, float, float, boolean, boolean, float, float)
*/
public void arcRel(float rx, float ry, float phi, boolean large_arc, boolean sweep, float x, float y) throws ParseException {
if (verbose)
System.out.println("arcRel: " + rx + " " + ry + " " + phi + " " + large_arc + " " + sweep + " " + x + " " + y);
Vertex lastPoint = pathPoints.getLast();
List<Vertex> arcVertices = ToolsGeometry.arcTo(lastPoint.x, lastPoint.y, rx, ry, phi, large_arc, sweep, lastPoint.x+x, lastPoint.y+y, 40);
//Prevent odd picking behavour, in which the normal is
//not correctly computed, because the 2 points are the same
if (!arcVertices.isEmpty()
&& lastPoint != null
&& arcVertices.get(0).equalsVector(lastPoint)
){
arcVertices.remove(0);
}
pathPoints.addAll(arcVertices);
currentSubPath.addAll(arcVertices);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#linetoAbs(float, float)
*/
public void linetoAbs(float x, float y) throws ParseException {
if (verbose)
System.out.println("linetoAbs x:" + x + " y:" + y);
Vertex vert = new Vertex(x,y,0);
pathPoints.add(vert);
currentSubPath.add(vert);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#linetoRel(float, float)
*/
public void linetoRel(float x, float y) throws ParseException {
if (verbose)
System.out.println("linetoRel: " + x + "," + y);
Vertex vert = new Vertex(pathPoints.getLast().getX() + x, pathPoints.getLast().getY() + y, 0);
pathPoints.add(vert);
currentSubPath.add(vert);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#linetoHorizontalAbs(float)
*/
public void linetoHorizontalAbs(float x) throws ParseException {
if (verbose)
System.out.println("linetoHorizontalAbs x:" + x);
Vertex vert = new Vertex(x, pathPoints.getLast().getY(), 0);
pathPoints.add(vert);
currentSubPath.add(vert);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#linetoHorizontalRel(float)
*/
public void linetoHorizontalRel(float x) throws ParseException {
if (verbose)
System.out.println("linetoHorizontalRel: " + x);
Vertex vert = new Vertex(pathPoints.getLast().getX() + x, pathPoints.getLast().getY(), 0);
pathPoints.add(vert);
currentSubPath.add(vert);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#linetoVerticalAbs(float)
*/
public void linetoVerticalAbs(float y) throws ParseException {
if (verbose)
System.out.println("linetoVerticalAbs y:" + y);
Vertex vert = new Vertex(pathPoints.getLast().getX(), y, 0);
pathPoints.add(vert);
currentSubPath.add(vert);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#linetoVerticalRel(float)
*/
public void linetoVerticalRel(float y) throws ParseException {
if (verbose)
System.out.println("linetoVerticalRel: " + y);
Vertex vert = new Vertex(pathPoints.getLast().getX(), pathPoints.getLast().getY() + y, 0);
pathPoints.add(vert);
currentSubPath.add(vert);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoQuadraticAbs(float, float, float, float)
*/
public void curvetoQuadraticAbs(float x1, float y1, float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoQuadraticAbs x1:" + x1 + " y1:" + y1 + " x:" + x+ " y:" + y);
if (!pathPoints.isEmpty() && pathPoints.getLast() != null){
Vertex lastEndPoint = new Vertex(pathPoints.getLast().getX(), pathPoints.getLast().getY(), pathPoints.getLast().getZ());
Vertex quadControlPoint = new Vertex(x1,y1,0);
//Put in startPoint = last QuadTo Endpoint of this smoothQuadTo, the calculated control point, and the endpoint of smoothQuadTo
BezierVertex b5 = ToolsGeometry.getCubicFromQuadraticCurve(lastEndPoint, quadControlPoint , new Vertex(x, y, 0));
cubicBezVertTOQuadricControlPoint.put(b5, quadControlPoint);
pathPoints.add(b5);
currentSubPath.add(b5);
}else{
System.err.println("last point = null at curvetoQuadraticAbs");
}
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoQuadraticRel(float, float, float, float)
*/
public void curvetoQuadraticRel(float x1, float y1, float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoQuadraticRel: " + x1 + "," + y1 + " " + x + "," + y);
if (!pathPoints.isEmpty() && pathPoints.getLast() != null){
Vertex lastPoint = pathPoints.getLast();
Vertex lastEndPoint = new Vertex(lastPoint.getX(), lastPoint.getY(), lastPoint.getZ());
Vertex quadControlPoint = new Vertex(lastPoint.getX() + x1, lastPoint.getY()+ y1, 0);
//Put in startPoint = last QuadTo Endpoint of this smoothQuadTo, the calculated control point, and the endpoint of smoothQuadTo
BezierVertex b5 = ToolsGeometry.getCubicFromQuadraticCurve(
lastEndPoint,
quadControlPoint ,
new Vertex(lastPoint.getX() + x, lastPoint.getY()+ y, 0));
cubicBezVertTOQuadricControlPoint.put(b5, quadControlPoint);
pathPoints.add(b5);
currentSubPath.add(b5);
}else{
System.out.println("last point null at curvetoQuadraticRel");
}
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoQuadraticSmoothAbs(float, float)
*/
public void curvetoQuadraticSmoothAbs(float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoQuadraticSmoothAbs " + " x:" + x+ " y:" + y);
Vertex lastPoint = pathPoints.getLast();
if (lastPoint instanceof BezierVertex && cubicBezVertTOQuadricControlPoint.get(lastPoint) != null){
Vertex lastEndPoint = new Vertex(pathPoints.getLast().getX(), pathPoints.getLast().getY(), pathPoints.getLast().getZ());
//Get control point of last QuadTo
Vertex lastQuadControlPoint = cubicBezVertTOQuadricControlPoint.get(lastPoint);
cubicBezVertTOQuadricControlPoint.remove(lastPoint);
//Rotate that controlpoint around the end point of the last QuadTo
lastQuadControlPoint.rotateZ(lastEndPoint, 180);
//Put in startPoint = last QuadTo Endpoint of this smoothQuadTo, the calculated control point, and the endpoint of smoothQuadTo
BezierVertex b5 = ToolsGeometry.getCubicFromQuadraticCurve(lastEndPoint, lastQuadControlPoint , new Vertex(x, y, 0));
//Save last quad control point
cubicBezVertTOQuadricControlPoint.put(b5, lastQuadControlPoint);
pathPoints.add(b5);
currentSubPath.add(b5);
}else{
if (verbose)
System.out.println("Couldnt get last controlpoint at: curvetoQuadraticSmoothAbs - using last point as controlpoint");
//If we couldnt retrieve the controlpoint of the current point,
//we use the current point as the new controlpoint
Vertex lastEndPoint = new Vertex(lastPoint.getX(),lastPoint.getY(),0);
Vertex quadControlPoint = new Vertex(lastPoint.getX(),lastPoint.getY(),0);
BezierVertex b5 = ToolsGeometry.getCubicFromQuadraticCurve(
lastEndPoint,
quadControlPoint ,
new Vertex(x, y, 0));
cubicBezVertTOQuadricControlPoint.put(b5, quadControlPoint);
pathPoints.add(b5);
currentSubPath.add(b5);
}
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoQuadraticSmoothRel(float, float)
*/
public void curvetoQuadraticSmoothRel(float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoQuadraticSmoothRel: " + x + "," + y);
Vertex lastPoint = pathPoints.getLast();
if (lastPoint instanceof BezierVertex && cubicBezVertTOQuadricControlPoint.get(lastPoint) != null){
Vertex lastEndPoint = new Vertex(pathPoints.getLast().getX(), pathPoints.getLast().getY(), pathPoints.getLast().getZ());
//Get control point of last QuadTo
Vertex lastQuadControlPoint = cubicBezVertTOQuadricControlPoint.get(lastPoint);
cubicBezVertTOQuadricControlPoint.remove(lastPoint);
//Rotate that controlpoint around the end point of the last QuadTo
lastQuadControlPoint.rotateZ(lastEndPoint, 180);
//Put in startPoint = last QuadTo Endpoint of this smoothQuadTo, the calculated control point, and the endpoint of smoothQuadTo
BezierVertex b5 = ToolsGeometry.getCubicFromQuadraticCurve(
lastEndPoint,
lastQuadControlPoint ,
new Vertex(lastPoint.getX() + x, lastPoint.getY() + y, 0));
//Save last quad control point
cubicBezVertTOQuadricControlPoint.put(b5, lastQuadControlPoint);
pathPoints.add(b5);
currentSubPath.add(b5);
}else{
if (verbose)
System.out.println("couldnt get last control point at curvetoQuadraticSmoothRel - using last point as the control point");
Vertex lastEndPoint = new Vertex(lastPoint.getX(),lastPoint.getY(),0);
Vertex quadControlPoint = new Vertex(lastPoint.getX(),lastPoint.getY(),0);
BezierVertex b5 = ToolsGeometry.getCubicFromQuadraticCurve(
lastEndPoint,
quadControlPoint ,
new Vertex(lastPoint.getX() + x, lastPoint.getY() + y, 0));
cubicBezVertTOQuadricControlPoint.put(b5, quadControlPoint);
pathPoints.add(b5);
currentSubPath.add(b5);
}
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoCubicAbs(float, float, float, float, float, float)
*/
public void curvetoCubicAbs(float x1, float y1, float x2, float y2, float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoCubicAbs x1:" + x1 + " y1:" + y1 + " x2:" + x2 + " y2:" + y2 + " x:" + x+ " y:" + y);
BezierVertex b = new BezierVertex(x1,y1,0, x2,y2,0, x,y,0);
pathPoints.add(b);
currentSubPath.add(b);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoCubicRel(float, float, float, float, float, float)
*/
public void curvetoCubicRel(float x1, float y1, float x2, float y2, float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoCubicSmoothRel: " + x1 + "," + y1 + " " + x2 + "," + y2 + " " + x + "," + y);
Vertex lastPoint = pathPoints.getLast();
BezierVertex b = new BezierVertex(
lastPoint.getX()+ x1, lastPoint.getY() + y1,0,
lastPoint.getX()+ x2, lastPoint.getY() + y2,0,
lastPoint.getX()+ x, lastPoint.getY() + y,0);
pathPoints.add(b);
currentSubPath.add(b);
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoCubicSmoothAbs(float, float, float, float)
*/
public void curvetoCubicSmoothAbs(float x2, float y2, float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoCubicSmoothAbs x2:" + x2 + " y2:" + y2 + " x:" + x+ " y:" + y);
Vertex lastPoint = pathPoints.getLast();
if (lastPoint instanceof BezierVertex){
BezierVertex lastBez = (BezierVertex)lastPoint;
Vertex lastConPointCopy = (Vertex)lastBez.getSecondCtrlPoint().getCopy();
//reflect the last controlpoint at the current point
lastConPointCopy.rotateZ(lastPoint, 180);
BezierVertex b = new BezierVertex(lastConPointCopy.getX(),lastConPointCopy.getY(),0, x2,y2,0, x,y,0);
pathPoints.add(b);
currentSubPath.add(b);
}else{
if (verbose)
System.out.println("Couldnt get last controlpoint at: curvetoCubicSmoothAbs - using last point as first controlpoint");
Vertex lastEndPoint = new Vertex(lastPoint.getX(),lastPoint.getY(),0);
BezierVertex b = new BezierVertex(
lastEndPoint.getX(),lastEndPoint.getY(),0,
x2,y2,0,
x,y,0);
pathPoints.add(b);
currentSubPath.add(b);
}
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#curvetoCubicSmoothRel(float, float, float, float)
*/
public void curvetoCubicSmoothRel(float x2, float y2, float x, float y) throws ParseException {
if (verbose)
System.out.println("curvetoCubicSmoothRel: " + x2 + "," + y2 + " " + x + "," + y);
Vertex lastPoint = pathPoints.getLast();
if (lastPoint instanceof BezierVertex){
BezierVertex lastBez = (BezierVertex)lastPoint;
Vertex lastConPointCopy = (Vertex)lastBez.getSecondCtrlPoint().getCopy();
//reflect the last controlpoint at the current point
lastConPointCopy.rotateZ(lastPoint, 180);
BezierVertex b = new BezierVertex(
lastConPointCopy.getX() , lastConPointCopy.getY(), 0,
lastPoint.getX() + x2, lastPoint.getY() + y2, 0,
lastPoint.getX() + x, lastPoint.getY() + y, 0);
pathPoints.add(b);
currentSubPath.add(b);
}else{
if (verbose)
System.out.println("Couldnt get last controlpoint at: curvetoCubicSmoothRel - using last point as first controlpoint");
Vertex lastEndPoint = new Vertex(lastPoint.getX(),lastPoint.getY(),0);
BezierVertex b = new BezierVertex(
lastEndPoint.getX(),lastEndPoint.getY(),0,
lastEndPoint.getX()+ x2, lastEndPoint.getY()+ y2, 0,
lastEndPoint.getX()+ x, lastEndPoint.getY()+ y, 0);
pathPoints.add(b);
currentSubPath.add(b);
}
}
/**
* if "Z" is encountered.
*
* @throws ParseException the parse exception
*/
public void closePath() throws ParseException {
if (verbose)
System.out.println("close Path");
//Close the current contour with the previous MoveTo Vertex
Vertex lastPointCopy = (Vertex)currentSubPath.get(0).getCopy();
currentSubPath.add(lastPointCopy);
pathPoints.add(lastPointCopy);
//Save the current contour and clear the current for the next contour
Vertex[] currentSplitPathArr = (Vertex[])currentSubPath.toArray(new Vertex[currentSubPath.size()]);
subPaths.add(currentSplitPathArr);
currentSubPath.clear();
}
/* (non-Javadoc)
* @see org.apache.batik.parser.PathHandler#endPath()
*/
public void endPath() throws ParseException {
if (verbose)
System.out.println("End Path");
//IF no Z command occured, were normally the last contour gets added,
//we have save the current contour here, but we dont close it
if (!currentSubPath.isEmpty()){
//Convert partial path list to array
Vertex[] currentSplitPathArr = (Vertex[])currentSubPath.toArray(new Vertex[currentSubPath.size()]);
//Add partial path array to list of all partial paths of this glyph
subPaths.add(currentSplitPathArr);
currentSubPath.clear();
}
}
/**
* Gets the path points.
*
* @return the path points
*/
public LinkedList<Vertex> getPathPoints() {
return pathPoints;
}
/**
* Gets the path points array.
*
* @return the path points array
*/
public Vertex[] getPathPointsArray() {
return (Vertex[])pathPoints.toArray(new Vertex[pathPoints.size()]);
}
/**
* @deprecated Only used when the intention is to draw the shapes using the stencil buffer..
*
* @return the reverse move to vertices
*/
public Vertex[] getReverseMoveToVertices() {
return (Vertex[])reverseMoveToStack.toArray(new Vertex[reverseMoveToStack.size()]);
}
/**
* @deprecated Only used when the intention is to draw the shapes using the stencil buffer..
* @return the reverse move to stack
*/
public Stack<Vertex> getReverseMoveToStack() {
return reverseMoveToStack;
}
/**
* Returns all the encountered "sub-paths" of the parsed path-element.
*
* @return the contours
*/
public ArrayList<Vertex[]> getContours() {
return subPaths;
}
/**
* Sets the verbose.
*
* @param verbose the new verbose
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
}