/* * 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() + " } "); } }