mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-14 04:24:05 +09:00
added sources for Slick
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
162
lib/slick-source/org/newdawn/slick/svg/Diagram.java
Normal file
162
lib/slick-source/org/newdawn/slick/svg/Diagram.java
Normal file
@@ -0,0 +1,162 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A diagram read from SVG containing multiple figures
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class Diagram {
|
||||
/** The figures in the diagram */
|
||||
private ArrayList figures = new ArrayList();
|
||||
/** The pattern definitions */
|
||||
private HashMap patterns = new HashMap();
|
||||
/** The linear gradients defined within the diagram */
|
||||
private HashMap gradients = new HashMap();
|
||||
/** The figures mapping */
|
||||
private HashMap figureMap = new HashMap();
|
||||
|
||||
/** The width of the diagram */
|
||||
private float width;
|
||||
/** The height of the diagram */
|
||||
private float height;
|
||||
|
||||
/**
|
||||
* Create a new empty diagram
|
||||
*
|
||||
* @param width The width of the diagram
|
||||
* @param height The height of the diagram
|
||||
*/
|
||||
public Diagram(float width, float height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the diagram
|
||||
*
|
||||
* @return The width of the diagram
|
||||
*/
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the diagram
|
||||
*
|
||||
* @return The height of the diagram
|
||||
*/
|
||||
public float getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pattern definition basd on a image
|
||||
*
|
||||
* @param name The name of the pattern
|
||||
* @param href The href to the image specified in the doc
|
||||
*/
|
||||
public void addPatternDef(String name, String href) {
|
||||
patterns.put(name, href);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add gradient to the diagram
|
||||
*
|
||||
* @param name The name of the gradient
|
||||
* @param gradient The gradient to be added
|
||||
*/
|
||||
public void addGradient(String name, Gradient gradient) {
|
||||
gradients.put(name, gradient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pattern definition from the diagram
|
||||
*
|
||||
* @param name The name of the pattern
|
||||
* @return The href to the image that was specified for the given pattern
|
||||
*/
|
||||
public String getPatternDef(String name) {
|
||||
return (String) patterns.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gradient defined in this document
|
||||
*
|
||||
* @param name The name of the gradient
|
||||
* @return The gradient definition
|
||||
*/
|
||||
public Gradient getGradient(String name) {
|
||||
return (Gradient) gradients.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the names of the patterns defined
|
||||
*
|
||||
* @return The names of the pattern
|
||||
*/
|
||||
public String[] getPatternDefNames() {
|
||||
return (String[]) patterns.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a figure by a given ID
|
||||
*
|
||||
* @param id The ID of the figure
|
||||
* @return The figure with the given ID
|
||||
*/
|
||||
public Figure getFigureByID(String id) {
|
||||
return (Figure) figureMap.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a figure to the diagram
|
||||
*
|
||||
* @param figure The figure to add
|
||||
*/
|
||||
public void addFigure(Figure figure) {
|
||||
figures.add(figure);
|
||||
figureMap.put(figure.getData().getAttribute(NonGeometricData.ID), figure);
|
||||
|
||||
String fillRef = figure.getData().getAsReference(NonGeometricData.FILL);
|
||||
Gradient gradient = getGradient(fillRef);
|
||||
if (gradient != null) {
|
||||
if (gradient.isRadial()) {
|
||||
for (int i=0;i<InkscapeLoader.RADIAL_TRIANGULATION_LEVEL;i++) {
|
||||
figure.getShape().increaseTriangulation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of figures in the diagram
|
||||
*
|
||||
* @return The number of figures in the diagram
|
||||
*/
|
||||
public int getFigureCount() {
|
||||
return figures.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the figure at a given index
|
||||
*
|
||||
* @param index The index of the figure to retrieve
|
||||
* @return The figure at the given index
|
||||
*/
|
||||
public Figure getFigure(int index) {
|
||||
return (Figure) figures.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a figure from the diagram
|
||||
*
|
||||
* @param figure The figure to be removed
|
||||
*/
|
||||
public void removeFigure(Figure figure) {
|
||||
figures.remove(figure);
|
||||
figureMap.remove(figure.getData().getAttribute("id"));
|
||||
}
|
||||
}
|
||||
84
lib/slick-source/org/newdawn/slick/svg/Figure.java
Normal file
84
lib/slick-source/org/newdawn/slick/svg/Figure.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
|
||||
/**
|
||||
* A figure that is part of diagram loaded from SVG
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class Figure {
|
||||
/** Ellipse Type */
|
||||
public static final int ELLIPSE = 1;
|
||||
/** Line Type */
|
||||
public static final int LINE = 2;
|
||||
/** Rectangle Type */
|
||||
public static final int RECTANGLE = 3;
|
||||
/** Path Type */
|
||||
public static final int PATH = 4;
|
||||
/** Polygon Type */
|
||||
public static final int POLYGON = 5;
|
||||
|
||||
/** The type of this figure */
|
||||
private int type;
|
||||
|
||||
/** The geometric shape of the figure */
|
||||
private Shape shape;
|
||||
/** The other bits of data assocaited with the SVG element */
|
||||
private NonGeometricData data;
|
||||
/** The transform that has already been applied to the shape */
|
||||
private Transform transform;
|
||||
|
||||
/**
|
||||
* Create a new figure
|
||||
*
|
||||
* @param type The type of the figure
|
||||
* @param shape The shape of the figure
|
||||
* @param data The other associated data
|
||||
* @param transform The transform that was applied to the shape
|
||||
*/
|
||||
public Figure(int type, Shape shape, NonGeometricData data, Transform transform) {
|
||||
this.shape = shape;
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transform that was applied to the shape given in the SVG
|
||||
* to get it to it's currently state
|
||||
*
|
||||
* @return The transform specified in the SVG
|
||||
*/
|
||||
public Transform getTransform() {
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of this figure
|
||||
*
|
||||
* @return The type of this figure
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shape of this figure
|
||||
*
|
||||
* @return The shape of this figure
|
||||
*/
|
||||
public Shape getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data associated with this figure
|
||||
*
|
||||
* @return The data associated with this figure
|
||||
*/
|
||||
public NonGeometricData getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
289
lib/slick-source/org/newdawn/slick/svg/Gradient.java
Normal file
289
lib/slick-source/org/newdawn/slick/svg/Gradient.java
Normal file
@@ -0,0 +1,289 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.ImageBuffer;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
|
||||
/**
|
||||
* A gradient definition from an SVG file, includes the stops, name and transform.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class Gradient {
|
||||
/** The name/id given to the gradient */
|
||||
private String name;
|
||||
/** The steps in colour of the gradient */
|
||||
private ArrayList steps = new ArrayList();
|
||||
/** The first x coordiante given in the gradient (cx in radial) */
|
||||
private float x1;
|
||||
/** The second x coordiante given in the gradient (fx in radial) */
|
||||
private float x2;
|
||||
/** The first y coordiante given in the gradient (cy in radial) */
|
||||
private float y1;
|
||||
/** The first y coordiante given in the gradient (fy in radial) */
|
||||
private float y2;
|
||||
/** The radius given if any */
|
||||
private float r;
|
||||
/** The texture representing this gradient */
|
||||
private Image image;
|
||||
/** True if this gradient is radial in nature */
|
||||
private boolean radial;
|
||||
/** The transform specified for the gradient */
|
||||
private Transform transform;
|
||||
/** The name of the referenced gradient */
|
||||
private String ref;
|
||||
|
||||
/**
|
||||
* Create a new gradient definition
|
||||
*
|
||||
* @param name The name of the gradient
|
||||
* @param radial True if the gradient is radial
|
||||
*/
|
||||
public Gradient(String name, boolean radial) {
|
||||
this.name = name;
|
||||
this.radial = radial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the gradient is radial
|
||||
*
|
||||
* @return True if the gradient is radial
|
||||
*/
|
||||
public boolean isRadial() {
|
||||
return radial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transform given for this definition
|
||||
*
|
||||
* @param trans The transform given for this definition
|
||||
*/
|
||||
public void setTransform(Transform trans) {
|
||||
this.transform = trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transform to apply during this gradient application
|
||||
*
|
||||
* @return The transform given for this gradient
|
||||
*/
|
||||
public Transform getTransform() {
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference another gradient, i.e. use it's colour stops
|
||||
*
|
||||
* @param ref The name of the other gradient to reference
|
||||
*/
|
||||
public void reference(String ref) {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the gradient reference
|
||||
*
|
||||
* @param diagram The diagram to resolve against
|
||||
*/
|
||||
public void resolve(Diagram diagram) {
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gradient other = diagram.getGradient(ref);
|
||||
|
||||
for (int i=0;i<other.steps.size();i++) {
|
||||
steps.add(other.steps.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the image used for texturing the gradient across shapes
|
||||
*/
|
||||
public void genImage() {
|
||||
if (image == null) {
|
||||
ImageBuffer buffer = new ImageBuffer(128,16);
|
||||
for (int i=0;i<128;i++) {
|
||||
Color col = getColorAt(i / 128.0f);
|
||||
for (int j=0;j<16;j++) {
|
||||
buffer.setRGBA(i, j, col.getRedByte(), col.getGreenByte(), col.getBlueByte(), col.getAlphaByte());
|
||||
}
|
||||
}
|
||||
image = buffer.getImage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image generated for this gradient
|
||||
*
|
||||
* @return The image generated for the gradient
|
||||
*/
|
||||
public Image getImage() {
|
||||
genImage();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the radius given in the SVG
|
||||
*
|
||||
* @param r The radius for radial gradients
|
||||
*/
|
||||
public void setR(float r) {
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the first x value given for the gradient (cx in the case of radial)
|
||||
*
|
||||
* @param x1 The first x value given for the gradient
|
||||
*/
|
||||
public void setX1(float x1) {
|
||||
this.x1 = x1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the second x value given for the gradient (fx in the case of radial)
|
||||
*
|
||||
* @param x2 The second x value given for the gradient
|
||||
*/
|
||||
public void setX2(float x2) {
|
||||
this.x2 = x2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the first y value given for the gradient (cy in the case of radial)
|
||||
*
|
||||
* @param y1 The first y value given for the gradient
|
||||
*/
|
||||
public void setY1(float y1) {
|
||||
this.y1 = y1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the second y value given for the gradient (fy in the case of radial)
|
||||
*
|
||||
* @param y2 The second y value given for the gradient
|
||||
*/
|
||||
public void setY2(float y2) {
|
||||
this.y2 = y2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the radius value given for this gradient
|
||||
*
|
||||
* @return The radius value given for this gradient
|
||||
*/
|
||||
public float getR() {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first x value given for this gradient (cx in the case of radial)
|
||||
*
|
||||
* @return The first x value given for this gradient
|
||||
*/
|
||||
public float getX1() {
|
||||
return x1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the second x value given for this gradient (fx in the case of radial)
|
||||
*
|
||||
* @return The second x value given for this gradient
|
||||
*/
|
||||
public float getX2() {
|
||||
return x2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first y value given for this gradient (cy in the case of radial)
|
||||
*
|
||||
* @return The first y value given for this gradient
|
||||
*/
|
||||
public float getY1() {
|
||||
return y1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the second y value given for this gradient (fy in the case of radial)
|
||||
*
|
||||
* @return The second y value given for this gradient
|
||||
*/
|
||||
public float getY2() {
|
||||
return y2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a colour step/stop to the gradient
|
||||
*
|
||||
* @param location The location on the gradient the colour affects
|
||||
* @param c The color to apply
|
||||
*/
|
||||
public void addStep(float location, Color c) {
|
||||
steps.add(new Step(location, c));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the intepolated colour at the given location on the gradient
|
||||
*
|
||||
* @param p The point of the gradient (0 >= n >= 1)
|
||||
* @return The interpolated colour at the given location
|
||||
*/
|
||||
public Color getColorAt(float p) {
|
||||
if (p <= 0) {
|
||||
return ((Step) steps.get(0)).col;
|
||||
}
|
||||
if (p > 1) {
|
||||
return ((Step) steps.get(steps.size()-1)).col;
|
||||
}
|
||||
|
||||
for (int i=1;i<steps.size();i++) {
|
||||
Step prev = ((Step) steps.get(i-1));
|
||||
Step current = ((Step) steps.get(i));
|
||||
|
||||
if (p <= current.location) {
|
||||
float dis = current.location - prev.location;
|
||||
p -= prev.location;
|
||||
float v = p / dis;
|
||||
|
||||
Color c = new Color(1,1,1,1);
|
||||
c.a = (prev.col.a * (1 - v)) + (current.col.a * (v));
|
||||
c.r = (prev.col.r * (1 - v)) + (current.col.r * (v));
|
||||
c.g = (prev.col.g * (1 - v)) + (current.col.g * (v));
|
||||
c.b = (prev.col.b * (1 - v)) + (current.col.b * (v));
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// shouldn't ever happen
|
||||
return Color.black;
|
||||
}
|
||||
|
||||
/**
|
||||
* The description of a single step on the gradient
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
private class Step {
|
||||
/** The location on the gradient */
|
||||
float location;
|
||||
/** The colour applied */
|
||||
Color col;
|
||||
|
||||
/**
|
||||
* Create a new step
|
||||
*
|
||||
* @param location The location on the gradient the colour affects
|
||||
* @param c The colour to apply
|
||||
*/
|
||||
public Step(float location, Color c) {
|
||||
this.location = location;
|
||||
this.col = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
226
lib/slick-source/org/newdawn/slick/svg/InkscapeLoader.java
Normal file
226
lib/slick-source/org/newdawn/slick/svg/InkscapeLoader.java
Normal file
@@ -0,0 +1,226 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.inkscape.DefsProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.ElementProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.EllipseProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.GroupProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.LineProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.PathProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.PolygonProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.RectProcessor;
|
||||
import org.newdawn.slick.svg.inkscape.UseProcessor;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.EntityResolver;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* A loader specifically for the SVG that is produced from Inkscape
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class InkscapeLoader implements Loader {
|
||||
/**
|
||||
* The number of times to over trigulate to get enough tesselation for
|
||||
* smooth shading
|
||||
*/
|
||||
public static int RADIAL_TRIANGULATION_LEVEL = 1;
|
||||
|
||||
/** The list of XML element processors */
|
||||
private static ArrayList processors = new ArrayList();
|
||||
|
||||
/** The diagram loaded */
|
||||
private Diagram diagram;
|
||||
|
||||
static {
|
||||
addElementProcessor(new RectProcessor());
|
||||
addElementProcessor(new EllipseProcessor());
|
||||
addElementProcessor(new PolygonProcessor());
|
||||
addElementProcessor(new PathProcessor());
|
||||
addElementProcessor(new LineProcessor());
|
||||
addElementProcessor(new GroupProcessor());
|
||||
addElementProcessor(new DefsProcessor());
|
||||
addElementProcessor(new UseProcessor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an <code>ElementProcessor</code> which will be passed
|
||||
* each element read as the Inkscape SVG document is processed.
|
||||
*
|
||||
* @param proc The processor to be added
|
||||
*/
|
||||
public static void addElementProcessor(ElementProcessor proc) {
|
||||
processors.add(proc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a SVG document into a diagram
|
||||
*
|
||||
* @param ref
|
||||
* The reference in the classpath to load the diagram from
|
||||
* @param offset
|
||||
* Offset the diagram for the height of the document
|
||||
* @return The diagram loaded
|
||||
* @throws SlickException
|
||||
* Indicates a failure to process the document
|
||||
*/
|
||||
public static Diagram load(String ref, boolean offset)
|
||||
throws SlickException {
|
||||
return load(ResourceLoader.getResourceAsStream(ref), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a SVG document into a diagram
|
||||
*
|
||||
* @param ref
|
||||
* The reference in the classpath to load the diagram from
|
||||
* @return The diagram loaded
|
||||
* @throws SlickException
|
||||
* Indicates a failure to process the document
|
||||
*/
|
||||
public static Diagram load(String ref) throws SlickException {
|
||||
return load(ResourceLoader.getResourceAsStream(ref), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a SVG document into a diagram
|
||||
*
|
||||
* @param offset
|
||||
* Offset the diagram for the height of the document
|
||||
* @param in
|
||||
* The input stream from which to read the SVG
|
||||
* @return The diagram loaded
|
||||
* @throws SlickException
|
||||
* Indicates a failure to process the document
|
||||
*/
|
||||
public static Diagram load(InputStream in, boolean offset)
|
||||
throws SlickException {
|
||||
return new InkscapeLoader().loadDiagram(in, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private, you should be using the static method
|
||||
*/
|
||||
private InkscapeLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a SVG document into a diagram
|
||||
*
|
||||
* @param in
|
||||
* The input stream from which to read the SVG
|
||||
* @return The diagram loaded
|
||||
* @throws SlickException
|
||||
* Indicates a failure to process the document
|
||||
*/
|
||||
private Diagram loadDiagram(InputStream in) throws SlickException {
|
||||
return loadDiagram(in, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a SVG document into a diagram
|
||||
*
|
||||
* @param in
|
||||
* The input stream from which to read the SVG
|
||||
* @param offset
|
||||
* Offset the diagram for the height of the document
|
||||
* @return The diagram loaded
|
||||
* @throws SlickException
|
||||
* Indicates a failure to process the document
|
||||
*/
|
||||
private Diagram loadDiagram(InputStream in, boolean offset)
|
||||
throws SlickException {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory
|
||||
.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(true);
|
||||
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
builder.setEntityResolver(new EntityResolver() {
|
||||
public InputSource resolveEntity(String publicId,
|
||||
String systemId) throws SAXException, IOException {
|
||||
return new InputSource(
|
||||
new ByteArrayInputStream(new byte[0]));
|
||||
}
|
||||
});
|
||||
|
||||
Document doc = builder.parse(in);
|
||||
Element root = doc.getDocumentElement();
|
||||
|
||||
String widthString = root.getAttribute("width");
|
||||
while (Character.isLetter(widthString
|
||||
.charAt(widthString.length() - 1))) {
|
||||
widthString = widthString.substring(0, widthString.length() - 1);
|
||||
}
|
||||
|
||||
String heightString = root.getAttribute("height");
|
||||
while (Character.isLetter(heightString
|
||||
.charAt(heightString.length() - 1))) {
|
||||
heightString = heightString.substring(0,heightString.length() - 1);
|
||||
}
|
||||
|
||||
float docWidth = Float.parseFloat(widthString);
|
||||
float docHeight = Float.parseFloat(heightString);
|
||||
|
||||
diagram = new Diagram(docWidth, docHeight);
|
||||
if (!offset) {
|
||||
docHeight = 0;
|
||||
}
|
||||
loadChildren(root, Transform
|
||||
.createTranslateTransform(0, -docHeight));
|
||||
|
||||
return diagram;
|
||||
} catch (Exception e) {
|
||||
throw new SlickException("Failed to load inkscape document", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.Loader#loadChildren(org.w3c.dom.Element,
|
||||
* org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void loadChildren(Element element, Transform t)
|
||||
throws ParsingException {
|
||||
NodeList list = element.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
loadElement((Element) list.item(i), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single element into the diagram
|
||||
*
|
||||
* @param element
|
||||
* The element ot be loaded
|
||||
* @param t
|
||||
* The transform to apply to the loaded element from the parent
|
||||
* @throws ParsingException
|
||||
* Indicates a failure to parse the element
|
||||
*/
|
||||
private void loadElement(Element element, Transform t)
|
||||
throws ParsingException {
|
||||
for (int i = 0; i < processors.size(); i++) {
|
||||
ElementProcessor processor = (ElementProcessor) processors.get(i);
|
||||
|
||||
if (processor.handles(element)) {
|
||||
processor.process(this, element, diagram, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import org.newdawn.slick.geom.Line;
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.TexCoordGenerator;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.geom.Vector2f;
|
||||
|
||||
/**
|
||||
* A filler for shapes that applys SVG linear gradients
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class LinearGradientFill implements TexCoordGenerator {
|
||||
/** The start position of the gradient line */
|
||||
private Vector2f start;
|
||||
/** The ends position of the gradient line */
|
||||
private Vector2f end;
|
||||
/** The gradient being applied */
|
||||
private Gradient gradient;
|
||||
/** The line of the gradient */
|
||||
private Line line;
|
||||
/** The shape being filled with gradient */
|
||||
private Shape shape;
|
||||
|
||||
/**
|
||||
* Create a new fill for gradients
|
||||
*
|
||||
* @param shape The shape being filled
|
||||
* @param trans The transform given for the shape
|
||||
* @param gradient The gradient to apply
|
||||
*/
|
||||
public LinearGradientFill(Shape shape, Transform trans, Gradient gradient) {
|
||||
this.gradient = gradient;
|
||||
|
||||
float x = gradient.getX1();
|
||||
float y = gradient.getY1();
|
||||
float mx = gradient.getX2();
|
||||
float my = gradient.getY2();
|
||||
|
||||
float h = my - y;
|
||||
float w = mx - x;
|
||||
|
||||
float[] s = new float[] {x,y+(h/2)};
|
||||
gradient.getTransform().transform(s, 0, s, 0, 1);
|
||||
trans.transform(s, 0, s, 0, 1);
|
||||
float[] e = new float[] {x+w,y+(h/2)};
|
||||
gradient.getTransform().transform(e, 0, e, 0, 1);
|
||||
trans.transform(e, 0, e, 0, 1);
|
||||
|
||||
start = new Vector2f(s[0],s[1]);
|
||||
end = new Vector2f(e[0],e[1]);
|
||||
|
||||
line = new Line(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.TexCoordGenerator#getCoordFor(float, float)
|
||||
*/
|
||||
public Vector2f getCoordFor(float x, float y) {
|
||||
Vector2f result = new Vector2f();
|
||||
line.getClosestPoint(new Vector2f(x,y), result);
|
||||
float u = result.distance(start);
|
||||
u /= line.length();
|
||||
|
||||
return new Vector2f(u,0);
|
||||
}
|
||||
|
||||
}
|
||||
20
lib/slick-source/org/newdawn/slick/svg/Loader.java
Normal file
20
lib/slick-source/org/newdawn/slick/svg/Loader.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Description of a simple XML loader
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public interface Loader {
|
||||
/**
|
||||
* Load the children of a given element
|
||||
*
|
||||
* @param element The element whose children should be loaded
|
||||
* @param t The transform to apply to all the children
|
||||
* @throws ParsingException Indicates a failure to read the XML
|
||||
*/
|
||||
public void loadChildren(Element element, Transform t) throws ParsingException;
|
||||
}
|
||||
210
lib/slick-source/org/newdawn/slick/svg/NonGeometricData.java
Normal file
210
lib/slick-source/org/newdawn/slick/svg/NonGeometricData.java
Normal file
@@ -0,0 +1,210 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
|
||||
/**
|
||||
* A set of data about a shape that doesn't fit into it's geometric
|
||||
* configuration.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class NonGeometricData {
|
||||
/** The ID of the figure */
|
||||
public static final String ID = "id";
|
||||
/** The fill type */
|
||||
public static final String FILL = "fill";
|
||||
/** The stroke type */
|
||||
public static final String STROKE = "stroke";
|
||||
/** The alpha value for filling */
|
||||
public static final String OPACITY = "opacity";
|
||||
/** The width of the line to draw */
|
||||
public static final String STROKE_WIDTH = "stroke-width";
|
||||
/** The mitre of the line to draw */
|
||||
public static final String STROKE_MITERLIMIT = "stroke-miterlimit";
|
||||
/** The dash definition of the line to draw */
|
||||
public static final String STROKE_DASHARRAY = "stroke-dasharray";
|
||||
/** The offset into the dash definition of the line to draw */
|
||||
public static final String STROKE_DASHOFFSET = "stroke-dashoffset";
|
||||
/** The alpha value for drawing */
|
||||
public static final String STROKE_OPACITY = "stroke-opacity";
|
||||
|
||||
/** Value indicating that no settings has been specified */
|
||||
public static final String NONE = "none";
|
||||
|
||||
/** The meta data stored for the figure */
|
||||
private String metaData = "";
|
||||
/** The attributes stored for the figure */
|
||||
private Properties props = new Properties();
|
||||
|
||||
/**
|
||||
* Create a set of non-geometric data for a figure
|
||||
*
|
||||
* @param metaData The meta data (either label or id) for the figure
|
||||
*/
|
||||
public NonGeometricData(String metaData) {
|
||||
this.metaData = metaData;
|
||||
addAttribute(STROKE_WIDTH, "1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Morph the color from a string
|
||||
*
|
||||
* @param str The string to morph
|
||||
* @return The new color string
|
||||
*/
|
||||
private String morphColor(String str) {
|
||||
if (str.equals("")) {
|
||||
return "#000000";
|
||||
}
|
||||
if (str.equals("white")) {
|
||||
return "#ffffff";
|
||||
}
|
||||
if (str.equals("black")) {
|
||||
return "#000000";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a configured style attribute into the data set
|
||||
*
|
||||
* @param attribute The attribute to add
|
||||
* @param value The value to assign
|
||||
*/
|
||||
public void addAttribute(String attribute, String value) {
|
||||
if (value == null) {
|
||||
value = "";
|
||||
}
|
||||
|
||||
if (attribute.equals(FILL) ) {
|
||||
value = morphColor(value);
|
||||
}
|
||||
if (attribute.equals(STROKE_OPACITY)) {
|
||||
if (value.equals("0")) {
|
||||
props.setProperty(STROKE, "none");
|
||||
}
|
||||
}
|
||||
if (attribute.equals(STROKE_WIDTH)) {
|
||||
if (value.equals("")) {
|
||||
value = "1";
|
||||
}
|
||||
if (value.endsWith("px")) {
|
||||
value = value.substring(0,value.length()-2);
|
||||
}
|
||||
}
|
||||
if (attribute.equals(STROKE)) {
|
||||
if ("none".equals(props.getProperty(STROKE))) {
|
||||
return;
|
||||
}
|
||||
if ("".equals(props.getProperty(STROKE))) {
|
||||
return;
|
||||
}
|
||||
value = morphColor(value);
|
||||
}
|
||||
|
||||
props.setProperty(attribute, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given attribute is in colour format
|
||||
*
|
||||
* @param attribute The attribute to check
|
||||
* @return True if the attirbute value is in colour format
|
||||
*/
|
||||
public boolean isColor(String attribute) {
|
||||
return getAttribute(attribute).startsWith("#");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta data assigned to the figure. Either the label or
|
||||
* the id value.
|
||||
*
|
||||
* @return The meta data assigned to the figure
|
||||
*/
|
||||
public String getMetaData() {
|
||||
return metaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attribtue value for a given attribute
|
||||
*
|
||||
* @param attribute The attribute whose value should be obtained
|
||||
* @return The value for the given attribute
|
||||
*/
|
||||
public String getAttribute(String attribute) {
|
||||
return props.getProperty(attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute value converted to a color. isColor should first be checked
|
||||
*
|
||||
* @param attribute The attribute whose value should be interpreted as a color
|
||||
* @return The color based on the attribute
|
||||
*/
|
||||
public Color getAsColor(String attribute) {
|
||||
if (!isColor(attribute)) {
|
||||
throw new RuntimeException("Attribute "+attribute+" is not specified as a color:"+getAttribute(attribute));
|
||||
}
|
||||
|
||||
int col = Integer.parseInt(getAttribute(attribute).substring(1), 16);
|
||||
|
||||
return new Color(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attribute value as a reference to another entity
|
||||
*
|
||||
* @param attribute The name of the attribute to retrieve
|
||||
* @return The reference part of the attribute value
|
||||
*/
|
||||
public String getAsReference(String attribute) {
|
||||
String value = getAttribute(attribute);
|
||||
if (value.length() < 7) {
|
||||
return "";
|
||||
}
|
||||
|
||||
value = value.substring(5, value.length()-1);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute converted to a float value
|
||||
*
|
||||
* @param attribute The attribute to retrieve
|
||||
* @return The float value derived from the attribute
|
||||
*/
|
||||
public float getAsFloat(String attribute) {
|
||||
String value = getAttribute(attribute);
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Float.parseFloat(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RuntimeException("Attribute "+attribute+" is not specified as a float:"+getAttribute(attribute));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the shape is meant to be filled
|
||||
*
|
||||
* @return True if the shape is meant to be filled
|
||||
*/
|
||||
public boolean isFilled() {
|
||||
return isColor(NonGeometricData.FILL);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the shape is meant to be outlined
|
||||
*
|
||||
* @return True if the shape is meant to be outlined
|
||||
*/
|
||||
public boolean isStroked() {
|
||||
return isColor(NonGeometricData.STROKE) && (getAsFloat(NonGeometricData.STROKE_WIDTH) > 0);
|
||||
}
|
||||
}
|
||||
54
lib/slick-source/org/newdawn/slick/svg/ParsingException.java
Normal file
54
lib/slick-source/org/newdawn/slick/svg/ParsingException.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Exception indicating a failure to parse XML, giving element information
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class ParsingException extends SlickException {
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
*
|
||||
* @param nodeID The ID of the node that failed validation
|
||||
* @param message The description of the failure
|
||||
* @param cause The exception causing this one
|
||||
*/
|
||||
public ParsingException(String nodeID, String message, Throwable cause) {
|
||||
super("("+nodeID+") "+message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
*
|
||||
* @param element The element that failed validation
|
||||
* @param message The description of the failure
|
||||
* @param cause The exception causing this one
|
||||
*/
|
||||
public ParsingException(Element element, String message, Throwable cause) {
|
||||
super("("+element.getAttribute("id")+") "+message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
*
|
||||
* @param nodeID The ID of the node that failed validation
|
||||
* @param message The description of the failure
|
||||
*/
|
||||
public ParsingException(String nodeID, String message) {
|
||||
super("("+nodeID+") "+message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
*
|
||||
* @param element The element that failed validation
|
||||
* @param message The description of the failure
|
||||
*/
|
||||
public ParsingException(Element element, String message) {
|
||||
super("("+element.getAttribute("id")+") "+message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.TexCoordGenerator;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.geom.Vector2f;
|
||||
|
||||
/**
|
||||
* A filler to apply a SVG radial gradient across a shape
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class RadialGradientFill implements TexCoordGenerator {
|
||||
/** The centre of the gradient */
|
||||
private Vector2f centre;
|
||||
/** The radius before the gradient is complete */
|
||||
private float radius;
|
||||
/** The gradient to apply */
|
||||
private Gradient gradient;
|
||||
/** The shape being filled */
|
||||
private Shape shape;
|
||||
|
||||
/**
|
||||
* Create a new fill for a radial gradient
|
||||
*
|
||||
* @param shape The shape being filled
|
||||
* @param trans The transform given for the shape in the SVG
|
||||
* @param gradient The gradient to apply across the shape
|
||||
*/
|
||||
public RadialGradientFill(Shape shape, Transform trans, Gradient gradient) {
|
||||
this.gradient = gradient;
|
||||
|
||||
radius = gradient.getR();
|
||||
float x = gradient.getX1();
|
||||
float y = gradient.getY1();
|
||||
|
||||
float[] c = new float[] {x,y};
|
||||
gradient.getTransform().transform(c, 0, c, 0, 1);
|
||||
trans.transform(c, 0, c, 0, 1);
|
||||
float[] rt = new float[] {x,y-radius};
|
||||
gradient.getTransform().transform(rt, 0, rt, 0, 1);
|
||||
trans.transform(rt, 0, rt, 0, 1);
|
||||
|
||||
centre = new Vector2f(c[0],c[1]);
|
||||
Vector2f dis = new Vector2f(rt[0],rt[1]);
|
||||
radius = dis.distance(centre);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.TexCoordGenerator#getCoordFor(float, float)
|
||||
*/
|
||||
public Vector2f getCoordFor(float x, float y) {
|
||||
float u = centre.distance(new Vector2f(x,y));
|
||||
u /= radius;
|
||||
|
||||
if (u > 0.99f) {
|
||||
u = 0.99f;
|
||||
}
|
||||
|
||||
return new Vector2f(u,0);
|
||||
}
|
||||
|
||||
}
|
||||
118
lib/slick-source/org/newdawn/slick/svg/SVGMorph.java
Normal file
118
lib/slick-source/org/newdawn/slick/svg/SVGMorph.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.newdawn.slick.geom.MorphShape;
|
||||
|
||||
/**
|
||||
* A utility to allow morphing between a set of similar SVG diagrams
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class SVGMorph extends Diagram {
|
||||
/** The list of figures being morphed */
|
||||
private ArrayList figures = new ArrayList();
|
||||
|
||||
/**
|
||||
* Create a new morph with a first diagram base
|
||||
*
|
||||
* @param diagram The base diagram which provides the first step of the morph
|
||||
*/
|
||||
public SVGMorph(Diagram diagram) {
|
||||
super(diagram.getWidth(), diagram.getHeight());
|
||||
|
||||
for (int i=0;i<diagram.getFigureCount();i++) {
|
||||
Figure figure = diagram.getFigure(i);
|
||||
Figure copy = new Figure(figure.getType(), new MorphShape(figure.getShape()), figure.getData(), figure.getTransform());
|
||||
|
||||
figures.add(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subsquent step to the morphing
|
||||
*
|
||||
* @param diagram The diagram to add as the next step in the morph
|
||||
*/
|
||||
public void addStep(Diagram diagram) {
|
||||
if (diagram.getFigureCount() != figures.size()) {
|
||||
throw new RuntimeException("Mismatched diagrams, missing ids");
|
||||
}
|
||||
for (int i=0;i<diagram.getFigureCount();i++) {
|
||||
Figure figure = diagram.getFigure(i);
|
||||
String id = figure.getData().getMetaData();
|
||||
|
||||
for (int j=0;j<figures.size();j++) {
|
||||
Figure existing = (Figure) figures.get(j);
|
||||
if (existing.getData().getMetaData().equals(id)) {
|
||||
MorphShape morph = (MorphShape) existing.getShape();
|
||||
morph.addShape(figure.getShape());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current diagram we should morph from. This only really works with
|
||||
* updateMorphTime() but can be used for smooth transitions between
|
||||
* morphs.
|
||||
*
|
||||
* @param diagram The diagram to use as the base of the morph
|
||||
*/
|
||||
public void setExternalDiagram(Diagram diagram) {
|
||||
for (int i=0;i<figures.size();i++) {
|
||||
Figure figure = (Figure) figures.get(i);
|
||||
|
||||
for (int j=0;j<diagram.getFigureCount();j++) {
|
||||
Figure newBase = diagram.getFigure(j);
|
||||
if (newBase.getData().getMetaData().equals(figure.getData().getMetaData())) {
|
||||
MorphShape shape = (MorphShape) figure.getShape();
|
||||
shape.setExternalFrame(newBase.getShape());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the morph time index by the amount specified
|
||||
*
|
||||
* @param delta The amount to update the morph by
|
||||
*/
|
||||
public void updateMorphTime(float delta) {
|
||||
for (int i=0;i<figures.size();i++) {
|
||||
Figure figure = (Figure) figures.get(i);
|
||||
MorphShape shape = (MorphShape) figure.getShape();
|
||||
shape.updateMorphTime(delta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "time" index for this morph. This is given in terms of diagrams, so
|
||||
* 0.5f would give you the position half way between the first and second diagrams.
|
||||
*
|
||||
* @param time The time index to represent on this diagrams
|
||||
*/
|
||||
public void setMorphTime(float time) {
|
||||
for (int i=0;i<figures.size();i++) {
|
||||
Figure figure = (Figure) figures.get(i);
|
||||
MorphShape shape = (MorphShape) figure.getShape();
|
||||
shape.setMorphTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Diagram#getFigureCount()
|
||||
*/
|
||||
public int getFigureCount() {
|
||||
return figures.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Diagram#getFigure(int)
|
||||
*/
|
||||
public Figure getFigure(int index) {
|
||||
return (Figure) figures.get(index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.newdawn.slick.svg;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.ShapeRenderer;
|
||||
import org.newdawn.slick.geom.TexCoordGenerator;
|
||||
import org.newdawn.slick.opengl.TextureImpl;
|
||||
import org.newdawn.slick.opengl.renderer.Renderer;
|
||||
import org.newdawn.slick.opengl.renderer.SGL;
|
||||
|
||||
/**
|
||||
* A very primtive implementation for rendering a diagram. This simply
|
||||
* sticks the shapes on the screen in the right fill and stroke colours
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class SimpleDiagramRenderer {
|
||||
/** The renderer to use for all GL operations */
|
||||
protected static SGL GL = Renderer.get();
|
||||
|
||||
/** The diagram to be rendered */
|
||||
public Diagram diagram;
|
||||
/** The display list representing the diagram */
|
||||
public int list = -1;
|
||||
|
||||
/**
|
||||
* Create a new simple renderer
|
||||
*
|
||||
* @param diagram The diagram to be rendered
|
||||
*/
|
||||
public SimpleDiagramRenderer(Diagram diagram) {
|
||||
this.diagram = diagram;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the diagram to the given graphics context
|
||||
*
|
||||
* @param g The graphics context to which we should render the diagram
|
||||
*/
|
||||
public void render(Graphics g) {
|
||||
// last list generation
|
||||
if (list == -1) {
|
||||
list = GL.glGenLists(1);
|
||||
GL.glNewList(list, SGL.GL_COMPILE);
|
||||
render(g, diagram);
|
||||
GL.glEndList();
|
||||
}
|
||||
|
||||
GL.glCallList(list);
|
||||
|
||||
TextureImpl.bindNone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to render a diagram in immediate mode
|
||||
*
|
||||
* @param g The graphics context to render to
|
||||
* @param diagram The diagram to render
|
||||
*/
|
||||
public static void render(Graphics g, Diagram diagram) {
|
||||
for (int i=0;i<diagram.getFigureCount();i++) {
|
||||
Figure figure = diagram.getFigure(i);
|
||||
|
||||
if (figure.getData().isFilled()) {
|
||||
if (figure.getData().isColor(NonGeometricData.FILL)) {
|
||||
g.setColor(figure.getData().getAsColor(NonGeometricData.FILL));
|
||||
g.fill(diagram.getFigure(i).getShape());
|
||||
g.setAntiAlias(true);
|
||||
g.draw(diagram.getFigure(i).getShape());
|
||||
g.setAntiAlias(false);
|
||||
}
|
||||
|
||||
String fill = figure.getData().getAsReference(NonGeometricData.FILL);
|
||||
if (diagram.getPatternDef(fill) != null){
|
||||
System.out.println("PATTERN");
|
||||
}
|
||||
if (diagram.getGradient(fill) != null) {
|
||||
Gradient gradient = diagram.getGradient(fill);
|
||||
Shape shape = diagram.getFigure(i).getShape();
|
||||
TexCoordGenerator fg = null;
|
||||
if (gradient.isRadial()) {
|
||||
fg = new RadialGradientFill(shape, diagram.getFigure(i).getTransform(), gradient);
|
||||
} else {
|
||||
fg = new LinearGradientFill(shape, diagram.getFigure(i).getTransform(), gradient);
|
||||
}
|
||||
|
||||
Color.white.bind();
|
||||
ShapeRenderer.texture(shape, gradient.getImage(), fg);
|
||||
}
|
||||
}
|
||||
|
||||
if (figure.getData().isStroked()) {
|
||||
if (figure.getData().isColor(NonGeometricData.STROKE)) {
|
||||
g.setColor(figure.getData().getAsColor(NonGeometricData.STROKE));
|
||||
g.setLineWidth(figure.getData().getAsFloat(NonGeometricData.STROKE_WIDTH));
|
||||
g.setAntiAlias(true);
|
||||
g.draw(diagram.getFigure(i).getShape());
|
||||
g.setAntiAlias(false);
|
||||
g.resetLineWidth();
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG VERSION
|
||||
// g.setColor(Color.black);
|
||||
// g.draw(diagram.getFigure(i).getShape());
|
||||
// g.setColor(Color.red);
|
||||
// g.fill(diagram.getFigure(i).getShape());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Gradient;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
* A processor for the defs node
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class DefsProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("defs")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform transform) throws ParsingException {
|
||||
NodeList patterns = element.getElementsByTagName("pattern");
|
||||
|
||||
for (int i=0;i<patterns.getLength();i++) {
|
||||
Element pattern = (Element) patterns.item(i);
|
||||
NodeList list = pattern.getElementsByTagName("image");
|
||||
if (list.getLength() == 0) {
|
||||
Log.warn("Pattern 1981 does not specify an image. Only image patterns are supported.");
|
||||
continue;
|
||||
}
|
||||
Element image = (Element) list.item(0);
|
||||
|
||||
String patternName = pattern.getAttribute("id");
|
||||
String ref = image.getAttributeNS(Util.XLINK, "href");
|
||||
diagram.addPatternDef(patternName, ref);
|
||||
}
|
||||
|
||||
NodeList linear = element.getElementsByTagName("linearGradient");
|
||||
ArrayList toResolve = new ArrayList();
|
||||
|
||||
for (int i=0;i<linear.getLength();i++) {
|
||||
Element lin = (Element) linear.item(i);
|
||||
String name = lin.getAttribute("id");
|
||||
Gradient gradient = new Gradient(name, false);
|
||||
|
||||
gradient.setTransform(Util.getTransform(lin, "gradientTransform"));
|
||||
|
||||
if (stringLength(lin.getAttribute("x1")) > 0) {
|
||||
gradient.setX1(Float.parseFloat(lin.getAttribute("x1")));
|
||||
}
|
||||
if (stringLength(lin.getAttribute("x2")) > 0) {
|
||||
gradient.setX2(Float.parseFloat(lin.getAttribute("x2")));
|
||||
}
|
||||
if (stringLength(lin.getAttribute("y1")) > 0) {
|
||||
gradient.setY1(Float.parseFloat(lin.getAttribute("y1")));
|
||||
}
|
||||
if (stringLength(lin.getAttribute("y2")) > 0) {
|
||||
gradient.setY2(Float.parseFloat(lin.getAttribute("y2")));
|
||||
}
|
||||
|
||||
String ref = lin.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
if (stringLength(ref) > 0) {
|
||||
gradient.reference(ref.substring(1));
|
||||
toResolve.add(gradient);
|
||||
} else {
|
||||
NodeList steps = lin.getElementsByTagName("stop");
|
||||
for (int j=0;j<steps.getLength();j++) {
|
||||
Element s = (Element) steps.item(j);
|
||||
float offset = Float.parseFloat(s.getAttribute("offset"));
|
||||
|
||||
String colInt = Util.extractStyle(s.getAttribute("style"),"stop-color");
|
||||
String opaInt = Util.extractStyle(s.getAttribute("style"),"stop-opacity");
|
||||
|
||||
int col = Integer.parseInt(colInt.substring(1), 16);
|
||||
Color stopColor = new Color(col);
|
||||
stopColor.a = Float.parseFloat(opaInt);
|
||||
|
||||
gradient.addStep(offset, stopColor);
|
||||
}
|
||||
|
||||
gradient.getImage();
|
||||
}
|
||||
|
||||
diagram.addGradient(name, gradient);
|
||||
}
|
||||
|
||||
NodeList radial = element.getElementsByTagName("radialGradient");
|
||||
for (int i=0;i<radial.getLength();i++) {
|
||||
Element rad = (Element) radial.item(i);
|
||||
String name = rad.getAttribute("id");
|
||||
Gradient gradient = new Gradient(name, true);
|
||||
|
||||
gradient.setTransform(Util.getTransform(rad, "gradientTransform"));
|
||||
|
||||
if (stringLength(rad.getAttribute("cx")) > 0) {
|
||||
gradient.setX1(Float.parseFloat(rad.getAttribute("cx")));
|
||||
}
|
||||
if (stringLength(rad.getAttribute("cy")) > 0) {
|
||||
gradient.setY1(Float.parseFloat(rad.getAttribute("cy")));
|
||||
}
|
||||
if (stringLength(rad.getAttribute("fx")) > 0) {
|
||||
gradient.setX2(Float.parseFloat(rad.getAttribute("fx")));
|
||||
}
|
||||
if (stringLength(rad.getAttribute("fy")) > 0) {
|
||||
gradient.setY2(Float.parseFloat(rad.getAttribute("fy")));
|
||||
}
|
||||
if (stringLength(rad.getAttribute("r")) > 0) {
|
||||
gradient.setR(Float.parseFloat(rad.getAttribute("r")));
|
||||
}
|
||||
|
||||
String ref = rad.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
if (stringLength(ref) > 0) {
|
||||
gradient.reference(ref.substring(1));
|
||||
toResolve.add(gradient);
|
||||
} else {
|
||||
NodeList steps = rad.getElementsByTagName("stop");
|
||||
for (int j=0;j<steps.getLength();j++) {
|
||||
Element s = (Element) steps.item(j);
|
||||
float offset = Float.parseFloat(s.getAttribute("offset"));
|
||||
|
||||
String colInt = Util.extractStyle(s.getAttribute("style"),"stop-color");
|
||||
String opaInt = Util.extractStyle(s.getAttribute("style"),"stop-opacity");
|
||||
|
||||
int col = Integer.parseInt(colInt.substring(1), 16);
|
||||
Color stopColor = new Color(col);
|
||||
stopColor.a = Float.parseFloat(opaInt);
|
||||
|
||||
gradient.addStep(offset, stopColor);
|
||||
}
|
||||
|
||||
gradient.getImage();
|
||||
}
|
||||
|
||||
diagram.addGradient(name, gradient);
|
||||
}
|
||||
|
||||
for (int i=0;i<toResolve.size();i++) {
|
||||
((Gradient) toResolve.get(i)).resolve(diagram);
|
||||
((Gradient) toResolve.get(i)).getImage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to cope with null values
|
||||
*
|
||||
* @param value The value to get the length of
|
||||
* @return The length of the string
|
||||
*/
|
||||
private int stringLength(String value) {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value.length();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* The description of a module which processes a single XML element from a SVG (inkscape)
|
||||
* document.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public interface ElementProcessor {
|
||||
/**
|
||||
* Process a document extracting all the elements that the processor is
|
||||
* interested in and producing appropriate diagram components for the
|
||||
* element.
|
||||
*
|
||||
* @param loader The loader/context of the parsing
|
||||
* @param element The element to be processed
|
||||
* @param diagram The diagram to be built
|
||||
* @param transform The transform to apply to all elements at this level
|
||||
* @throws ParsingException Indicates an invalid content to an element
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform transform) throws ParsingException;
|
||||
|
||||
/**
|
||||
* Check if this processor handles the element specified
|
||||
*
|
||||
* @param element The element to check
|
||||
* @return True if this processor can handle the given element
|
||||
*/
|
||||
public boolean handles(Element element);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import org.newdawn.slick.geom.Ellipse;
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Figure;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Processor for <ellipse> and <path> nodes marked as arcs
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class EllipseProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform t) throws ParsingException {
|
||||
Transform transform = Util.getTransform(element);
|
||||
transform = new Transform(t, transform);
|
||||
|
||||
float x = Util.getFloatAttribute(element,"cx");
|
||||
float y = Util.getFloatAttribute(element,"cy");
|
||||
float rx = Util.getFloatAttribute(element,"rx");
|
||||
float ry = Util.getFloatAttribute(element,"ry");
|
||||
|
||||
Ellipse ellipse = new Ellipse(x,y,rx,ry);
|
||||
Shape shape = ellipse.transform(transform);
|
||||
|
||||
NonGeometricData data = Util.getNonGeometricData(element);
|
||||
data.addAttribute("cx", ""+x);
|
||||
data.addAttribute("cy", ""+y);
|
||||
data.addAttribute("rx", ""+rx);
|
||||
data.addAttribute("ry", ""+ry);
|
||||
|
||||
diagram.addFigure(new Figure(Figure.ELLIPSE, shape, data, transform));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("ellipse")) {
|
||||
return true;
|
||||
}
|
||||
if (element.getNodeName().equals("path")) {
|
||||
if ("arc".equals(element.getAttributeNS(Util.SODIPODI, "type"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* TODO: Document this class
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class GroupProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("g")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**O
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform t) throws ParsingException {
|
||||
Transform transform = Util.getTransform(element);
|
||||
transform = new Transform(t, transform);
|
||||
|
||||
loader.loadChildren(element, transform);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A custom non-geometric data type that can pass back any attribute
|
||||
* on the field.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class InkscapeNonGeometricData extends NonGeometricData {
|
||||
/** The element read from the SVG */
|
||||
private Element element;
|
||||
|
||||
/**
|
||||
* Create a new non-geometric data holder
|
||||
*
|
||||
* @param metaData The metadata provided
|
||||
* @param element The XML element from the SVG document
|
||||
*/
|
||||
public InkscapeNonGeometricData(String metaData, Element element) {
|
||||
super(metaData);
|
||||
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.NonGeometricData#getAttribute(java.lang.String)
|
||||
*/
|
||||
public String getAttribute(String attribute) {
|
||||
String result = super.getAttribute(attribute);
|
||||
if (result == null) {
|
||||
result = element.getAttribute(attribute);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML element that is wrapped by this instance.
|
||||
*
|
||||
* @return The XML element for this instance
|
||||
*/
|
||||
public Element getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.newdawn.slick.geom.Line;
|
||||
import org.newdawn.slick.geom.Polygon;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Figure;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A processor for the <line> element
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class LineProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* Process the points in a polygon definition
|
||||
*
|
||||
* @param poly The polygon being built
|
||||
* @param element The XML element being read
|
||||
* @param tokens The tokens representing the path
|
||||
* @return The number of points found
|
||||
* @throws ParsingException Indicates an invalid token in the path
|
||||
*/
|
||||
private static int processPoly(Polygon poly, Element element, StringTokenizer tokens) throws ParsingException {
|
||||
int count = 0;
|
||||
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String nextToken = tokens.nextToken();
|
||||
if (nextToken.equals("L")) {
|
||||
continue;
|
||||
}
|
||||
if (nextToken.equals("z")) {
|
||||
break;
|
||||
}
|
||||
if (nextToken.equals("M")) {
|
||||
continue;
|
||||
}
|
||||
if (nextToken.equals("C")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String tokenX = nextToken;
|
||||
String tokenY = tokens.nextToken();
|
||||
|
||||
try {
|
||||
float x = Float.parseFloat(tokenX);
|
||||
float y = Float.parseFloat(tokenY);
|
||||
|
||||
poly.addPoint(x,y);
|
||||
count++;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(element.getAttribute("id"), "Invalid token in points list", e);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform t) throws ParsingException {
|
||||
Transform transform = Util.getTransform(element);
|
||||
transform = new Transform(t, transform);
|
||||
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
|
||||
if (element.getNodeName().equals("line")) {
|
||||
x1 = Float.parseFloat(element.getAttribute("x1"));
|
||||
x2 = Float.parseFloat(element.getAttribute("x2"));
|
||||
y1 = Float.parseFloat(element.getAttribute("y1"));
|
||||
y2 = Float.parseFloat(element.getAttribute("y2"));
|
||||
} else {
|
||||
String points = element.getAttribute("d");
|
||||
StringTokenizer tokens = new StringTokenizer(points, ", ");
|
||||
Polygon poly = new Polygon();
|
||||
if (processPoly(poly, element, tokens) == 2) {
|
||||
x1 = poly.getPoint(0)[0];
|
||||
y1 = poly.getPoint(0)[1];
|
||||
x2 = poly.getPoint(1)[0];
|
||||
y2 = poly.getPoint(1)[1];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float[] in = new float[] {x1,y1,x2,y2};
|
||||
float[] out = new float[4];
|
||||
|
||||
transform.transform(in,0,out,0,2);
|
||||
Line line = new Line(out[0],out[1],out[2],out[3]);
|
||||
|
||||
NonGeometricData data = Util.getNonGeometricData(element);
|
||||
data.addAttribute("x1",""+x1);
|
||||
data.addAttribute("x2",""+x2);
|
||||
data.addAttribute("y1",""+y1);
|
||||
data.addAttribute("y2",""+y2);
|
||||
|
||||
diagram.addFigure(new Figure(Figure.LINE, line, data, transform));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("line")) {
|
||||
return true;
|
||||
}
|
||||
if (element.getNodeName().equals("path")) {
|
||||
if (!"arc".equals(element.getAttributeNS(Util.SODIPODI, "type"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.newdawn.slick.geom.Path;
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Figure;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A processor for the <polygon> and <path> elements marked as not an arc.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class PathProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* Process the points in a polygon definition
|
||||
*
|
||||
* @param element The XML element being read
|
||||
* @param tokens The tokens representing the path
|
||||
* @return The number of points found
|
||||
* @throws ParsingException Indicates an invalid token in the path
|
||||
*/
|
||||
private static Path processPoly(Element element, StringTokenizer tokens) throws ParsingException {
|
||||
int count = 0;
|
||||
|
||||
ArrayList pts = new ArrayList();
|
||||
boolean moved = false;
|
||||
boolean reasonToBePath = false;
|
||||
Path path = null;
|
||||
|
||||
while (tokens.hasMoreTokens()) {
|
||||
try {
|
||||
String nextToken = tokens.nextToken();
|
||||
if (nextToken.equals("L")) {
|
||||
float x = Float.parseFloat(tokens.nextToken());
|
||||
float y = Float.parseFloat(tokens.nextToken());
|
||||
path.lineTo(x,y);
|
||||
continue;
|
||||
}
|
||||
if (nextToken.equals("z")) {
|
||||
path.close();
|
||||
continue;
|
||||
}
|
||||
if (nextToken.equals("M")) {
|
||||
if (!moved) {
|
||||
moved = true;
|
||||
float x = Float.parseFloat(tokens.nextToken());
|
||||
float y = Float.parseFloat(tokens.nextToken());
|
||||
path = new Path(x,y);
|
||||
continue;
|
||||
}
|
||||
|
||||
reasonToBePath = true;
|
||||
float x = Float.parseFloat(tokens.nextToken());
|
||||
float y = Float.parseFloat(tokens.nextToken());
|
||||
path.startHole(x,y);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (nextToken.equals("C")) {
|
||||
reasonToBePath = true;
|
||||
float cx1 = Float.parseFloat(tokens.nextToken());
|
||||
float cy1 = Float.parseFloat(tokens.nextToken());
|
||||
float cx2 = Float.parseFloat(tokens.nextToken());
|
||||
float cy2 = Float.parseFloat(tokens.nextToken());
|
||||
float x = Float.parseFloat(tokens.nextToken());
|
||||
float y = Float.parseFloat(tokens.nextToken());
|
||||
path.curveTo(x,y,cx1,cy1,cx2,cy2);
|
||||
continue;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(element.getAttribute("id"), "Invalid token in points list", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reasonToBePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform t) throws ParsingException {
|
||||
Transform transform = Util.getTransform(element);
|
||||
transform = new Transform(t, transform);
|
||||
|
||||
String points = element.getAttribute("points");
|
||||
if (element.getNodeName().equals("path")) {
|
||||
points = element.getAttribute("d");
|
||||
}
|
||||
|
||||
StringTokenizer tokens = new StringTokenizer(points, ", ");
|
||||
Path path = processPoly(element, tokens);
|
||||
NonGeometricData data = Util.getNonGeometricData(element);
|
||||
if (path != null) {
|
||||
Shape shape = path.transform(transform);
|
||||
|
||||
diagram.addFigure(new Figure(Figure.PATH, shape, data, transform));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("path")) {
|
||||
if (!"arc".equals(element.getAttributeNS(Util.SODIPODI, "type"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.newdawn.slick.geom.Polygon;
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Figure;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A processor for the <polygon> and <path> elements marked as not an arc.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class PolygonProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* Process the points in a polygon definition
|
||||
*
|
||||
* @param poly The polygon being built
|
||||
* @param element The XML element being read
|
||||
* @param tokens The tokens representing the path
|
||||
* @return The number of points found
|
||||
* @throws ParsingException Indicates an invalid token in the path
|
||||
*/
|
||||
private static int processPoly(Polygon poly, Element element, StringTokenizer tokens) throws ParsingException {
|
||||
int count = 0;
|
||||
|
||||
ArrayList pts = new ArrayList();
|
||||
boolean moved = false;
|
||||
boolean closed = false;
|
||||
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String nextToken = tokens.nextToken();
|
||||
if (nextToken.equals("L")) {
|
||||
continue;
|
||||
}
|
||||
if (nextToken.equals("z")) {
|
||||
closed = true;
|
||||
break;
|
||||
}
|
||||
if (nextToken.equals("M")) {
|
||||
if (!moved) {
|
||||
moved = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (nextToken.equals("C")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String tokenX = nextToken;
|
||||
String tokenY = tokens.nextToken();
|
||||
|
||||
try {
|
||||
float x = Float.parseFloat(tokenX);
|
||||
float y = Float.parseFloat(tokenY);
|
||||
|
||||
poly.addPoint(x,y);
|
||||
count++;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(element.getAttribute("id"), "Invalid token in points list", e);
|
||||
}
|
||||
}
|
||||
|
||||
poly.setClosed(closed);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform t) throws ParsingException {
|
||||
Transform transform = Util.getTransform(element);
|
||||
transform = new Transform(t, transform);
|
||||
|
||||
String points = element.getAttribute("points");
|
||||
if (element.getNodeName().equals("path")) {
|
||||
points = element.getAttribute("d");
|
||||
}
|
||||
|
||||
StringTokenizer tokens = new StringTokenizer(points, ", ");
|
||||
Polygon poly = new Polygon();
|
||||
int count = processPoly(poly, element, tokens);
|
||||
|
||||
NonGeometricData data = Util.getNonGeometricData(element);
|
||||
if (count > 3) {
|
||||
Shape shape = poly.transform(transform);
|
||||
|
||||
diagram.addFigure(new Figure(Figure.POLYGON, shape, data, transform));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("polygon")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (element.getNodeName().equals("path")) {
|
||||
if (!"arc".equals(element.getAttributeNS(Util.SODIPODI, "type"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import org.newdawn.slick.geom.Rectangle;
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Figure;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A processor for the <rect> element.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class RectProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram, Transform t) throws ParsingException {
|
||||
Transform transform = Util.getTransform(element);
|
||||
transform = new Transform(t, transform);
|
||||
|
||||
float width = Float.parseFloat(element.getAttribute("width"));
|
||||
float height = Float.parseFloat(element.getAttribute("height"));
|
||||
float x = Float.parseFloat(element.getAttribute("x"));
|
||||
float y = Float.parseFloat(element.getAttribute("y"));
|
||||
|
||||
Rectangle rect = new Rectangle(x,y,width+1,height+1);
|
||||
Shape shape = rect.transform(transform);
|
||||
|
||||
NonGeometricData data = Util.getNonGeometricData(element);
|
||||
data.addAttribute("width", ""+width);
|
||||
data.addAttribute("height", ""+height);
|
||||
data.addAttribute("x", ""+x);
|
||||
data.addAttribute("y", ""+y);
|
||||
|
||||
diagram.addFigure(new Figure(Figure.RECTANGLE, shape, data, transform));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
if (element.getNodeName().equals("rect")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.Diagram;
|
||||
import org.newdawn.slick.svg.Figure;
|
||||
import org.newdawn.slick.svg.Loader;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Processor for the "use", a tag that allows references to other elements
|
||||
* and cloning.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class UseProcessor implements ElementProcessor {
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#handles(org.w3c.dom.Element)
|
||||
*/
|
||||
public boolean handles(Element element) {
|
||||
return element.getNodeName().equals("use");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.svg.inkscape.ElementProcessor#process(org.newdawn.slick.svg.Loader, org.w3c.dom.Element, org.newdawn.slick.svg.Diagram, org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public void process(Loader loader, Element element, Diagram diagram,
|
||||
Transform transform) throws ParsingException {
|
||||
|
||||
String ref = element.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
String href = Util.getAsReference(ref);
|
||||
|
||||
Figure referenced = diagram.getFigureByID(href);
|
||||
if (referenced == null) {
|
||||
throw new ParsingException(element, "Unable to locate referenced element: "+href);
|
||||
}
|
||||
|
||||
Transform local = Util.getTransform(element);
|
||||
Transform trans = local.concatenate(referenced.getTransform());
|
||||
|
||||
NonGeometricData data = Util.getNonGeometricData(element);
|
||||
Shape shape = referenced.getShape().transform(trans);
|
||||
data.addAttribute(NonGeometricData.FILL, referenced.getData().getAttribute(NonGeometricData.FILL));
|
||||
data.addAttribute(NonGeometricData.STROKE, referenced.getData().getAttribute(NonGeometricData.STROKE));
|
||||
data.addAttribute(NonGeometricData.OPACITY, referenced.getData().getAttribute(NonGeometricData.OPACITY));
|
||||
data.addAttribute(NonGeometricData.STROKE_WIDTH, referenced.getData().getAttribute(NonGeometricData.STROKE_WIDTH));
|
||||
|
||||
Figure figure = new Figure(referenced.getType(), shape, data, trans);
|
||||
diagram.addFigure(figure);
|
||||
}
|
||||
|
||||
}
|
||||
198
lib/slick-source/org/newdawn/slick/svg/inkscape/Util.java
Normal file
198
lib/slick-source/org/newdawn/slick/svg/inkscape/Util.java
Normal file
@@ -0,0 +1,198 @@
|
||||
package org.newdawn.slick.svg.inkscape;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
import org.newdawn.slick.svg.NonGeometricData;
|
||||
import org.newdawn.slick.svg.ParsingException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A set of utility for processing the SVG documents produced by Inkscape
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class Util {
|
||||
/** The namespace for inkscape */
|
||||
public static final String INKSCAPE = "http://www.inkscape.org/namespaces/inkscape";
|
||||
/** The namespace for sodipodi */
|
||||
public static final String SODIPODI = "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
|
||||
/** The namespace for xlink */
|
||||
public static final String XLINK = "http://www.w3.org/1999/xlink";
|
||||
|
||||
/**
|
||||
* Get the non-geometric data information from an XML element
|
||||
*
|
||||
* @param element The element to be processed
|
||||
* @return The non-geometric data (i.e. stroke, fill, etc)
|
||||
*/
|
||||
static NonGeometricData getNonGeometricData(Element element) {
|
||||
String meta = getMetaData(element);
|
||||
|
||||
NonGeometricData data = new InkscapeNonGeometricData(meta, element);
|
||||
data.addAttribute(NonGeometricData.ID, element.getAttribute("id"));
|
||||
data.addAttribute(NonGeometricData.FILL, getStyle(element, NonGeometricData.FILL));
|
||||
data.addAttribute(NonGeometricData.STROKE, getStyle(element, NonGeometricData.STROKE));
|
||||
data.addAttribute(NonGeometricData.OPACITY, getStyle(element, NonGeometricData.OPACITY));
|
||||
data.addAttribute(NonGeometricData.STROKE_DASHARRAY, getStyle(element, NonGeometricData.STROKE_DASHARRAY));
|
||||
data.addAttribute(NonGeometricData.STROKE_DASHOFFSET, getStyle(element, NonGeometricData.STROKE_DASHOFFSET));
|
||||
data.addAttribute(NonGeometricData.STROKE_MITERLIMIT, getStyle(element, NonGeometricData.STROKE_MITERLIMIT));
|
||||
data.addAttribute(NonGeometricData.STROKE_OPACITY, getStyle(element, NonGeometricData.STROKE_OPACITY));
|
||||
data.addAttribute(NonGeometricData.STROKE_WIDTH, getStyle(element, NonGeometricData.STROKE_WIDTH));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta data store within an element either in the label or
|
||||
* id atributes
|
||||
*
|
||||
* @param element The element to be processed
|
||||
* @return The meta data stored
|
||||
*/
|
||||
static String getMetaData(Element element) {
|
||||
String label = element.getAttributeNS(INKSCAPE, "label");
|
||||
if ((label != null) && (!label.equals(""))) {
|
||||
return label;
|
||||
}
|
||||
|
||||
return element.getAttribute("id");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the style attribute setting for a given style information element (i.e. fill, stroke)
|
||||
*
|
||||
* @param element The element to be processed
|
||||
* @param styleName The name of the attribute to retrieve
|
||||
* @return The style value
|
||||
*/
|
||||
static String getStyle(Element element, String styleName) {
|
||||
String value = element.getAttribute(styleName);
|
||||
|
||||
if ((value != null) && (value.length() > 0)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
String style = element.getAttribute("style");
|
||||
return extractStyle(style, styleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the style value from a Inkscape encoded string
|
||||
*
|
||||
* @param style The style string to be decoded
|
||||
* @param attribute The style attribute to retrieve
|
||||
* @return The value for the given attribute
|
||||
*/
|
||||
static String extractStyle(String style, String attribute) {
|
||||
if (style == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringTokenizer tokens = new StringTokenizer(style,";");
|
||||
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String token = tokens.nextToken();
|
||||
String key = token.substring(0,token.indexOf(':'));
|
||||
if (key.equals(attribute)) {
|
||||
return token.substring(token.indexOf(':')+1);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a transform defined in the XML
|
||||
*
|
||||
* @param element The element from which the transform should be read
|
||||
* @return The transform to be applied
|
||||
*/
|
||||
static Transform getTransform(Element element) {
|
||||
return getTransform(element, "transform");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a transform defined in the XML
|
||||
*
|
||||
* @param element The element from which the transform should be read
|
||||
* @param attribute The name of the attribute holding the transform
|
||||
* @return The transform to be applied
|
||||
*/
|
||||
static Transform getTransform(Element element, String attribute) {
|
||||
String str = element.getAttribute(attribute);
|
||||
if (str == null) {
|
||||
return new Transform();
|
||||
}
|
||||
|
||||
if (str.equals("")) {
|
||||
return new Transform();
|
||||
} else if (str.startsWith("translate")) {
|
||||
str = str.substring(0, str.length()-1);
|
||||
str = str.substring("translate(".length());
|
||||
StringTokenizer tokens = new StringTokenizer(str, ", ");
|
||||
float x = Float.parseFloat(tokens.nextToken());
|
||||
float y = Float.parseFloat(tokens.nextToken());
|
||||
|
||||
return Transform.createTranslateTransform(x,y);
|
||||
} else if (str.startsWith("matrix")) {
|
||||
float[] pose = new float[6];
|
||||
str = str.substring(0, str.length()-1);
|
||||
str = str.substring("matrix(".length());
|
||||
StringTokenizer tokens = new StringTokenizer(str, ", ");
|
||||
float[] tr = new float[6];
|
||||
for (int j=0;j<tr.length;j++) {
|
||||
tr[j] = Float.parseFloat(tokens.nextToken());
|
||||
}
|
||||
|
||||
pose[0] = tr[0];
|
||||
pose[1] = tr[2];
|
||||
pose[2] = tr[4];
|
||||
pose[3] = tr[1];
|
||||
pose[4] = tr[3];
|
||||
pose[5] = tr[5];
|
||||
|
||||
return new Transform(pose);
|
||||
}
|
||||
|
||||
return new Transform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a floating point attribute that may appear in either the default or
|
||||
* SODIPODI namespace
|
||||
*
|
||||
* @param element The element from which the attribute should be read
|
||||
* @param attr The attribute to be read
|
||||
* @return The value from the given attribute
|
||||
* @throws ParsingException Indicates the value in the attribute was not a float
|
||||
*/
|
||||
static float getFloatAttribute(Element element, String attr) throws ParsingException {
|
||||
String cx = element.getAttribute(attr);
|
||||
if ((cx == null) || (cx.equals(""))) {
|
||||
cx = element.getAttributeNS(SODIPODI, attr);
|
||||
}
|
||||
|
||||
try {
|
||||
return Float.parseFloat(cx);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(element, "Invalid value for: "+attr, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attribute value as a reference to another entity
|
||||
*
|
||||
* @param value The value to treat as reference
|
||||
* @return The reference part of the attribute value
|
||||
*/
|
||||
public static String getAsReference(String value) {
|
||||
if (value.length() < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
value = value.substring(1, value.length());
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
4
lib/slick-source/org/newdawn/slick/svg/package.html
Normal file
4
lib/slick-source/org/newdawn/slick/svg/package.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<BODY>
|
||||
Demo/Test SVG area. Tiny line is integrated to render-to-texture - however a full implementation is
|
||||
to follow rendering SVG to OpenGL geometric shapes.
|
||||
</BODY>
|
||||
Reference in New Issue
Block a user