/*
* CCVisu is a tool for visual graph clustering
* and general force-directed graph layout.
* This file is part of CCVisu.
*
* Copyright (C) 2005-2012 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@uni-passau.de)
* University of Passau, Bavaria, Germany
*/
package org.sosy_lab.ccvisu.writers;
import java.awt.Color;
import java.io.PrintWriter;
import java.util.List;
import org.sosy_lab.ccvisu.Options;
import org.sosy_lab.ccvisu.Options.OptionsEnum;
import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.GraphEdge;
import org.sosy_lab.ccvisu.graph.GraphVertex;
import org.sosy_lab.ccvisu.graph.Position;
/**
* Writer for layouts in VRML format.
*/
public class WriterDataLayoutVRML extends WriterDataLayout {
public WriterDataLayoutVRML(PrintWriter out, GraphData graph, Options opt) {
super(out, graph, opt);
}
/**
* Writes the layout in graphics format VRML.
*/
@Override
public void write() {
int size = (int) (1000 * options.getOption(OptionsEnum.scalePos).getFloat());
// Header
out.println("#VRML V2.0 utf8");
out.println("#");
out.println("# Generated by " + Options.toolDescription());
out.println("# " + Options.currentDateTime());
out.println();
out.println(" PROTO TRANSFORM_Sphere [");
out.println(" field SFVec3f translation 0 0 0");
out.println(" field SFVec3f scale 1 1 1");
out.println(" field SFColor diffuseColor 0.5 0.5 0.5");
out.println(" field SFString name \"\"");
out.println(" field SFVec3f textTranslate 0 0 1");
out.println(" field SFFloat textSize " + options.getOption(OptionsEnum.fontSize).getInt());
out.println(" ] {");
out.println(" Transform {");
out.println(" translation IS translation");
out.println(" children [");
out.println(" Transform {");
out.println(" scale IS scale");
out.println(" children [");
out.println(" DEF sensor0 TouchSensor {}");
out.println(" Shape {");
out.println(" geometry Sphere { radius 0.5 }");
out.println(" appearance Appearance {");
out.println(" material Material {");
out.println(" ambientIntensity 0.3");
out.println(" diffuseColor IS diffuseColor");
out.println(" }");
out.println(" }");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" Billboard {");
out.println(" axisOfRotation 0 0 0");
out.println(" children [");
out.println(" Transform {");
out.println(" translation IS textTranslate");
out.println(" children [");
out.println(" Shape {");
out.println(" geometry DEF label0 Text { string [ \"\" ] fontStyle FontStyle { family \"SANS\" size IS textSize } }");
out.println(" appearance Appearance {");
out.println(" material Material {");
out.println(" ambientIntensity 0.3");
out.println(" diffuseColor 0 0 0");
out.println(" }");
out.println(" }");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" DEF script0 Script {");
out.println(" eventIn SFBool isOver");
out.println(" eventIn SFBool touchTime");
out.println(" eventOut MFString trans");
out.println(" field SFBool labelDisplayed FALSE");
out.println(" field SFString label IS name");
out.println(" url [ \"javascript:");
out.println(" function isOver(value) {");
out.println(" if (value) {");
out.println(" trans[0] = label;");
out.println(" } else {");
out.println(" if (!labelDisplayed) trans[0] = '';");
out.println(" }");
out.println(" }");
out.println(" function touchTime() {");
out.println(" if (labelDisplayed) {");
out.println(" trans[0] = '';");
out.println(" labelDisplayed = false;");
out.println(" } else {");
out.println(" trans[0] = label;");
out.println(" labelDisplayed = true;");
if (options.getOption(OptionsEnum.openURL).getBool()) {
out.println(" Browser.loadURL(new MFString(label), new MFString('target=DUMMY'));");
}
out.println(" }");
out.println(" }\"");
out.println(" ]");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" ROUTE sensor0.isOver TO script0.isOver");
out.println(" ROUTE sensor0.touchTime TO script0.touchTime");
out.println(" ROUTE script0.trans TO label0.set_string");
out.println(" }");
out.println();
out.println(" PROTO TRANSFORM_Cylinder [");
out.println(" field SFVec3f translation 0 0 0");
out.println(" field SFRotation rotation 0 0 0 0");
out.println(" field SFFloat height 1");
out.println(" field SFColor diffuseColor 0.5 0.5 0.5");
out.println(" field SFString name \"\"");
out.println(" field SFVec3f textTranslate 0 0 1");
out.println(" field SFFloat textSize " + options.getOption(OptionsEnum.fontSize).getInt());
out.println(" ] {");
out.println(" Transform {");
out.println(" translation IS translation");
out.println(" children [");
out.println(" Transform {");
out.println(" rotation IS rotation");
out.println(" children [");
out.println(" DEF sensor0 TouchSensor {}");
out.println(" Shape {");
out.println(" geometry Cylinder {");
out.println(" height IS height");
out.println(" radius 0.5");
out.println(" top FALSE");
out.println(" side TRUE");
out.println(" bottom FALSE");
out.println(" }");
out.println(" appearance Appearance {");
out.println(" material Material {");
out.println(" ambientIntensity 0.3");
out.println(" diffuseColor IS diffuseColor");
out.println(" }");
out.println(" }");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" Billboard {");
out.println(" axisOfRotation 0 0 0");
out.println(" children [");
out.println(" Transform {");
out.println(" translation IS textTranslate");
out.println(" children [");
out.println(" Shape {");
out.println(" geometry DEF label0 Text { string [ \"\" ] fontStyle FontStyle { family \"SANS\" size IS textSize } }");
out.println(" appearance Appearance {");
out.println(" material Material {");
out.println(" ambientIntensity 0.3");
out.println(" diffuseColor 0 0 0");
out.println(" }");
out.println(" }");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" DEF script0 Script {");
out.println(" eventIn SFBool isOver");
out.println(" eventIn SFBool touchTime");
out.println(" eventOut MFString trans");
out.println(" field SFBool labelDisplayed FALSE");
out.println(" field SFString label IS name");
out.println(" url [ \"javascript:");
out.println(" function isOver(value) {");
out.println(" if (value) {");
out.println(" trans[0] = label;");
out.println(" } else {");
out.println(" if (!labelDisplayed) trans[0] = '';");
out.println(" }");
out.println(" }");
out.println(" function touchTime() {");
out.println(" if (labelDisplayed) {");
out.println(" trans[0] = '';");
out.println(" labelDisplayed = false;");
out.println(" } else {");
out.println(" trans[0] = label;");
out.println(" labelDisplayed = true;");
out.println(" }");
out.println(" }\"");
out.println(" ]");
out.println(" }");
out.println(" ]");
out.println(" }");
out.println(" ROUTE sensor0.isOver TO script0.isOver");
out.println(" ROUTE sensor0.touchTime TO script0.touchTime");
out.println(" ROUTE script0.trans TO label0.set_string");
out.println(" }");
out.println();
out.println("Background { skyColor .8 .8 .8 }");
// Body
writeGraphicsLayout(graph.getVertices(), graph.getEdges(), size);
}
/**
* Write graphics layout.
* @param size Size of output area (e.g., number of pixel).
*/
@Override
public void writeGraphicsLayout(List<GraphVertex> vertices,
List<GraphEdge> edges, int size) {
float xPosMin = 1000000;
float xPosMax = -1000000;
float yPosMin = 1000000;
float yPosMax = -1000000;
float zPosMin = 1000000;
float zPosMax = -1000000;
for (GraphVertex vertex : graph.getVertices()) {
if (vertex.isShowVertex()) {
Position position = vertex.getPosition();
xPosMin = Math.min(xPosMin, position.x);
xPosMax = Math.max(xPosMax, position.x);
yPosMin = Math.min(yPosMin, position.y);
yPosMax = Math.max(yPosMax, position.y);
zPosMin = Math.min(zPosMin, position.z);
zPosMax = Math.max(zPosMax, position.z);
}
}
float layoutDist;
layoutDist = Math.max(xPosMax - xPosMin, yPosMax - yPosMin);
layoutDist = Math.max(layoutDist, zPosMax - zPosMin);
float xOffset = -xPosMin + 0.05f * layoutDist;
float yOffset = -yPosMin + 0.05f * layoutDist;
float zOffset = -zPosMin + 0.05f * layoutDist;
float scale = 0.9f * size / layoutDist;
int x = (int) (((xPosMin + xPosMax) / 2 + xOffset) * scale);
int y = (int) (((yPosMin + yPosMax) / 2 + yOffset) * -scale + size);
int z = (int) ((zPosMax
+ Math.max((xPosMax - xPosMin), (yPosMax - yPosMin)) * 1.3
+ zOffset) * scale);
out.println("Viewpoint { ");
out.println(" position " + x + " " + y + " " + z);
out.println(" orientation 0 0 1 0 ");
out.println(" description \"origin\" ");
out.println("} ");
out.println();
// Draw the edges.
if (options.getOption(OptionsEnum.showEdges).getBool() && !graph.getEdges().isEmpty()) {
for (GraphEdge edge : graph.getEdges()) {
Position sourcePos = edge.getSource().getPosition();
Position targetPos = edge.getTarget().getPosition();
writeEdge(edge,
(int) ((sourcePos.x + xOffset) * scale),
(int) ((sourcePos.y + yOffset) * -scale + size),
(int) ((sourcePos.z + zOffset) * scale),
(int) ((targetPos.x + xOffset) * scale),
(int) ((targetPos.y + yOffset) * -scale + size),
(int) ((targetPos.z + zOffset) * scale));
}
}
// Draw the vertices.
for (GraphVertex vertex : graph.getVertices()) {
if (!vertex.isShowName() && vertex.isShowVertex()) {
int radius = (int) Math.max(Math.pow(vertex.getDegree(), 0.5)
* options.getOption(OptionsEnum.minVert).getFloat(),
options.getOption(OptionsEnum.minVert).getFloat());
int xPos = (int) ((vertex.getPosition().x + xOffset) * scale);
int yPos = (int) ((vertex.getPosition().y + yOffset) * -scale + size);
int zPos = (int) ((vertex.getPosition().z + zOffset) * scale);
// Determine color for vertex.
Color color = vertex.getColor();
if (options.getOption(OptionsEnum.depDegreeColor).getBool()) {
float redValue = vertex.getDegreeOut() / graph.getMaxOutdegree();
color = new Color(redValue, 1 - redValue, 0);
}
writeVertex(vertex, xPos, yPos, zPos, radius, color);
}
}
// Draw the annotated vertices.
for (GraphVertex vertex : graph.getVertices()) {
if (vertex.isShowName() && vertex.isShowVertex()) {
int diameter = (int) Math.max(Math.pow(vertex.getDegree(), 0.5)
* options.getOption(OptionsEnum.minVert).getFloat(),
options.getOption(OptionsEnum.minVert).getFloat());
Position vertexPosition = vertex.getPosition();
int xPos = (int) ((vertexPosition.x + xOffset) * scale);
int yPos = (int) ((vertexPosition.y + yOffset) * -scale + size);
int zPos = (int) ((vertexPosition.z + zOffset) * scale);
// Determine color for vertex.
Color color = vertex.getColor();
if (options.getOption(OptionsEnum.depDegreeColor).getBool()) {
float redValue = vertex.getDegreeOut() / graph.getMaxOutdegree();
color = new Color(redValue, 1 - redValue, 0);
}
writeVertex(vertex, xPos, yPos, zPos, diameter, color);
}
}
}
/**
* Writes a vertex in VRML format.
* @param vertex 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 width Width of the vertex.
* @param height Height of the vertex.
*/
@Override
public void writeVertex(GraphVertex vertex, int xPos, int yPos, int zPos,
int width, int height, Color color) {
String name = vertex.getName();
//remove double quote
if ((name.length() > 0 && name.charAt(0) == '"') && name.endsWith("\"")) {
name = name.substring(1, name.length() - 1);
}
int widthTwice = 2 * width;
out.println("TRANSFORM_Sphere { translation "
+ xPos + " " + yPos + " " + zPos + " "
+ "scale "
+ widthTwice + " " + widthTwice + " " + widthTwice + " "
+ "diffuseColor "
+ color.getRed() + " " + color.getGreen() + " " + color.getBlue() + " "
+ "name \"" + name + "\" textTranslate "
+ widthTwice + " "
+ -options.getOption(OptionsEnum.fontSize).getInt() / 2
+ " " + widthTwice + " } ");
}
/**
* Writes an edge.
* @param edge an 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.
*/
@Override
public void writeEdge(GraphEdge edge, int xPos1, int yPos1, int zPos1,
int xPos2, int yPos2, int zPos2) {
String edgeName = edge.getRelName();
int deltaX = xPos2 - xPos1;
int deltaY = yPos2 - yPos1;
int deltaZ = zPos2 - zPos1;
float dist = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
// It does not make sense to draw an edge of length zero.
if (dist == 0) {
return;
}
/*
float rotx = dy;
float roty = -dx;
float rotz = 0.0f;
*/
float rotx = deltaZ;
float roty = 0.0f;
float rotz = -deltaX;
float norm = (float) Math.sqrt(rotx * rotx + roty * roty + rotz * rotz);
float rotw = (float) Math.asin(norm / dist);
if (deltaY < 0) {
rotw = -rotw;
}
out.println("TRANSFORM_Cylinder { translation "
+ (xPos1 + xPos2) / 2 + " "
+ (yPos1 + yPos2) / 2 + " "
+ (zPos1 + zPos2) / 2 + " "
+ "rotation " + rotx + " " + roty + " " + rotz + " " + rotw + " "
+ "height " + dist + " "
+ "diffuseColor 0 0 0 "
+ "name \"" + edgeName + "\" textTranslate 0 0 "
+ options.getOption(OptionsEnum.fontSize).getInt()
+ " } ");
}
}