Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.graphstream.stream.file.FileSinkSVG Maven / Gradle / Ivy
/*
* This file is part of GraphStream .
*
* GraphStream is a library whose purpose is to handle static or dynamic
* graph, create them from scratch, file or any source and display them.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL or under the terms of the GNU LGPL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
/**
* @since 2012-03-26
*
* @author Guilhelm Savin
* @author Hicham Brahimi
* @author Yoann Pigné
*/
package org.graphstream.stream.file;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Element;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.ui.geom.Vector2;
import org.graphstream.ui.graphicGraph.StyleGroup;
import org.graphstream.ui.graphicGraph.StyleGroupSet;
import org.graphstream.ui.graphicGraph.stylesheet.Color;
import org.graphstream.ui.graphicGraph.stylesheet.Colors;
import org.graphstream.ui.graphicGraph.stylesheet.Selector;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
import org.graphstream.ui.graphicGraph.stylesheet.Value;
import org.graphstream.ui.graphicGraph.stylesheet.Values;
import org.graphstream.ui.graphicGraph.stylesheet.Selector.Type;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.Shape;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.StrokeMode;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.Units;
import org.graphstream.ui.graphicGraph.stylesheet.StyleSheet;
public class FileSinkSVG implements FileSink {
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.file.FileSink#begin(java.lang.String)
*/
public void begin(String fileName) throws IOException {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.file.FileSink#begin(java.io.OutputStream)
*/
public void begin(OutputStream stream) throws IOException {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.file.FileSink#begin(java.io.Writer)
*/
public void begin(Writer writer) throws IOException {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.file.FileSink#end()
*/
public void end() throws IOException {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.file.FileSink#flush()
*/
public void flush() throws IOException {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.file.FileSink#writeAll(org.graphstream.graph.Graph ,
* java.lang.String)
*/
public void writeAll(Graph graph, String fileName) throws IOException {
FileWriter out = new FileWriter(fileName);
writeAll(graph, out);
out.close();
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.file.FileSink#writeAll(org.graphstream.graph.Graph ,
* java.io.OutputStream)
*/
public void writeAll(Graph graph, OutputStream stream) throws IOException {
OutputStreamWriter out = new OutputStreamWriter(stream);
writeAll(graph, out);
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.file.FileSink#writeAll(org.graphstream.graph.Graph ,
* java.io.Writer)
*/
public void writeAll(Graph g, Writer w) throws IOException {
XMLWriter out = new XMLWriter();
SVGContext ctx = new SVGContext();
try {
out.start(w);
} catch (XMLStreamException e) {
throw new IOException(e);
} catch (FactoryConfigurationError e) {
throw new RuntimeException(e);
}
try {
ctx.init(out, g);
ctx.writeElements(out, g);
ctx.end(out);
} catch (XMLStreamException e) {
throw new IOException(e);
}
try {
out.end();
} catch (XMLStreamException e) {
throw new IOException(e);
}
}
private static String d(double d) {
return String.format(Locale.ROOT, "%f", d);
}
private static double getX(Node n) {
if (n.hasNumber("x"))
return n.getNumber("x");
if (n.hasArray("xy")) {
Object[] xy = n.getArray("xy");
if (xy != null && xy.length > 0 && xy[0] instanceof Number)
return ((Number) xy[0]).doubleValue();
}
if (n.hasArray("xyz")) {
Object[] xyz = n.getArray("xyz");
if (xyz != null && xyz.length > 0 && xyz[0] instanceof Number)
return ((Number) xyz[0]).doubleValue();
}
System.err.printf("[WARNING] no x attribute for node \"%s\" %s\n", n.getId(), n.hasAttribute("xyz"));
return Math.random();
}
private static double getY(Node n) {
if (n.hasNumber("y"))
return n.getNumber("y");
if (n.hasArray("xy")) {
Object[] xy = n.getArray("xy");
if (xy != null && xy.length > 1 && xy[1] instanceof Number)
return ((Number) xy[1]).doubleValue();
}
if (n.hasArray("xyz")) {
Object[] xyz = n.getArray("xyz");
if (xyz != null && xyz.length > 1 && xyz[1] instanceof Number)
return ((Number) xyz[1]).doubleValue();
}
return Math.random();
}
private static String getSize(Value v) {
String u = v.units.name().toLowerCase();
return String.format(Locale.ROOT, "%f%s", v.value, u);
}
private static String getSize(Values v, int index) {
String u = v.units.name().toLowerCase();
if (Units.PERCENTS.equals(v.units))
u = "%";
return String.format(Locale.ROOT, "%f%s", v.get(index), u);
}
static class SVGContext {
StyleGroupSet groups;
StyleSheet stylesheet;
HashMap svgStyles;
ViewBox viewBox;
public SVGContext() {
stylesheet = new StyleSheet();
groups = new StyleGroupSet(stylesheet);
svgStyles = new HashMap();
viewBox = new ViewBox(0, 0, 1000, 1000);
}
public void init(XMLWriter out, Graph g) throws IOException, XMLStreamException {
if (g.hasAttribute("ui.stylesheet")) {
stylesheet.load(((String) g.getAttribute("ui.stylesheet")));
}
groups.addElement(g);
viewBox.compute(g, groups.getStyleFor(g));
out.open("svg");
out.attribute("xmlns", "http://www.w3.org/2000/svg");
out.attribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
out.attribute("xmlns:cc", "http://creativecommons.org/ns#");
out.attribute("xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
out.attribute("xmlns:svg", "http://www.w3.org/2000/svg");
out.attribute("viewBox",
String.format(Locale.ROOT, "%f %f %f %f", viewBox.x1, viewBox.y1, viewBox.x2, viewBox.y2));
out.attribute("id", g.getId());
out.attribute("version", "1.1");
try {
g.edges().forEach(e -> {
groups.addElement(e);
if (e.hasAttribute("ui.style")) {
try {
stylesheet.parseStyleFromString(new Selector(Type.EDGE, e.getId(), null),
(String) e.getAttribute("ui.style"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
groups.checkElementStyleGroup(e);
});
g.nodes().forEach(n -> {
groups.addElement(n);
if (n.hasAttribute("ui.style")) {
try {
stylesheet.parseStyleFromString(new Selector(Type.NODE, n.getId(), null),
(String) n.getAttribute("ui.style"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
groups.checkElementStyleGroup(n);
});
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException)
throw (IOException) e.getCause();
if (e.getCause() instanceof XMLStreamException)
throw (IOException) e.getCause();
}
for (StyleGroup group : groups.groups())
svgStyles.put(group, new SVGStyle(group));
out.open("defs");
for (SVGStyle svgStyle : svgStyles.values())
svgStyle.writeDef(out);
out.close();
}
public void end(XMLWriter out) throws XMLStreamException {
out.close();
}
public void writeElements(XMLWriter out, Graph g) throws XMLStreamException {
out.open("g");
out.attribute("id", "graph-misc");
writeElement(out, g);
out.close();
Iterator> it = groups.getZIterator();
out.open("g");
out.attribute("id", "elements");
while (it.hasNext()) {
HashSet set = it.next();
for (StyleGroup sg : set)
for (Element e : sg.elements())
writeElement(out, e);
}
out.close();
}
public void writeElement(XMLWriter out, Element e) throws XMLStreamException {
String id = "";
SVGStyle style = null;
String transform = null;
if (e instanceof Edge) {
id = String.format("egde-%s", e.getId());
style = svgStyles.get(groups.getStyleFor((Edge) e));
} else if (e instanceof Node) {
id = String.format("node-%s", e.getId());
style = svgStyles.get(groups.getStyleFor((Node) e));
transform = String.format(Locale.ROOT, "translate(%f,%f)", viewBox.convertX((Node) e),
viewBox.convertY((Node) e));
} else if (e instanceof Graph) {
id = "graph-background";
style = svgStyles.get(groups.getStyleFor((Graph) e));
}
out.open("g");
out.attribute("id", id);
out.open("path");
if (style != null)
out.attribute("style", style.getElementStyle(e));
if (transform != null)
out.attribute("transform", transform);
out.attribute("d", getPath(e, style));
out.close();
if (e.hasLabel("label"))
writeElementText(out, (String) e.getAttribute("label"), e, style.group);
out.close();
}
public void writeElementText(XMLWriter out, String text, Element e, StyleGroup style)
throws XMLStreamException {
if (style == null || style.getTextVisibilityMode() != StyleConstants.TextVisibilityMode.HIDDEN) {
double x, y;
x = 0;
y = 0;
if (e instanceof Node) {
x = viewBox.convertX((Node) e);
y = viewBox.convertY((Node) e);
} else if (e instanceof Edge) {
Node n0, n1;
n0 = ((Edge) e).getNode0();
n1 = ((Edge) e).getNode0();
x = viewBox.convertX((getX(n0) + getX(n1)) / 2);
y = viewBox.convertY((getY(n0) + getY(n1)) / 2);
}
out.open("g");
out.open("text");
out.attribute("x", d(x));
out.attribute("y", d(y));
if (style != null) {
if (style.getTextColorCount() > 0)
out.attribute("fill", toHexColor(style.getTextColor(0)));
switch (style.getTextAlignment()) {
case CENTER:
out.attribute("text-anchor", "middle");
out.attribute("alignment-baseline", "central");
break;
case LEFT:
out.attribute("text-anchor", "start");
break;
case RIGHT:
out.attribute("text-anchor", "end");
break;
default:
break;
}
switch (style.getTextSize().units) {
case PX:
case GU:
out.attribute("font-size", d(style.getTextSize().value));
break;
case PERCENTS:
out.attribute("font-size", d(style.getTextSize().value) + "%");
break;
}
if (style.getTextFont() != null)
out.attribute("font-family", style.getTextFont());
switch (style.getTextStyle()) {
case NORMAL:
break;
case ITALIC:
out.attribute("font-style", "italic");
break;
case BOLD:
out.attribute("font-weight", "bold");
break;
case BOLD_ITALIC:
out.attribute("font-weight", "bold");
out.attribute("font-style", "italic");
break;
}
}
out.characters(text);
out.close();
out.close();
}
}
public String getPath(Element e, SVGStyle style) {
StringBuilder buffer = new StringBuilder();
if (e instanceof Node) {
double sx, sy;
Values size = style.group.getSize();
sx = getValue(size.get(0), size.units, true);
if (size.getValueCount() > 1)
sy = getValue(size.get(1), size.units, false);
else
sy = getValue(size.get(0), size.units, false);
switch (style.group.getShape()) {
case ROUNDED_BOX:
double rx, ry;
rx = Math.min(5, sx / 2);
ry = Math.min(5, sy / 2);
concat(buffer, " m ", d(-sx / 2 + rx), " ", d(-sy / 2));
concat(buffer, " h ", d(sx - 2 * rx));
concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 ", d(rx), ",", d(ry));
concat(buffer, " v ", d(sy - 2 * ry));
concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 -", d(rx), ",", d(ry));
concat(buffer, " h ", d(-sx + 2 * rx));
concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 -", d(rx), ",-", d(ry));
concat(buffer, " v ", d(-sy + 2 * ry));
concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 ", d(rx), "-", d(ry));
concat(buffer, " z");
break;
case BOX:
concat(buffer, " m ", d(-sx / 2), " ", d(-sy / 2));
concat(buffer, " h ", d(sx));
concat(buffer, " v ", d(sy));
concat(buffer, " h ", d(-sx));
concat(buffer, " z");
break;
case DIAMOND:
concat(buffer, " m ", d(-sx / 2), " 0");
concat(buffer, " l ", d(sx / 2), " ", d(-sy / 2));
concat(buffer, " l ", d(sx / 2), " ", d(sy / 2));
concat(buffer, " l ", d(-sx / 2), " ", d(sy / 2));
concat(buffer, " z");
break;
case TRIANGLE:
concat(buffer, " m ", d(0), " ", d(-sy / 2));
concat(buffer, " l ", d(sx / 2), " ", d(sy));
concat(buffer, " h ", d(-sx));
concat(buffer, " z");
break;
default:
case CIRCLE:
concat(buffer, " m ", d(-sx / 2), " 0");
concat(buffer, " a ", d(sx / 2), ",", d(sy / 2), " 0 1 0 ", d(sx), ",0");
concat(buffer, " ", d(sx / 2), ",", d(sy / 2), " 0 1 0 -", d(sx), ",0");
concat(buffer, " z");
break;
}
} else if (e instanceof Graph) {
concat(buffer, " M ", d(viewBox.x1), " ", d(viewBox.y1));
concat(buffer, " L ", d(viewBox.x2), " ", d(viewBox.y1));
concat(buffer, " L ", d(viewBox.x2), " ", d(viewBox.y2));
concat(buffer, " L ", d(viewBox.x1), " ", d(viewBox.y2));
concat(buffer, " Z");
} else if (e instanceof Edge) {
//---------- Size Edge
double sizeEdge = getValue(style.group.getSize().get(0), style.group.getSize().units, true);
//---------- Size Arrow
double sx, sy;
Values sizeArrow = style.group.getArrowSize();
sx = getValue(sizeArrow.get(0), sizeArrow.units, true);
if (sizeArrow.getValueCount() > 1)
sy = getValue(sizeArrow.get(1), sizeArrow.units, false);
else
sy = getValue(sizeArrow.get(0), sizeArrow.units, false);
//-------------- Draw Edge
Edge edge = (Edge) e;
Node src, trg;
double x1, y1;
double x2, y2;
src = edge.getSourceNode();
trg = edge.getTargetNode();
x1 = viewBox.convertX(src);
y1 = viewBox.convertY(src);
x2 = viewBox.convertX(trg);
y2 = viewBox.convertY(trg);
double nodeSize, xCenter, yCenter, xCenterCenter, yCenterCenter ;
double[] perpen;
switch(style.group.getShape()) {
case ANGLE:
double[] perpendicular = getPerpendicular(x1, y1, x2, y2, sizeEdge);
double x1Prim = perpendicular[0];
double y1Prim = perpendicular[1];
double x2Prim = perpendicular[2];
double y2Prim = perpendicular[3];
concat(buffer, " M ", d(x1), " ", d(y1));
concat(buffer, " L ", d(x1Prim), " ", d(y1Prim));
concat(buffer, " L ", d(x2Prim), " ", d(y2Prim));
concat(buffer, " Z");
break;
case CUBIC_CURVE:
nodeSize = svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(0);
if (svgStyles.get(groups.getStyleFor(trg)).group.getSize().getValueCount() > 1) {
nodeSize = Math.max(nodeSize,svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(1));
}
// First part of the curve
xCenter = (x1+x2)/2 ;
yCenter = (y1+y2)/2 ;
perpen = getPerpendicular(x1, y1, xCenter, yCenter, Math.sqrt(Math.pow(Math.abs(x1-xCenter), 2)+Math.pow(Math.abs(y1-yCenter), 2))*2);
double x45degrees = (x1+perpen[2])/2;
double y45degrees = (y1+perpen[3])/2;
xCenterCenter = (x1+xCenter)/2 ;
yCenterCenter = (y1+yCenter)/2 ;
double x20degrees = (xCenterCenter+x45degrees)/2;
double y20degrees = (yCenterCenter+y45degrees)/2;
concat(buffer, " M ", d(x1), " ", d(y1));
concat(buffer, " C ", d(x20degrees), " ", d(y20degrees), " ", d(x20degrees), " ", d(y20degrees), " ", d(xCenter), " ", d(yCenter));
/// Second part of the curve
double x45degrees2nd = (x2+perpen[0])/2;
double y45degrees2nd = (y2+perpen[1])/2;
double xCenterCenter2nd = (x2+xCenter)/2;
double yCenterCenter2nd = (y2+yCenter)/2;
double x20degrees2nd = (xCenterCenter2nd+x45degrees2nd)/2;
double y20degrees2nd = (yCenterCenter2nd+y45degrees2nd)/2;
concat(buffer, " S ", d(x20degrees2nd), " ", d(y20degrees2nd), " ", d(x2), " ", d(y2));
concat(buffer, " C ", d(x20degrees2nd), " ", d(y20degrees2nd), " ", d(x20degrees2nd), " ", d(y20degrees2nd), " ", d(xCenter), " ", d(yCenter));
concat(buffer, " S ", d(x20degrees), " ", d(y20degrees), " ", d(x1), " ", d(y1));
concat(buffer, " Z");
break;
case BLOB:
nodeSize = svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(0);
if (svgStyles.get(groups.getStyleFor(trg)).group.getSize().getValueCount() > 1) {
nodeSize = Math.max(nodeSize,svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(1));
}
xCenter = (x1+x2)/2 ;
yCenter = (y1+y2)/2 ;
xCenterCenter = (x1+xCenter)/2 ;
yCenterCenter = (y1+yCenter)/2 ;
double[] perpenCenter = getPerpendicular(x1, y1, xCenter, yCenter, sizeEdge);
double[] perpenX1 = getPerpendicular(xCenter, yCenter, x1, y1, nodeSize);
double[] perpenXCenter1 = getPerpendicular(x1, y1, xCenterCenter, yCenterCenter, sizeEdge);
concat(buffer, " M ", d(perpenX1[0]), " ", d(perpenX1[1]));
concat(buffer, " Q ", d(perpenXCenter1[0]), " ", d(perpenXCenter1[1]), " ", d(perpenCenter[0]), " ", d(perpenCenter[1]));
concat(buffer, " L ", d(x2), " ", d(y2));
concat(buffer, " L ", d(perpenCenter[2]), " ", d(perpenCenter[3]));
concat(buffer, " Q ", d(perpenXCenter1[2]), " ", d(perpenXCenter1[3]), " ", d(perpenX1[2]), " ", d(perpenX1[3]));
concat(buffer, " Z");
if(! edge.isDirected()) {
double[] perpenX2 = getPerpendicular(xCenter, yCenter, x2, y2, nodeSize);
xCenterCenter2nd = (x2+xCenter)/2;
yCenterCenter2nd = (y2+yCenter)/2;
double[] perpenXCenter2 = getPerpendicular(x2, y2, xCenterCenter2nd, yCenterCenter2nd, sizeEdge);
concat(buffer, " M ", d(perpenX2[0]), " ", d(perpenX2[1]));
concat(buffer, " Q ", d(perpenXCenter2[0]), " ", d(perpenXCenter2[1]), " ", d(perpenCenter[0]), " ", d(perpenCenter[1]));
concat(buffer, " L ", d(x1), " ", d(y1));
concat(buffer, " L ", d(perpenCenter[2]), " ", d(perpenCenter[3]));
concat(buffer, " Q ", d(perpenXCenter2[2]), " ", d(perpenXCenter2[3]), " ", d(perpenX2[2]), " ", d(perpenX2[3]));
concat(buffer, " Z");
}
break;
default:
case LINE:
concat(buffer, " M ", d(x1), " ", d(y1));
concat(buffer, " L ", d(x2), " ", d(y2));
break;
}
//-------------------- draw arrow
if(edge.isDirected()) {
//--------------------- Size node ---------------------------------------
nodeSize = svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(0);
double diag = -1;
if (svgStyles.get(groups.getStyleFor(trg)).group.getSize().getValueCount() > 1) {
diag = Math.sqrt(Math.pow(nodeSize, 2)+Math.pow(svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(1), 2));
nodeSize = Math.min(nodeSize,svgStyles.get(groups.getStyleFor(trg)).group.getSize().get(1));
} else {
diag = Math.sqrt(Math.pow(nodeSize, 2)+Math.pow(nodeSize, 2));
}
if(svgStyles.get(groups.getStyleFor(trg)).group.getShape().equals(Shape.CIRCLE)) {
nodeSize = nodeSize/2;
} else if (svgStyles.get(groups.getStyleFor(trg)).group.getShape().equals(Shape.BOX) ||
svgStyles.get(groups.getStyleFor(trg)).group.getShape().equals(Shape.ROUNDED_BOX) ||
svgStyles.get(groups.getStyleFor(trg)).group.getShape().equals(Shape.DIAMOND) ||
svgStyles.get(groups.getStyleFor(trg)).group.getShape().equals(Shape.TRIANGLE)) {
nodeSize = diag/2 ;
}
//----------------------------------------------------------------------
double distance = Math.sqrt(((x2-x1)*(x2-x1))+((y2-y1)*(y2-y1)));
double ratioPoint, ratioLine ;
double x2Root, y2Root ;
double x2Point, y2Point ;
double x1Prim, y1Prim, x2Prim, y2Prim ;
switch (style.group.getArrowShape()) {
case CIRCLE:
ratioPoint = 1-(nodeSize/distance);
ratioLine = 1-(((sx/2)+nodeSize)/distance);
x2Root = (((1-ratioLine)*x1)+(ratioLine*x2));
y2Root = (((1-ratioLine)*y1)+(ratioLine*y2));
x2Point = (((1-ratioPoint)*x1)+(ratioPoint*x2));
y2Point = (((1-ratioPoint)*y1)+(ratioPoint*y2));
perpen = getPerpendicular(x2, y2, x2Root, y2Root, sy);
x1Prim = perpen[0];
y1Prim = perpen[1];
x2Prim = perpen[2];
y2Prim = perpen[3];
concat(buffer, " M ", d(x1Prim), " ", d(y1Prim));
concat(buffer, " A ", d(sx / 4), " ", d(sy / 4), " 0 1 0 ", d(x2Prim), " ", d(y2Prim));
concat(buffer, " ", d(sx / 4), " ", d(sy / 4), " 0 1 0 ", d(x1Prim), " ", d(y1Prim));
concat(buffer, " Z");
break;
case DIAMOND:
ratioPoint = 1-(nodeSize/distance);
ratioLine = 1-(((sx/2)+nodeSize)/distance);
x2Root = (((1-ratioLine)*x1)+(ratioLine*x2));
y2Root = (((1-ratioLine)*y1)+(ratioLine*y2));
x2Point = (((1-ratioPoint)*x1)+(ratioPoint*x2));
y2Point = (((1-ratioPoint)*y1)+(ratioPoint*y2));
System.out.println(nodeSize+" "+sx);
double ratioEnd = 1-((sx+nodeSize)/distance);
double x2End = (((1-ratioEnd)*x1)+(ratioEnd*x2));
double y2End = (((1-ratioEnd)*y1)+(ratioEnd*y2));
perpen = getPerpendicular(x2, y2, x2Root, y2Root, sy);
x1Prim = perpen[0];
y1Prim = perpen[1];
x2Prim = perpen[2];
y2Prim = perpen[3];
concat(buffer, " M ", d(x2Point), " ", d(y2Point));
concat(buffer, " L ", d(x1Prim), " ", d(y1Prim));
concat(buffer, " L ", d(x2End), " ", d(y2End));
concat(buffer, " L ", d(x2Prim), " ", d(y2Prim));
//concat(buffer, " L ", d(x2Point), " ", d(y2Point));
concat(buffer, " Z");
break;
default:
case ARROW:
ratioPoint = 1-(nodeSize/distance);
ratioLine = 1-((sx+nodeSize)/distance);
x2Root = (((1-ratioLine)*x1)+(ratioLine*x2));
y2Root = (((1-ratioLine)*y1)+(ratioLine*y2));
x2Point = (((1-ratioPoint)*x1)+(ratioPoint*x2));
y2Point = (((1-ratioPoint)*y1)+(ratioPoint*y2));
perpen = getPerpendicular(x2, y2, x2Root, y2Root, sy);
x1Prim = perpen[0];
y1Prim = perpen[1];
x2Prim = perpen[2];
y2Prim = perpen[3];
if (style.group.getShape().equals(Shape.CUBIC_CURVE)) {
double rotation = 25 ;
System.out.println(y2Point-y2);
if (y2Point-y2 <= 1)
rotation = -rotation ;
Vector2 v = rotatePoint(x2, y2, rotation, x2Point, y2Point);
x2Point = v.x();
y2Point = v.y();
v = rotatePoint(x2, y2, rotation, x1Prim, y1Prim);
x1Prim = v.x();
y1Prim = v.y();
v = rotatePoint(x2, y2, rotation, x2Prim, y2Prim);
x2Prim = v.x();
y2Prim = v.y();
}
concat(buffer, " M ", d(x1Prim), " ", d(y1Prim));
concat(buffer, " L ", d(x2Prim), " ", d(y2Prim));
concat(buffer, " L ", d(x2Point), " ", d(y2Point));
concat(buffer, " Z");
System.out.println("Arrow = ("+x1Prim+", "+y1Prim+") ("+x1Prim+", "+y2Prim+") ("+x2Point+", "+y2Point+")");
break;
}
}
}
return buffer.toString();
}
/**
* rotates the point around a center and returns the new point
* @param cx x coordinate of the center
* @param cy y coordinate of the center
* @param angle in degrees (sign determines the direction + is counter-clockwise - is clockwise)
* @param px x coordinate of point to rotate
* @param py y coordinate of point to rotate
* */
public static Vector2 rotatePoint(double cx, double cy, double angle, double px, double py){
double absangl = Math.abs(angle);
double s = Math.sin(Math.toRadians(absangl));
double c = Math.cos(Math.toRadians(absangl));
// translate point back to origin:
px -= cx;
py -= cy;
// rotate point
double xnew;
double ynew;
if (angle > 0) {
xnew = px * c - py * s;
ynew = px * s + py * c;
}
else {
xnew = px * c + py * s;
ynew = -px * s + py * c;
}
// translate point back:
px = xnew + cx;
py = ynew + cy;
return new Vector2(px, py);
}
public double[] getPerpendicular(double x1, double y1, double x2, double y2, double size) {
double slope, slopePerpen;
slope = (y2-y1)/(x2-x1);
double x1Prim, x2Prim, y1Prim, y2Prim ;
if(Double.isInfinite(slope)) {
x1Prim = x2-(size/2);
y1Prim = y2;
x2Prim = x2+(size/2);
y2Prim = y2;
}
else if (slope == 0) {
x1Prim = x2;
y1Prim = y2-(size/2);
x2Prim = x2;
y2Prim = y2+(size/2);
}
else {
slopePerpen = (-1/slope);
//concat(buffer, " m ", d(x2), " ", d(y2));
double deltaX = 1/(Math.sqrt((slopePerpen*slopePerpen)+1));
double deltaY = slopePerpen/(Math.sqrt((slopePerpen*slopePerpen)+1));
x1Prim = x2-((size/2)*deltaX);
y1Prim = y2-((size/2)*deltaY);
x2Prim = x2+((size/2)*deltaX);
y2Prim = y2+((size/2)*deltaY);
}
return new double[] {x1Prim, y1Prim, x2Prim, y2Prim};
}
public double getValue(Value v, boolean horizontal) {
return getValue(v.value, v.units, horizontal);
}
public double getValue(double d, StyleConstants.Units units, boolean horizontal) {
switch (units) {
case PX:
// TODO
return d;
case GU:
// TODO
return d;
case PERCENTS:
if (horizontal)
return (viewBox.x2 - viewBox.x1) * d / 100.0;
else
return (viewBox.y2 - viewBox.y1) * d / 100.0;
}
return d;
}
}
static class ViewBox {
double x1, y1, x2, y2;
double x3, y3, x4, y4;
double[] padding = { 0, 0 };
ViewBox(double x1, double y1, double x2, double y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
void compute(Graph g, StyleGroup style) {
x3 = y3 = Double.MAX_VALUE;
x4 = y4 = Double.MIN_VALUE;
g.nodes().forEach(n -> {
x3 = Math.min(x3, getX(n));
y3 = Math.min(y3, getY(n));
x4 = Math.max(x4, getX(n));
y4 = Math.max(y4, getY(n));
});
Values v = style.getPadding();
if (v.getValueCount() > 0) {
padding[0] = v.get(0);
padding[1] = v.getValueCount() > 1 ? v.get(1) : v.get(0);
}
}
double convertX(double x) {
return (x2 - x1 - 2 * padding[0]) * (x - x3) / (x4 - x3) + x1 + padding[0];
}
double convertX(Node n) {
return convertX(getX(n));
}
double convertY(double y) {
return (y2 - y1 - 2 * padding[1]) * (y - y3) / (y4 - y3) + y1 + padding[1];
}
double convertY(Node n) {
return convertY(getY(n));
}
}
static class SVGStyle {
static int gradientId = 0;
String style;
StyleGroup group;
boolean gradient;
boolean dynfill;
public SVGStyle(StyleGroup group) throws XMLStreamException {
this.group = group;
this.gradient = false;
this.dynfill = false;
switch (group.getType()) {
case EDGE:
buildEdgeStyle();
break;
case NODE:
buildNodeStyle();
break;
case GRAPH:
buildGraphStyle();
break;
case SPRITE:
default:
break;
}
}
void buildNodeStyle() {
StringBuilder styleSB = new StringBuilder();
switch (group.getFillMode()) {
case GRADIENT_RADIAL:
case GRADIENT_HORIZONTAL:
case GRADIENT_VERTICAL:
case GRADIENT_DIAGONAL1:
case GRADIENT_DIAGONAL2:
concat(styleSB, "fill:url(#%gradient-id%);");
this.gradient = true;
break;
case PLAIN:
concat(styleSB, "fill:", toHexColor(group.getFillColor(0)), ";");
concat(styleSB, "fill-opacity:", d(group.getFillColor(0).getAlpha() / 255.0), ";");
break;
case DYN_PLAIN:
dynfill = true;
concat(styleSB, "fill:%fill-color%;");
concat(styleSB, "fill-opacity:%fill-opacity%;");
break;
case IMAGE_TILED:
case IMAGE_SCALED:
case IMAGE_SCALED_RATIO_MAX:
case IMAGE_SCALED_RATIO_MIN:
case NONE:
break;
}
concat(styleSB, "fill-rule:nonzero;");
if (group.getStrokeMode() != StrokeMode.NONE) {
concat(styleSB, "stroke:", toHexColor(group.getStrokeColor(0)), ";");
concat(styleSB, "stroke-width:", getSize(group.getStrokeWidth()), ";");
}
style = styleSB.toString();
}
void buildGraphStyle() {
buildNodeStyle();
}
void buildEdgeStyle() {
StringBuilder styleSB = new StringBuilder();
switch (group.getFillMode()) {
case GRADIENT_RADIAL:
case GRADIENT_HORIZONTAL:
case GRADIENT_VERTICAL:
case GRADIENT_DIAGONAL1:
case GRADIENT_DIAGONAL2:
concat(styleSB, "stroke:url(#%gradient-id%);");
this.gradient = true;
break;
case PLAIN:
concat(styleSB, "fill:", toHexColor(group.getFillColor(0)), ";");
concat(styleSB, "fill-opacity:", d(group.getFillColor(0).getAlpha() / 255.0), ";");
concat(styleSB, "stroke:", toHexColor(group.getFillColor(0)), ";");
break;
case DYN_PLAIN:
concat(styleSB, "stroke:", toHexColor(group.getFillColor(0)), ";");
break;
case IMAGE_TILED:
case IMAGE_SCALED:
case IMAGE_SCALED_RATIO_MAX:
case IMAGE_SCALED_RATIO_MIN:
case NONE:
break;
}
if (! group.getShape().equals(Shape.ANGLE) && ! group.getShape().equals(Shape.BLOB)) { // Size used in the path creation
concat(styleSB, "stroke-width:", getSize(group.getSize(), 0), ";");
}
style = styleSB.toString();
}
public void writeDef(XMLWriter out) throws XMLStreamException {
if (gradient) {
String gid = String.format("gradient%x", gradientId++);
String type = "linearGradient";
String x1 = null, x2 = null, y1 = null, y2 = null;
switch (group.getFillMode()) {
case GRADIENT_RADIAL:
type = "radialGradient";
break;
case GRADIENT_HORIZONTAL:
x1 = "0%";
y1 = "50%";
x2 = "100%";
y2 = "50%";
break;
case GRADIENT_VERTICAL:
x1 = "50%";
y1 = "0%";
x2 = "50%";
y2 = "100%";
break;
case GRADIENT_DIAGONAL1:
x1 = "0%";
y1 = "0%";
x2 = "100%";
y2 = "100%";
break;
case GRADIENT_DIAGONAL2:
x1 = "100%";
y1 = "100%";
x2 = "0%";
y2 = "0%";
break;
default:
break;
}
out.open(type);
out.attribute("id", gid);
out.attribute("gradientUnits", "objectBoundingBox");
if (type.equals("linearGradient")) {
out.attribute("x1", x1);
out.attribute("y1", y1);
out.attribute("x2", x2);
out.attribute("y2", y2);
}
for (int i = 0; i < group.getFillColorCount(); i++) {
out.open("stop");
out.attribute("stop-color", toHexColor(group.getFillColor(i)));
out.attribute("stop-opacity", d(group.getFillColor(i).getAlpha() / 255.0));
out.attribute("offset", Double.toString(i / (double) (group.getFillColorCount() - 1)));
out.close();
}
out.close();
style = style.replace("%gradient-id%", gid);
}
}
public String getElementStyle(Element e) {
String st = style;
if (dynfill) {
if (group.getFillColorCount() > 1) {
String color, opacity;
double d = e.hasNumber("ui.color") ? e.getNumber("ui.color") : 0;
double a, b;
Colors colors = group.getFillColors();
int s = Math.min((int) (d * group.getFillColorCount()), colors.size() - 2);
a = s / (double) (colors.size() - 1);
b = (s + 1) / (double) (colors.size() - 1);
d = (d - a) / (b - a);
Color c1 = colors.get(s), c2 = colors.get(s + 1);
color = String.format("#%02x%02x%02x", (int) (c1.getRed() + d * (c2.getRed() - c1.getRed())),
(int) (c1.getGreen() + d * (c2.getGreen() - c1.getGreen())),
(int) (c1.getBlue() + d * (c2.getBlue() - c1.getBlue())));
opacity = Double.toString((c1.getAlpha() + d * (c2.getAlpha() - c1.getAlpha())) / 255.0);
st = st.replace("%fill-color%", color);
st = st.replace("%fill-opacity%", opacity);
}
}
return st;
}
}
static class XMLWriter {
XMLStreamWriter out;
int depth;
boolean closed;
void start(Writer w) throws XMLStreamException, FactoryConfigurationError, IOException {
if (out != null)
end();
out = XMLOutputFactory.newInstance().createXMLStreamWriter(w);
out.writeStartDocument();
}
void end() throws XMLStreamException {
out.writeEndDocument();
out.flush();
out.close();
out = null;
}
void open(String name) throws XMLStreamException {
out.writeCharacters("\n");
for (int i = 0; i < depth; i++)
out.writeCharacters(" ");
out.writeStartElement(name);
depth++;
}
void close() throws XMLStreamException {
out.writeEndElement();
depth--;
}
void attribute(String key, String value) throws XMLStreamException {
out.writeAttribute(key, value);
}
void characters(String data) throws XMLStreamException {
out.writeCharacters(data);
}
}
private static void concat(StringBuilder buffer, Object... args) {
if (args != null) {
for (int i = 0; i < args.length; i++)
buffer.append(args[i].toString());
}
}
private static String toHexColor(Color c) {
return String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue());
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#edgeAttributeAdded(java.lang.String,
* long, java.lang.String, java.lang.String, java.lang.Object)
*/
public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, String attribute, Object value) {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#edgeAttributeChanged(java.lang.String ,
* long, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object)
*/
public void edgeAttributeChanged(String sourceId, long timeId, String edgeId, String attribute, Object oldValue,
Object newValue) {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#edgeAttributeRemoved(java.lang.String ,
* long, java.lang.String, java.lang.String)
*/
public void edgeAttributeRemoved(String sourceId, long timeId, String edgeId, String attribute) {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#graphAttributeAdded(java.lang.String ,
* long, java.lang.String, java.lang.Object)
*/
public void graphAttributeAdded(String sourceId, long timeId, String attribute, Object value) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.AttributeSink#graphAttributeChanged(java.lang.
* String, long, java.lang.String, java.lang.Object, java.lang.Object)
*/
public void graphAttributeChanged(String sourceId, long timeId, String attribute, Object oldValue,
Object newValue) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.AttributeSink#graphAttributeRemoved(java.lang.
* String, long, java.lang.String)
*/
public void graphAttributeRemoved(String sourceId, long timeId, String attribute) {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#nodeAttributeAdded(java.lang.String,
* long, java.lang.String, java.lang.String, java.lang.Object)
*/
public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, String attribute, Object value) {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#nodeAttributeChanged(java.lang.String ,
* long, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object)
*/
public void nodeAttributeChanged(String sourceId, long timeId, String nodeId, String attribute, Object oldValue,
Object newValue) {
}
/*
* (non-Javadoc)
*
* @see
* org.graphstream.stream.AttributeSink#nodeAttributeRemoved(java.lang.String ,
* long, java.lang.String, java.lang.String)
*/
public void nodeAttributeRemoved(String sourceId, long timeId, String nodeId, String attribute) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.ElementSink#edgeAdded(java.lang.String, long,
* java.lang.String, java.lang.String, java.lang.String, boolean)
*/
public void edgeAdded(String sourceId, long timeId, String edgeId, String fromNodeId, String toNodeId,
boolean directed) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.ElementSink#edgeRemoved(java.lang.String, long,
* java.lang.String)
*/
public void edgeRemoved(String sourceId, long timeId, String edgeId) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.ElementSink#graphCleared(java.lang.String, long)
*/
public void graphCleared(String sourceId, long timeId) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.ElementSink#nodeAdded(java.lang.String, long,
* java.lang.String)
*/
public void nodeAdded(String sourceId, long timeId, String nodeId) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.ElementSink#nodeRemoved(java.lang.String, long,
* java.lang.String)
*/
public void nodeRemoved(String sourceId, long timeId, String nodeId) {
}
/*
* (non-Javadoc)
*
* @see org.graphstream.stream.ElementSink#stepBegins(java.lang.String, long,
* double)
*/
public void stepBegins(String sourceId, long timeId, double step) {
}
}