/*
* CCVisu is a tool for visual graph clustering
* and general force-directed graph layout.
* This file is part of CCVisu.
*
* Copyright (C) 2005-2007 Dirk Beyer
*
* CCVisu is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* CCVisu 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 CCVisu; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please find the GNU Lesser General Public License in file
* license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
*
* Dirk Beyer (firstname.lastname@sfu.ca)
* Simon Fraser University (SFU), B.C., Canada
*/
package ccvisu;
import java.awt.Color;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
/*****************************************************************
* Writer for layouts in SVG format (Scalable Vector Graphs, XML, W3C).
* @version $Revision$; $Date$
* @author Dirk Beyer
*****************************************************************/
public class WriterDataGraphicsSVG extends WriterDataGraphics {
private PrintWriter out;
private float scalePos;
private String inputName;
/** used to build uniq id for the edges */
private int edgeNumber = 0;
/**
* Constructor.
* @param graph Graph representation, contains the positions of the vertices.
* @param out Output stream writer.
* @param minVert Diameter of the smallest vertex.
* @param fontSize Font size of vertex annotations.
* @param backColor Background color.
* @param blackCircle If true, draw black circle around each vertex.
* @param showEdges If true, draw the edges between the vertices (if possible).
* @param scalePos Scaling factor for the layout to adjust to drawing area.
*/
public WriterDataGraphicsSVG(GraphData graph,
PrintWriter out,
float minVert,
int fontSize,
Color backColor,
boolean blackCircle,
boolean showEdges,
boolean openURL,
float scalePos,
String inputName) {
super(graph, minVert, fontSize, backColor, blackCircle, showEdges, openURL);
this.out = out;
this.scalePos = scalePos;
this.inputName = inputName;
}
/*****************************************************************
* Writes the layout in graphics format SVG.
*****************************************************************/
public void write() {
int size = (int) (1000 * scalePos);
// Header.
out.print(
"<?xml version=\"1.0\" standalone=\"no\"?>" + endl
+ " " + endl
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"" + endl
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" + endl
+ " " + endl
+ "<!-- Generated by CCVisu, a tool for visual graph clustering " + endl
+ " and general force-directed graph layout. " + endl
+ " " + DateFormat.getDateTimeInstance().format(new Date()) + " -->" + endl
+ " " + endl
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"" + endl
+ " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" + endl
+ " width=\"100%\" height=\"100%\"" + endl
+ " viewBox=\"0 0 " + size + " " + size + "\">" + endl
+ " " + endl
+ " <title>Visualization " + inputName + "</title>" + endl
+ " " + endl
+ " <script><![CDATA[ " + endl
+ " " + endl
+ " /* " + endl
+ " * Returns the innermost SVG object that triggered the event " + endl
+ " * and that has a non-null id attribute. This can be either the event's " + endl
+ " * target itself, or its parent node, or the first ancestor of the target " + endl
+ " * with a non-null id. " + endl
+ " * " + endl
+ " * Input Parameters: " + endl
+ " * evt - The JavaScript object describing the triggering event. " + endl
+ " * Return Value: " + endl
+ " * The target node or first target's ancestor with a non-null id. " + endl
+ " */ " + endl
+ " function get_target(evt) { " + endl
+ " var target = evt.target; " + endl
+ " while (target && !target.getAttribute('id')) { " + endl
+ " target = target.parentNode; " + endl
+ " } " + endl
+ " return target; " + endl
+ " } " + endl
+ " " + endl
+ " /* " + endl
+ " * Adds or removes a duplicate of the text object that " + endl
+ " * corresponds to the circle that was clicked on / pointed to: " + endl
+ " * The circle node is the evt.target. " + endl
+ " * If the text node is already there, the node gets removed. " + endl
+ " * Otherwise the text node is cloned. The cloned node's id is changed. " + endl
+ " * The cloned node is appended to the 'contents' group. " + endl
+ " * " + endl
+ " * Input Parameters: " + endl
+ " * evt - JavaScript object describing the triggering event. " + endl
+ " * postfix - String to distinguish between click and move. " + endl
+ " */ " + endl
+ " function annot_toggle(evt, postfix) { " + endl
+ " // Retrieve node of object that was clicked on. " + endl
+ " var target = get_target(evt); " + endl
+ " var svgdoc = target.ownerDocument; " + endl
+ " " + endl
+ " // Retrieve annotation node. " + endl
+ " var annotNode = svgdoc.getElementById(target.getAttribute('id') " + endl
+ " + '__text' + postfix); " + endl
+ " // Check whether annotation is already set. " + endl
+ " if (annotNode) { " + endl
+ " // Remove object's node from its group node. " + endl
+ " var groupnode = annotNode.parentNode; " + endl
+ " groupnode.removeChild (annotNode); " + endl
+ " } else { " + endl
+ " // Clone the text object and set its attributes. " + endl
+ " var text = svgdoc.getElementById(target.getAttribute('id') + '__text'); " + endl
+ " annotNode = text.cloneNode(true); " + endl
+ " annotNode.setAttribute('id', target.getAttribute('id') + '__text' + postfix); " + endl
+ " // Retrieve the node for 'contents' group. " + endl
+ " var contents = svgdoc.getElementById('contents'); " + endl
+ " // Insert the cloned object into the contents group node. " + endl
+ " contents.parentNode.appendChild(annotNode); " + endl
+ " window.status = target.getAttribute('id'); " + endl
+ " } " + endl
+ " } " + endl
+ " " + endl
+ " ]]></script> " + endl + endl
);
// Body.
writeGraphicsLayout(size);
// Footer.
out.print("</svg> " + endl);
}
/**
* Writes a vertex in SVG format.
* @param curVertex The vertex object, to access vertex attributes.
* @param xPos x coordinate of the vertex.
* @param yPos y coordinate of the vertex.
* @param zPos z coordinate of the vertex.
* @param radius Radius of the vertex.
*/
public void writeVertex(GraphVertex curVertex,
int xPos, int yPos, int zPos, int radius) {
String name = curVertex.name;
//remove double quote
if(name.startsWith("\"") && name.endsWith("\"")){
name = name.substring(1,name.length()-1);
}
// Prepare color string for vertex.
// Convert to hex RGB value and get rid of the alpha value.
String colorStr = Integer.toHexString(curVertex.color.getRGB() & 0x00FFFFFF);
// Fill up to 6 digits.
colorStr = "000000" + colorStr;
colorStr = colorStr.substring(colorStr.length() - 6, colorStr.length());
// Prepare color string for annotation text.
Color textColor = new Color( 0xffffffff - backColor.getRGB() );
// Convert to hex RGB value and get rid of the alpha value.
String textColStr = Integer.toHexString(textColor.getRGB() & 0x00FFFFFF);
// Fill up to 6 digits.
textColStr = "000000" + textColStr;
textColStr = textColStr.substring(textColStr.length() - 6, textColStr.length());
// Write the definition for the annotation.
out.print(
" <defs>"
+ " <text"
+ " id=\"" + name + "__text\""
+ " x=\"" + (xPos + radius + 3) + "\""
+ " y=\"" + (yPos + 3) + "\""
+ " style=\"font-size:" + fontSize + "pt;fill:#" + textColStr + "\"> "
+ name
+ " </text>"
+ " </defs> " + endl
);
// Write graphical objects.
out.print(" <g id=\"contents\"> " + endl);
// Write vertex.
String strokeString = "";
if (blackCircle) {
strokeString = " stroke=\"black\"";
}
//link information
if(openURL){
out.println(" <a xlink:href=\"" + name + "\">");
}
out.print(
" <circle"
+ " id=\"" + name + "\""
+ " cx=\"" + xPos + "\""
+ " cy=\"" + yPos + "\""
+ " r=\"" + radius + "\""
+ " fill=\"#" + colorStr + "\""
+ strokeString
+ " onmouseover=\"annot_toggle(evt, '_move')\""
+ " onmouseout=\"annot_toggle(evt, '_move')\""
+ " onclick=\"annot_toggle(evt, '_click')\""
+ " /> " + endl
);
if(openURL){
out.println(" </a>");
}
// Write annotation, only if required.
if (curVertex.showName) {
out.print(" <text"
+ " id=\"" + name + "__text_click\""
+ " x=\"" + (xPos + radius + 3) + "\""
+ " y=\"" + (yPos + 3) + "\""
+ " style=\"font-size:" + fontSize + "pt;fill:#" + textColStr + "\"> "
+ name
+ " </text> " + endl
);
}
out.print(" </g> " + endl);
}
/**
* Writes an edge.
* @param index index of the edge in graph.edges
* @param xPos1 x coordinate of the first point.
* @param yPos1 y coordinate of the first point.
* @param zPos1 z coordinate of the first point.
* @param xPos2 x coordinate of the second point.
* @param yPos2 y coordinate of the second point.
* @param zPos2 z coordinate of the second point.
*/
public void writeEdge(int index, int xPos1, int yPos1, int zPos1,
int xPos2, int yPos2, int zPos2){
String edgeName = graph.edges.get(index).relName;
//Prepare color string for annotation text.
Color textColor = new Color( 0xffffffff - backColor.getRGB() );
// Convert to hex RGB value and get rid of the alpha value.
String textColStr = Integer.toHexString(textColor.getRGB() & 0x00FFFFFF);
// Fill up to 6 digits.
textColStr = "000000" + textColStr;
textColStr = textColStr.substring(textColStr.length() - 6, textColStr.length());
// Write the definition for the annotation.
out.print(
" <defs>"
+ " <text"
+ " id=\"" + edgeName + edgeNumber + "__text\""
+ " x=\"" + ((xPos1 + xPos2)/2 + fontSize + 3) + "\""
+ " y=\"" + ((yPos1 + yPos2)/2 + fontSize + 3) + "\""
+ " style=\"font-size:" + fontSize + "pt;fill:#" + textColStr + "\"> "
+ edgeName
+ " </text>"
+ " </defs> " + endl
);
//Write graphical objects.
out.print(" <g id=\"contents\"> " + endl);
out.print("<line id=\""+ edgeName + edgeNumber
+"\" style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none;\" "
+ "y1=\""+yPos1+"\" y2=\""+yPos2+"\" x1=\""+xPos1+"\" x2=\""+xPos2+"\" "
+ " onmouseover=\"annot_toggle(evt, '_move')\""
+ " onmouseout=\"annot_toggle(evt, '_move')\""
+ " onclick=\"annot_toggle(evt, '_click')\" />"
+ endl);
out.print(" </g> " + endl);
++edgeNumber;
}
};